Flutter Version Flutter 3.24.3 • channel stable
How to continuously monitor the status of Bluetooth at the global level using the Flutter framework? No matter which page you are on, when you hear that Bluetooth is turned off, you will directly jump to the page where Bluetooth is turned off. Can this be achieved? If the current BuildContext instance is not obtained, there is no way to navigate. The navigatorKey property of MaterialApp cannot be used because the onGeneratedRoute property is not used. Is there any other way to achieve this requirement?
You can ignore the third-party plugin library. My problem now is that I cannot globally listen in Flutter and navigate to a new page based on the listening results, regardless of which page it is currently on. However, I cannot implement this (because the listener is not in the widget page) because I cannot obtain the context instance of the current page. This is because it is a global listening, not a specific page listening.
I have looked at the API of streamBuilder, and it reads streams and builds components. Can it only rely on widgets? Once the widget is relied upon, there will be a declaration cycle for the component. What I want is to globally monitor this state throughout the application and automatically redirect routes based on changes in state. Regarding the navigation routing method, the articles I found are all about navigating in widgets where there is a context context. Because the above requirements are globally monitored and not limited to the context of the current active page, it is impossible to obtain the latest context value.
Flutter Version Flutter 3.24.3 • channel stable
How to continuously monitor the status of Bluetooth at the global level using the Flutter framework? No matter which page you are on, when you hear that Bluetooth is turned off, you will directly jump to the page where Bluetooth is turned off. Can this be achieved? If the current BuildContext instance is not obtained, there is no way to navigate. The navigatorKey property of MaterialApp cannot be used because the onGeneratedRoute property is not used. Is there any other way to achieve this requirement?
You can ignore the third-party plugin library. My problem now is that I cannot globally listen in Flutter and navigate to a new page based on the listening results, regardless of which page it is currently on. However, I cannot implement this (because the listener is not in the widget page) because I cannot obtain the context instance of the current page. This is because it is a global listening, not a specific page listening.
I have looked at the API of streamBuilder, and it reads streams and builds components. Can it only rely on widgets? Once the widget is relied upon, there will be a declaration cycle for the component. What I want is to globally monitor this state throughout the application and automatically redirect routes based on changes in state. Regarding the navigation routing method, the articles I found are all about navigating in widgets where there is a context context. Because the above requirements are globally monitored and not limited to the context of the current active page, it is impossible to obtain the latest context value.
Share Improve this question asked Nov 19, 2024 at 13:25 dreamdream 134 bronze badges 15- What should be the behaviour when the app is closed or in background ? – Aks Commented Nov 20, 2024 at 12:21
- If the application is closed, it naturally means it has ended, or if the application is running in the background, it should continue to run and listen. This question seems unrelated to the current issue, right – dream Commented Nov 26, 2024 at 1:51
- Actually, this is related. Because In android there are 2 ways to listen broadcast receivers. one way you can always listen to changes event he app is closed, another way you can only listen when the app is running. So, for now I have working code for always listen changes and showing a Toast message. Let see if we can create your scenario. – Aks Commented Nov 26, 2024 at 3:44
- Can the monitor automatically navigate to the specified page in the listener after hearing the status change? What I want is to globally monitor and push the navigation to a page where Bluetooth has been turned off after the Bluetooth connection is disconnected. On that page, you can request Bluetooth to be turned on, so there is no need to repeatedly add some judgments on each page. Because the global listener does not have a context object in the widget. – dream Commented Nov 26, 2024 at 11:29
- you can navigate without context using a global key, the hard task is listening the bluetooth state. – Aks Commented Nov 26, 2024 at 12:09
1 Answer
Reset to default 0Packages used:
- go_router
- bluetooth_manager
Note: You need to ask for bluetooth permission manually. For this example, I enabled the bluetooth permission for this app manually from device settings.
Add permission in AndroidManifest.xml
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
main.dart
import 'package:bt_navigate/bluetooth_service.dart';
import 'package:bt_navigate/router.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() {
GoRouter router = AppRouter.setupRouter();
final BluetoothService bluetoothService = BluetoothService();
//Global monitoring
bluetoothService.getBluetoothStateStream().listen((event) {
print("event received: $event");
router.refresh();
});
runApp(MyApp(router: router));
}
class MyApp extends StatelessWidget {
const MyApp({super.key, required this.router});
final GoRouter router;
@override
Widget build(BuildContext context) {
return MaterialApp.router(
debugShowCheckedModeBanner: false,
routeInformationParser: router.routeInformationParser,
routeInformationProvider: router.routeInformationProvider,
routerDelegate: router.routerDelegate,
theme: ThemeData(useMaterial3: false),
);
}
}
router.dart
import 'package:bt_navigate/bluetooth_service.dart';
import 'package:bt_navigate/screens/bluetooth_off_screen.dart';
import 'package:bt_navigate/screens/home_page.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class AppRouter {
static GoRouter setupRouter() {
const String homeRoute = "/";
const String bluetoothOffRoute = "/bluetooth_off";
final BluetoothService bluetoothService = BluetoothService();
return GoRouter(
initialLocation: homeRoute,
routes: [
GoRoute(
path: homeRoute,
pageBuilder: (context, state) {
return const MaterialPage(child: HomePage());
},
),
GoRoute(
path: bluetoothOffRoute,
pageBuilder: (context, state) {
return const MaterialPage(child: BluetoothOffScreen());
},
),
],
redirect: (context, state) async {
// Check the Bluetooth status
final isBluetoothOn = await bluetoothService.isBluetoothOn();
// Define paths
final bool onBluetoothPage = state.matchedLocation == bluetoothOffRoute;
// final bool onHomePage = state.matchedLocation == "/";
// Redirect logic
if (!isBluetoothOn) {
return bluetoothOffRoute; // Navigate to a Bluetooth-disabled page
}
if (onBluetoothPage && isBluetoothOn) {
return homeRoute; // Navigate to the home page
}
return null; // No redirection
},
);
}
}
bluetooth_service.dart
import 'package:bluetooth_manager/bluetooth_manager.dart';
import 'package:bluetooth_manager/models/bluetooth_models.dart';
class BluetoothService {
// Private constructor
BluetoothService._privateConstructor();
// Singleton instance
static final BluetoothService _instance = BluetoothService._privateConstructor();
// Factory constructor to return the same instance
factory BluetoothService() {
return _instance;
}
// BluetoothManager instance
final BluetoothManager bluetoothManager = BluetoothManager();
// Method to get the current Bluetooth state
Future<bool> isBluetoothOn() async {
final state = await bluetoothManager.getBluetoothState();
return state == BluetoothState.on;
}
// Stream to listen to Bluetooth state changes
Stream<BluetoothState> getBluetoothStateStream() {
return bluetoothManager.getBluetoothStateStream();
}
}
homepage.dart
import 'package:bt_navigate/bluetooth_service.dart';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final BluetoothService bluetoothService = BluetoothService();
@override
void initState() {
super.initState();
}
Future<bool> getBluetoothStatus() async {
return await bluetoothService.isBluetoothOn();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(backgroundColor: Colors.green,),
body: SafeArea(
child: Center(
child: FutureBuilder(
future: getBluetoothStatus(),
builder: (context, snapshot) {
return Text("status: ${snapshot.data}");
},
))),
);
}
}
bluetooth_off_screen.dart
import 'package:flutter/material.dart';
class BluetoothOffScreen extends StatefulWidget {
const BluetoothOffScreen({super.key});
@override
State<BluetoothOffScreen> createState() => _BluetoothOffScreenState();
}
class _BluetoothOffScreenState extends State<BluetoothOffScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(backgroundColor: Colors.red,),
body: const Center(child: Text("Bluetooth status off")),
);
}
}
Output