top of page

Bluetooth in MAUI App

  • Valentin Taran
  • 5 сент.
  • 6 мин. чтения

Bluetooth technology is becoming increasingly relevant in cross-platform development — from fitness trackers and IoT devices to offline peer-to-peer games. In this article, I’ll show you how to implement Bluetooth connectivity in a .NET MAUI app using a simple yet illustrative example: a TicTacToe game.


Although Bluetooth is often associated with hardware integrations, it can be just as useful for software-only solutions — for example, establishing offline communication between two mobile devices without relying on internet access or a backend server.


Before diving into code, let’s take a step back and understand the underlying technologies and the tools available for working with Bluetooth in .NET MAUI.


cover image

Domain Overview


There are two primary Bluetooth technologies used in modern app development:

  • Bluetooth Classic – Offers higher data throughput, suitable for continuous data streaming (e.g., audio), but consumes more power.

  • Bluetooth Low Energy (BLE) – Optimized for small, periodic data transfers and significantly more energy-efficient.


For my TicTacToe project, Bluetooth LE was a clear winner due to the following reasons:

✅ Small data payloads (just game moves)

✅ Mobile-friendly, power-efficient

✅ No need for constant data streams


In scenarios like peer-to-peer games or lightweight communication between devices, BLE is the optimal choice.


Choosing the Right Package


Working with BLE in .NET MAUI isn’t straightforward out of the box. Rather than dealing with native APIs on each platform, developers usually rely on open-source libraries that abstract away platform-specific details and offer a unified BLE API.


At the time of writing, two major NuGet packages stood out as the go-to solutions for BLE integration in .NET MAUI:

Pros:

  • Well-established and widely used

  • Active community support

Cons:

  • Outdated documentation for .NET 6/7

  • Some examples don’t work out-of-the-box in MAUI

  • Less modular and harder to extend


Pros:

  • Modern architecture with strong support for .NET MAUI

  • Clean dependency injection integration

  • Great documentation and practical examples

Cons:

  • Slightly higher learning curve due to its modular design


💡 Why I chose Shiny:The clear documentation, up-to-date samples, and modern architecture made it much easier to get started and build a reliable BLE experience. It saved me hours I’d otherwise spend debugging connection issues and helped me focus on building the actual game logic.


Implementing Bluetooth LE in a Tic-Tac-Toe App


To demonstrate how Bluetooth LE can be used in practice, I decided to build a simple Tic-Tac-Toe app where two devices communicate directly over BLE — without any internet or server.


Here’s the basic idea:

  • One player acts as the host, initializing a BLE service with a writable characteristic.

  • This characteristic exposes a UUID, which will be shared with the second player (the client) to establish communication.

  • The client scans for available devices, connects to the host, and writes data via the discovered characteristic.

  • Once the connection is established, both players navigate to the game screen, where they synchronize via Bluetooth and start playing.


This setup demonstrates a lightweight peer-to-peer communication model using BLE, where each move in the game is sent and received in real time via Bluetooth.

Let’s break it down, starting with the host logic.


tic-tac-toe image

Host Side


To begin, I created a HostPage that displays the current connection status — indicating whether another player has successfully connected to the host.


host page

In the code-behind of the HostPage, I start by requesting the necessary Bluetooth permissions inside the constructor. I also override the OnNavigatedFrom method to stop the BLE service when the user leaves the page — this ensures that no new connections can be made when the host is no longer active.


To keep things clean and maintainable, I introduced a BaseViewModel that implements INotifyPropertyChanged, serving as the foundation for view-model logic. On top of that, I created a BaseHostViewModel, which inherits from BaseViewModel and encapsulates all Bluetooth host-specific functionality. This layered architecture improves code organization and makes the Bluetooth logic easier to test and extend.



I then created a HostViewModel, inheriting from BaseHostViewModel, where I overrode the BuildServer method to set up the BLE service with a writable characteristic.

The UUID is sent in two stages: first, its length is transmitted, followed by the UUID itself in parts. After a successful exchange, the app navigates to the game page, passing either the device name or UUID, depending on the platform.



Client Side


Now let’s move on to the client logic.

I started by creating a ClientPage that includes a button to initiate scanning, a CollectionView to display discovered devices, and an ActivityIndicator to show scanning status. In the page’s constructor, I requested Bluetooth permissions, and I overrode OnNavigatedFrom to stop scanning when the user leaves the screen.

For the view model, I introduced a BaseClientViewModel that extends BaseViewModel, encapsulating all common functionality for the client side and keeping the logic clean and reusable.



Next, I implemented a ClientViewModel, inheriting from BaseClientViewModel. It handles BLE scanning when the user taps the scan button. Upon selecting a device, the view model connects to the peripheral, locates the required characteristic, generates a new UUID, splits it by hyphens (-), and sends it to the host piece by piece.

Once the UUID transmission is complete, the app navigates to the game page, passing along the device name or UUID depending on the platform.



Game


Now we move on to the core of the app — the game itself.

The connection between players follows a peer-to-peer model, meaning both users act as clients and servers at the same time. This allows two-way communication without relying on a central host once the initial pairing is complete.

To implement the game interface, I created a GamePage that includes:

  • Several labels to display game status and player information,

  • A Grid powered by BindableLayout, which binds to the TicTacToeField as the data source,

  • An SKCanvasView to draw the winning line when a player wins.


This setup creates a responsive, visually intuitive UI while handling all game state updates in real time via Bluetooth LE.


main game page

In the GamePage code-behind, I retrieve data passed from the previous screens, override SizeAllocated to configure canvas dimensions, use OnNavigatedFrom to clear BLE state, and override OnAppearing to prevent rendering glitches on Android (like duplicate Xs or Os).


For data handling, I use a PayloadHelper class that serializes and deserializes objects to and from JSON for Bluetooth transmission.

To display the game outcome, I implemented a GameOverPopup, derived from BasePopup. It shows the result (win, lose, or draw) along with buttons to retry the game or return to the main menu.

gameover popup

For the game logic, I introduced a BaseHostClientViewModel, which extends BaseViewModel. This shared view model handles all core responsibilities on both the host and client sides — including move synchronization, game state updates, and Bluetooth communication.



With the architecture in place and the game logic fully implemented, the result is a fully functional peer-to-peer Tic-Tac-Toe game powered by Bluetooth LE — no internet connection required. Players can connect, sync game state, and enjoy a smooth experience across devices.


🎥 Here's a short demo of the app in action:



Conclusion


This project demonstrates how Bluetooth Low Energy (BLE) integration in a MAUI app enables secure 🔒, efficient ⚡, and offline peer-to-peer communication 🤝. By choosing the Shiny plugin for its clarity and ease of use, and structuring the app with MVVM, we created a reliable Tic-Tac-Toe game 🎮 that respects user privacy and device constraints.


Working with BLE challenges us to think carefully about energy consumption and connection stability, which are critical for delivering a smooth user experience. This approach opens doors 🚪 to many applications beyond gaming — any scenario where low-energy, direct device communication is needed. Combining MAUI’s cross-platform power 🌐 with BLE’s efficiency provides a solid foundation for innovative, connected apps 🔗.


With this knowledge, developers can create versatile apps that work seamlessly across platforms while keeping user data safe and connections reliable.


The full code from this article will be here 📂


Let’s Build the Future Together

At Igniscor, we don’t just build apps — we craft complete mobile experiences. From embedded systems to sleek, high-performance mobile interfaces, we turn ideas into reality with creativity and care. Have a project in mind? Let’s make it happen — contact us today

Комментарии


Последние Посты

Оставьте сообщение — и мы обязательно с вами свяжемся

Наше местоположение

Кирова 3, офис 9

Гродно, Беларусь

Сообщение отправлено! Спасибо!

Отправьте нам свой запрос
bottom of page