From a1c2bde0ec9ad7ba10d115bd7c17b854a9e63fd6 Mon Sep 17 00:00:00 2001 From: Alann Maulana Date: Mon, 30 Nov 2020 17:05:12 +0700 Subject: [PATCH] update example using getx --- example/lib/app_broadcasting.dart | 355 ------------------ example/lib/app_scanning.dart | 306 --------------- .../requirement_state_controller.dart | 60 +++ example/lib/main.dart | 15 +- example/lib/view/app_broadcasting.dart | 182 +++++++++ example/lib/view/app_scanning.dart | 149 ++++++++ example/lib/view/home_page.dart | 267 +++++++++++++ example/pubspec.yaml | 43 +-- 8 files changed, 667 insertions(+), 710 deletions(-) delete mode 100644 example/lib/app_broadcasting.dart delete mode 100644 example/lib/app_scanning.dart create mode 100644 example/lib/controller/requirement_state_controller.dart create mode 100644 example/lib/view/app_broadcasting.dart create mode 100644 example/lib/view/app_scanning.dart create mode 100644 example/lib/view/home_page.dart diff --git a/example/lib/app_broadcasting.dart b/example/lib/app_broadcasting.dart deleted file mode 100644 index 53ab764b..00000000 --- a/example/lib/app_broadcasting.dart +++ /dev/null @@ -1,355 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:flutter/material.dart'; - -import 'package:flutter/services.dart'; -import 'package:flutter_beacon/flutter_beacon.dart'; - -class MyApp extends StatefulWidget { - @override - _MyAppState createState() => _MyAppState(); -} - -class _MyAppState extends State with WidgetsBindingObserver { - final StreamController streamController = StreamController(); - final clearFocus = FocusNode(); - StreamSubscription _streamBluetooth; - bool authorizationStatusOk = false; - bool locationServiceEnabled = false; - bool bluetoothEnabled = false; - bool broadcasting = false; - - final regexUUID = RegExp( - r'[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}'); - final uuidController = - TextEditingController(text: 'CB10023F-A318-3394-4199-A8730C7C1AEC'); - final majorController = TextEditingController(text: '0'); - final minorController = TextEditingController(text: '0'); - - bool get broadcastReady => - authorizationStatusOk == true && - locationServiceEnabled == true && - bluetoothEnabled == true; - - @override - void initState() { - WidgetsBinding.instance.addObserver(this); - - super.initState(); - - listeningState(); - } - - listeningState() async { - print('Listening to bluetooth state'); - _streamBluetooth = flutterBeacon - .bluetoothStateChanged() - .listen((BluetoothState state) async { - print('BluetoothState = $state'); - streamController.add(state); - - switch (state) { - case BluetoothState.stateOn: - initScanBeacon(); - break; - case BluetoothState.stateOff: - // await pauseScanBeacon(); - await checkAllRequirements(); - break; - } - }); - } - - checkAllRequirements() async { - final bluetoothState = await flutterBeacon.bluetoothState; - final bluetoothEnabled = bluetoothState == BluetoothState.stateOn; - final authorizationStatus = await flutterBeacon.authorizationStatus; - final authorizationStatusOk = - authorizationStatus == AuthorizationStatus.allowed || - authorizationStatus == AuthorizationStatus.always; - final locationServiceEnabled = - await flutterBeacon.checkLocationServicesIfEnabled; - - setState(() { - this.authorizationStatusOk = authorizationStatusOk; - this.locationServiceEnabled = locationServiceEnabled; - this.bluetoothEnabled = bluetoothEnabled; - }); - } - - initScanBeacon() async { - await flutterBeacon.initializeScanning; - await checkAllRequirements(); - if (!authorizationStatusOk || - !locationServiceEnabled || - !bluetoothEnabled) { - print('RETURNED, authorizationStatusOk=$authorizationStatusOk, ' - 'locationServiceEnabled=$locationServiceEnabled, ' - 'bluetoothEnabled=$bluetoothEnabled'); - return; - } - } - - @override - void didChangeAppLifecycleState(AppLifecycleState state) async { - print('AppLifecycleState = $state'); - if (state == AppLifecycleState.resumed) { - if (_streamBluetooth != null && _streamBluetooth.isPaused) { - _streamBluetooth.resume(); - } - await checkAllRequirements(); - if (authorizationStatusOk && locationServiceEnabled && bluetoothEnabled) { - await initScanBeacon(); - } else { - //await pauseScanBeacon(); - await checkAllRequirements(); - } - } else if (state == AppLifecycleState.paused) { - _streamBluetooth?.pause(); - } - } - - @override - void dispose() { - WidgetsBinding.instance.removeObserver(this); - streamController?.close(); - _streamBluetooth?.cancel(); - flutterBeacon.close; - - clearFocus.dispose(); - - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Flutter Beacon'), - centerTitle: false, - actions: [ - if (!authorizationStatusOk && locationServiceEnabled) - IconButton( - icon: Icon(Icons.portable_wifi_off), - color: Colors.red, - onPressed: () async { - await flutterBeacon.requestAuthorization; - }, - ), - if (!locationServiceEnabled) - IconButton( - icon: Icon(Icons.location_off), - color: Colors.red, - onPressed: () async { - if (Platform.isAndroid) { - await flutterBeacon.openLocationSettings; - } else if (Platform.isIOS) { - await showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text('Location Services Off'), - content: Text( - 'Please enable Location Services on Settings > Privacy > Location Services.'), - actions: [ - FlatButton( - onPressed: () => Navigator.pop(context), - child: Text('OK'), - ), - ], - ); - }, - ); - } - }, - ), - StreamBuilder( - builder: (context, snapshot) { - if (snapshot.hasData) { - final state = snapshot.data; - - if (state == BluetoothState.stateOn) { - return IconButton( - icon: Icon(Icons.bluetooth_connected), - onPressed: () {}, - color: Colors.lightBlueAccent, - ); - } - - if (state == BluetoothState.stateOff) { - return IconButton( - icon: Icon(Icons.bluetooth), - onPressed: () async { - if (Platform.isAndroid) { - try { - await flutterBeacon.openBluetoothSettings; - } on PlatformException catch (e) { - print(e); - } - } else if (Platform.isIOS) { - await showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text('Bluetooth is Off'), - content: Text( - 'Please enable Bluetooth on Settings > Bluetooth.'), - actions: [ - FlatButton( - onPressed: () => Navigator.pop(context), - child: Text('OK'), - ), - ], - ); - }, - ); - } - }, - color: Colors.red, - ); - } - - return IconButton( - icon: Icon(Icons.bluetooth_disabled), - onPressed: () {}, - color: Colors.grey, - ); - } - - return SizedBox.shrink(); - }, - stream: streamController.stream, - initialData: BluetoothState.stateUnknown, - ), - ], - ), - body: GestureDetector( - onTap: () => FocusScope.of(context).requestFocus(clearFocus), - child: broadcastReady != true - ? Center(child: Text('Please wait...')) - : Form( - autovalidateMode: AutovalidateMode.onUserInteraction, - child: Container( - padding: - const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - uuidField, - majorField, - minorField, - SizedBox(height: 16), - buttonBroadcast, - ], - ), - ), - ), - ), - ); - } - - Widget get uuidField { - return TextFormField( - readOnly: broadcasting, - controller: uuidController, - decoration: InputDecoration( - labelText: 'Proximity UUID', - ), - validator: (val) { - if (val == null || val.isEmpty) { - return 'Proximity UUID required'; - } - - if (!regexUUID.hasMatch(val)) { - return 'Invalid Proxmity UUID format'; - } - - return null; - }, - ); - } - - Widget get majorField { - return TextFormField( - readOnly: broadcasting, - controller: majorController, - decoration: InputDecoration( - labelText: 'Major', - ), - keyboardType: TextInputType.number, - validator: (val) { - if (val == null || val.isEmpty) { - return 'Major required'; - } - - try { - int major = int.parse(val); - - if (major < 0 || major > 65535) { - return 'Major must be number between 0 and 65535'; - } - } on FormatException { - return 'Major must be number'; - } - - return null; - }, - ); - } - - Widget get minorField { - return TextFormField( - readOnly: broadcasting, - controller: minorController, - decoration: InputDecoration( - labelText: 'Minor', - ), - keyboardType: TextInputType.number, - validator: (val) { - if (val == null || val.isEmpty) { - return 'Minor required'; - } - - try { - int minor = int.parse(val); - - if (minor < 0 || minor > 65535) { - return 'Minor must be number between 0 and 65535'; - } - } on FormatException { - return 'Minor must be number'; - } - - return null; - }, - ); - } - - Widget get buttonBroadcast { - return RaisedButton( - onPressed: () async { - if (broadcasting) { - await flutterBeacon.stopBroadcast(); - } else { - await flutterBeacon.startBroadcast(BeaconBroadcast( - proximityUUID: uuidController.text, - major: int.tryParse(majorController.text) ?? 0, - minor: int.tryParse(minorController.text) ?? 0, - )); - } - - final isBroadcasting = await flutterBeacon.isBroadcasting(); - - if (mounted) { - setState(() { - broadcasting = isBroadcasting; - }); - } - }, - child: Text('Broadcast${broadcasting ? 'ing' : ''}'), - color: broadcasting ? Colors.red : Theme.of(context).primaryColor, - textColor: Colors.white, - ); - } -} diff --git a/example/lib/app_scanning.dart b/example/lib/app_scanning.dart deleted file mode 100644 index c73ef446..00000000 --- a/example/lib/app_scanning.dart +++ /dev/null @@ -1,306 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'dart:async'; - -import 'package:flutter/services.dart'; -import 'package:flutter_beacon/flutter_beacon.dart'; - -class MyApp extends StatefulWidget { - @override - _MyAppState createState() => _MyAppState(); -} - -class _MyAppState extends State with WidgetsBindingObserver { - final StreamController streamController = StreamController(); - StreamSubscription _streamBluetooth; - StreamSubscription _streamRanging; - final _regionBeacons = >{}; - final _beacons = []; - bool authorizationStatusOk = false; - bool locationServiceEnabled = false; - bool bluetoothEnabled = false; - - @override - void initState() { - WidgetsBinding.instance.addObserver(this); - - super.initState(); - - listeningState(); - } - - listeningState() async { - print('Listening to bluetooth state'); - _streamBluetooth = flutterBeacon - .bluetoothStateChanged() - .listen((BluetoothState state) async { - print('BluetoothState = $state'); - streamController.add(state); - - switch (state) { - case BluetoothState.stateOn: - initScanBeacon(); - break; - case BluetoothState.stateOff: - await pauseScanBeacon(); - await checkAllRequirements(); - break; - } - }); - } - - checkAllRequirements() async { - final bluetoothState = await flutterBeacon.bluetoothState; - final bluetoothEnabled = bluetoothState == BluetoothState.stateOn; - final authorizationStatus = await flutterBeacon.authorizationStatus; - final authorizationStatusOk = - authorizationStatus == AuthorizationStatus.allowed || - authorizationStatus == AuthorizationStatus.always; - final locationServiceEnabled = - await flutterBeacon.checkLocationServicesIfEnabled; - - setState(() { - this.authorizationStatusOk = authorizationStatusOk; - this.locationServiceEnabled = locationServiceEnabled; - this.bluetoothEnabled = bluetoothEnabled; - }); - } - - initScanBeacon() async { - await flutterBeacon.initializeScanning; - await checkAllRequirements(); - if (!authorizationStatusOk || - !locationServiceEnabled || - !bluetoothEnabled) { - print('RETURNED, authorizationStatusOk=$authorizationStatusOk, ' - 'locationServiceEnabled=$locationServiceEnabled, ' - 'bluetoothEnabled=$bluetoothEnabled'); - return; - } - final regions = [ - Region( - identifier: 'Cubeacon', - proximityUUID: 'CB10023F-A318-3394-4199-A8730C7C1AEC', - ), - ]; - - if (_streamRanging != null) { - if (_streamRanging.isPaused) { - _streamRanging.resume(); - return; - } - } - - _streamRanging = - flutterBeacon.ranging(regions).listen((RangingResult result) { - print(result); - if (result != null && mounted) { - setState(() { - _regionBeacons[result.region] = result.beacons; - _beacons.clear(); - _regionBeacons.values.forEach((list) { - _beacons.addAll(list); - }); - _beacons.sort(_compareParameters); - }); - } - }); - } - - pauseScanBeacon() async { - _streamRanging?.pause(); - if (_beacons.isNotEmpty) { - setState(() { - _beacons.clear(); - }); - } - } - - int _compareParameters(Beacon a, Beacon b) { - int compare = a.proximityUUID.compareTo(b.proximityUUID); - - if (compare == 0) { - compare = a.major.compareTo(b.major); - } - - if (compare == 0) { - compare = a.minor.compareTo(b.minor); - } - - return compare; - } - - @override - void didChangeAppLifecycleState(AppLifecycleState state) async { - print('AppLifecycleState = $state'); - if (state == AppLifecycleState.resumed) { - if (_streamBluetooth != null && _streamBluetooth.isPaused) { - _streamBluetooth.resume(); - } - await checkAllRequirements(); - if (authorizationStatusOk && locationServiceEnabled && bluetoothEnabled) { - await initScanBeacon(); - } else { - await pauseScanBeacon(); - await checkAllRequirements(); - } - } else if (state == AppLifecycleState.paused) { - _streamBluetooth?.pause(); - } - } - - @override - void dispose() { - WidgetsBinding.instance.removeObserver(this); - streamController?.close(); - _streamRanging?.cancel(); - _streamBluetooth?.cancel(); - flutterBeacon.close; - - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Flutter Beacon'), - centerTitle: false, - actions: [ - if (!authorizationStatusOk && locationServiceEnabled) - IconButton( - icon: Icon(Icons.portable_wifi_off), - color: Colors.red, - onPressed: () async { - await flutterBeacon.requestAuthorization; - }, - ), - if (!locationServiceEnabled) - IconButton( - icon: Icon(Icons.location_off), - color: Colors.red, - onPressed: () async { - if (Platform.isAndroid) { - await flutterBeacon.openLocationSettings; - } else if (Platform.isIOS) { - await showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text('Location Services Off'), - content: Text( - 'Please enable Location Services on Settings > Privacy > Location Services.'), - actions: [ - FlatButton( - onPressed: () => Navigator.pop(context), - child: Text('OK'), - ), - ], - ); - }, - ); - } - }, - ), - StreamBuilder( - builder: (context, snapshot) { - if (snapshot.hasData) { - final state = snapshot.data; - - if (state == BluetoothState.stateOn) { - return IconButton( - icon: Icon(Icons.bluetooth_connected), - onPressed: () {}, - color: Colors.lightBlueAccent, - ); - } - - if (state == BluetoothState.stateOff) { - return IconButton( - icon: Icon(Icons.bluetooth), - onPressed: () async { - if (Platform.isAndroid) { - try { - await flutterBeacon.openBluetoothSettings; - } on PlatformException catch (e) { - print(e); - } - } else if (Platform.isIOS) { - await showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text('Bluetooth is Off'), - content: Text( - 'Please enable Bluetooth on Settings > Bluetooth.'), - actions: [ - FlatButton( - onPressed: () => Navigator.pop(context), - child: Text('OK'), - ), - ], - ); - }, - ); - } - }, - color: Colors.red, - ); - } - - return IconButton( - icon: Icon(Icons.bluetooth_disabled), - onPressed: () {}, - color: Colors.grey, - ); - } - - return SizedBox.shrink(); - }, - stream: streamController.stream, - initialData: BluetoothState.stateUnknown, - ), - ], - ), - body: _beacons == null || _beacons.isEmpty - ? Center(child: CircularProgressIndicator()) - : ListView( - children: ListTile.divideTiles( - context: context, - tiles: _beacons.map( - (beacon) { - return ListTile( - title: Text( - beacon.proximityUUID, - style: TextStyle(fontSize: 15.0), - ), - subtitle: new Row( - mainAxisSize: MainAxisSize.max, - children: [ - Flexible( - child: Text( - 'Major: ${beacon.major}\nMinor: ${beacon.minor}', - style: TextStyle(fontSize: 13.0), - ), - flex: 1, - fit: FlexFit.tight, - ), - Flexible( - child: Text( - 'Accuracy: ${beacon.accuracy}m\nRSSI: ${beacon.rssi}', - style: TextStyle(fontSize: 13.0), - ), - flex: 2, - fit: FlexFit.tight, - ) - ], - ), - ); - }, - ), - ).toList(), - ), - ); - } -} \ No newline at end of file diff --git a/example/lib/controller/requirement_state_controller.dart b/example/lib/controller/requirement_state_controller.dart new file mode 100644 index 00000000..ec9864fd --- /dev/null +++ b/example/lib/controller/requirement_state_controller.dart @@ -0,0 +1,60 @@ +import 'package:flutter_beacon/flutter_beacon.dart'; +import 'package:get/get.dart'; + +class RequirementStateController extends GetxController { + var bluetoothState = BluetoothState.stateOff.obs; + var authorizationStatus = AuthorizationStatus.notDetermined.obs; + var locationService = false.obs; + + var _startBroadcasting = false.obs; + var _startScanning = false.obs; + var _pauseScanning = false.obs; + + bool get bluetoothEnabled => bluetoothState.value == BluetoothState.stateOn; + bool get authorizationStatusOk => + authorizationStatus.value == AuthorizationStatus.allowed || + authorizationStatus.value == AuthorizationStatus.always; + bool get locationServiceEnabled => locationService.value; + + updateBluetoothState(BluetoothState state) { + bluetoothState.value = state; + } + + updateAuthorizationStatus(AuthorizationStatus status) { + authorizationStatus.value = status; + } + + updateLocationService(bool flag) { + locationService.value = flag; + } + + startBroadcasting() { + _startBroadcasting.value = true; + } + + stopBroadcasting() { + _startBroadcasting.value = false; + } + + startScanning() { + _startScanning.value = true; + _pauseScanning.value = false; + } + + pauseScanning() { + _startScanning.value = false; + _pauseScanning.value = true; + } + + Stream get startBroadcastStream { + return _startBroadcasting.stream; + } + + Stream get startStream { + return _startScanning.stream; + } + + Stream get pauseStream { + return _pauseScanning.stream; + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index 74fc4785..e86f272d 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,11 +1,9 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; - import 'package:flutter/services.dart'; +import 'package:get/get.dart'; -import 'app_scanning.dart' as s; -import 'app_broadcasting.dart' as b; +import 'controller/requirement_state_controller.dart'; +import 'view/home_page.dart'; void main() { runApp(MainApp()); @@ -14,10 +12,12 @@ void main() { class MainApp extends StatelessWidget { @override Widget build(BuildContext context) { + Get.put(RequirementStateController()); + final themeData = Theme.of(context); final primary = Colors.blue; - return MaterialApp( + return GetMaterialApp( theme: ThemeData( brightness: Brightness.light, primarySwatch: primary, @@ -40,8 +40,9 @@ class MainApp extends StatelessWidget { ), darkTheme: ThemeData( brightness: Brightness.dark, + primarySwatch: primary, ), - home: Platform.isIOS ? b.MyApp() : s.MyApp(), + home: HomePage(), ); } } diff --git a/example/lib/view/app_broadcasting.dart b/example/lib/view/app_broadcasting.dart new file mode 100644 index 00000000..43219e59 --- /dev/null +++ b/example/lib/view/app_broadcasting.dart @@ -0,0 +1,182 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter/services.dart'; +import 'package:flutter_beacon/flutter_beacon.dart'; +import 'package:flutter_beacon_example/controller/requirement_state_controller.dart'; +import 'package:get/get.dart'; + +class TabBroadcasting extends StatefulWidget { + @override + _TabBroadcastingState createState() => _TabBroadcastingState(); +} + +class _TabBroadcastingState extends State { + final controller = Get.find(); + final clearFocus = FocusNode(); + bool broadcasting = false; + + final regexUUID = RegExp( + r'[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}'); + final uuidController = + TextEditingController(text: 'CB10023F-A318-3394-4199-A8730C7C1AEC'); + final majorController = TextEditingController(text: '0'); + final minorController = TextEditingController(text: '0'); + + bool get broadcastReady => + controller.authorizationStatusOk == true && + controller.locationServiceEnabled == true && + controller.bluetoothEnabled == true; + + @override + void initState() { + super.initState(); + + controller.startBroadcastStream.listen((flag) { + if (flag == true) { + initScanBeacon(); + } + }); + } + + initScanBeacon() async { + await flutterBeacon.initializeScanning; + } + + @override + void dispose() { + clearFocus.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: GestureDetector( + onTap: () => FocusScope.of(context).requestFocus(clearFocus), + child: broadcastReady != true + ? Center(child: Text('Please wait...')) + : Form( + autovalidateMode: AutovalidateMode.onUserInteraction, + child: Container( + padding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + uuidField, + majorField, + minorField, + SizedBox(height: 16), + buttonBroadcast, + ], + ), + ), + ), + ), + ); + } + + Widget get uuidField { + return TextFormField( + readOnly: broadcasting, + controller: uuidController, + decoration: InputDecoration( + labelText: 'Proximity UUID', + ), + validator: (val) { + if (val == null || val.isEmpty) { + return 'Proximity UUID required'; + } + + if (!regexUUID.hasMatch(val)) { + return 'Invalid Proxmity UUID format'; + } + + return null; + }, + ); + } + + Widget get majorField { + return TextFormField( + readOnly: broadcasting, + controller: majorController, + decoration: InputDecoration( + labelText: 'Major', + ), + keyboardType: TextInputType.number, + validator: (val) { + if (val == null || val.isEmpty) { + return 'Major required'; + } + + try { + int major = int.parse(val); + + if (major < 0 || major > 65535) { + return 'Major must be number between 0 and 65535'; + } + } on FormatException { + return 'Major must be number'; + } + + return null; + }, + ); + } + + Widget get minorField { + return TextFormField( + readOnly: broadcasting, + controller: minorController, + decoration: InputDecoration( + labelText: 'Minor', + ), + keyboardType: TextInputType.number, + validator: (val) { + if (val == null || val.isEmpty) { + return 'Minor required'; + } + + try { + int minor = int.parse(val); + + if (minor < 0 || minor > 65535) { + return 'Minor must be number between 0 and 65535'; + } + } on FormatException { + return 'Minor must be number'; + } + + return null; + }, + ); + } + + Widget get buttonBroadcast { + return RaisedButton( + onPressed: () async { + if (broadcasting) { + await flutterBeacon.stopBroadcast(); + } else { + await flutterBeacon.startBroadcast(BeaconBroadcast( + proximityUUID: uuidController.text, + major: int.tryParse(majorController.text) ?? 0, + minor: int.tryParse(minorController.text) ?? 0, + )); + } + + final isBroadcasting = await flutterBeacon.isBroadcasting(); + + if (mounted) { + setState(() { + broadcasting = isBroadcasting; + }); + } + }, + child: Text('Broadcast${broadcasting ? 'ing' : ''}'), + color: broadcasting ? Colors.red : Theme.of(context).primaryColor, + textColor: Colors.white, + ); + } +} diff --git a/example/lib/view/app_scanning.dart b/example/lib/view/app_scanning.dart new file mode 100644 index 00000000..5c0327ff --- /dev/null +++ b/example/lib/view/app_scanning.dart @@ -0,0 +1,149 @@ +import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'package:flutter_beacon/flutter_beacon.dart'; +import 'package:get/get.dart'; + +import '../controller/requirement_state_controller.dart'; + +class TabScanning extends StatefulWidget { + @override + _TabScanningState createState() => _TabScanningState(); +} + +class _TabScanningState extends State { + StreamSubscription _streamRanging; + final _regionBeacons = >{}; + final _beacons = []; + final controller = Get.find(); + + @override + void initState() { + super.initState(); + + controller.startStream.listen((flag) { + if (flag == true) { + initScanBeacon(); + } + }); + + controller.pauseStream.listen((flag) { + if (flag == true) { + pauseScanBeacon(); + } + }); + } + + initScanBeacon() async { + await flutterBeacon.initializeScanning; + if (!controller.authorizationStatusOk || + !controller.locationServiceEnabled || + !controller.bluetoothEnabled) { + print('RETURNED, authorizationStatusOk=${controller.authorizationStatusOk}, ' + 'locationServiceEnabled=${controller.locationServiceEnabled}, ' + 'bluetoothEnabled=${controller.bluetoothEnabled}'); + return; + } + final regions = [ + Region( + identifier: 'Cubeacon', + proximityUUID: 'CB10023F-A318-3394-4199-A8730C7C1AEC', + ), + ]; + + if (_streamRanging != null) { + if (_streamRanging.isPaused) { + _streamRanging.resume(); + return; + } + } + + _streamRanging = + flutterBeacon.ranging(regions).listen((RangingResult result) { + print(result); + if (result != null && mounted) { + setState(() { + _regionBeacons[result.region] = result.beacons; + _beacons.clear(); + _regionBeacons.values.forEach((list) { + _beacons.addAll(list); + }); + _beacons.sort(_compareParameters); + }); + } + }); + } + + pauseScanBeacon() async { + _streamRanging?.pause(); + if (_beacons.isNotEmpty) { + setState(() { + _beacons.clear(); + }); + } + } + + int _compareParameters(Beacon a, Beacon b) { + int compare = a.proximityUUID.compareTo(b.proximityUUID); + + if (compare == 0) { + compare = a.major.compareTo(b.major); + } + + if (compare == 0) { + compare = a.minor.compareTo(b.minor); + } + + return compare; + } + + @override + void dispose() { + _streamRanging?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: _beacons == null || _beacons.isEmpty + ? Center(child: CircularProgressIndicator()) + : ListView( + children: ListTile.divideTiles( + context: context, + tiles: _beacons.map( + (beacon) { + return ListTile( + title: Text( + beacon.proximityUUID, + style: TextStyle(fontSize: 15.0), + ), + subtitle: new Row( + mainAxisSize: MainAxisSize.max, + children: [ + Flexible( + child: Text( + 'Major: ${beacon.major}\nMinor: ${beacon.minor}', + style: TextStyle(fontSize: 13.0), + ), + flex: 1, + fit: FlexFit.tight, + ), + Flexible( + child: Text( + 'Accuracy: ${beacon.accuracy}m\nRSSI: ${beacon.rssi}', + style: TextStyle(fontSize: 13.0), + ), + flex: 2, + fit: FlexFit.tight, + ) + ], + ), + ); + }, + ), + ).toList(), + ), + ); + } +} \ No newline at end of file diff --git a/example/lib/view/home_page.dart b/example/lib/view/home_page.dart new file mode 100644 index 00000000..08dacf84 --- /dev/null +++ b/example/lib/view/home_page.dart @@ -0,0 +1,267 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_beacon/flutter_beacon.dart'; +import 'package:get/get.dart'; + +import 'app_scanning.dart'; +import 'app_broadcasting.dart'; +import '../controller/requirement_state_controller.dart'; + +class HomePage extends StatefulWidget { + @override + _HomePageState createState() => _HomePageState(); +} + +class _HomePageState extends State with WidgetsBindingObserver { + final controller = Get.find(); + StreamSubscription _streamBluetooth; + int currentIndex = 0; + + @override + void initState() { + WidgetsBinding.instance.addObserver(this); + + super.initState(); + + listeningState(); + } + + listeningState() async { + print('Listening to bluetooth state'); + _streamBluetooth = flutterBeacon + .bluetoothStateChanged() + .listen((BluetoothState state) async { + print('BluetoothState = $state'); + controller.updateBluetoothState(state); + + if (state == BluetoothState.stateOn) { + checkAllRequirements(); + } + }); + } + + checkAllRequirements() async { + final bluetoothState = await flutterBeacon.bluetoothState; + controller.updateBluetoothState(bluetoothState); + + final authorizationStatus = await flutterBeacon.authorizationStatus; + controller.updateAuthorizationStatus(authorizationStatus); + + final locationServiceEnabled = + await flutterBeacon.checkLocationServicesIfEnabled; + controller.updateLocationService(locationServiceEnabled); + + if (controller.bluetoothEnabled && + controller.locationServiceEnabled && + controller.locationServiceEnabled) { + controller.startScanning(); + } + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) async { + print('AppLifecycleState = $state'); + if (state == AppLifecycleState.resumed) { + if (_streamBluetooth != null && _streamBluetooth.isPaused) { + _streamBluetooth.resume(); + } + await checkAllRequirements(); + if (controller.authorizationStatusOk && + controller.locationServiceEnabled && + controller.bluetoothEnabled) { + if (currentIndex == 0) { + controller.startScanning(); + } else { + controller.pauseScanning(); + } + } else { + if (currentIndex == 0) { + controller.pauseScanning(); + } else { + + } + await checkAllRequirements(); + } + } else if (state == AppLifecycleState.paused) { + _streamBluetooth?.pause(); + } + } + + @override + void dispose() { + _streamBluetooth?.cancel(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + preferredSize: Size.fromHeight(kToolbarHeight), + child: Obx(() => AppBar( + title: const Text('Flutter Beacon'), + centerTitle: false, + actions: [ + if (controller.locationServiceEnabled) + if (!controller.authorizationStatusOk) + IconButton( + tooltip: 'Not Authorized', + icon: Icon(Icons.portable_wifi_off), + color: Colors.red, + onPressed: () async { + await flutterBeacon.requestAuthorization; + }, + ) + else + IconButton( + tooltip: 'Authorized', + icon: Icon(Icons.wifi_tethering), + color: Colors.blue, + onPressed: () async { + await flutterBeacon.requestAuthorization; + }, + ), + IconButton( + tooltip: controller.locationServiceEnabled + ? 'Location Service ON' + : 'Location Service OFF', + icon: Icon( + controller.locationServiceEnabled + ? Icons.location_on + : Icons.location_off, + ), + color: controller.locationServiceEnabled + ? Colors.blue + : Colors.red, + onPressed: controller.locationServiceEnabled + ? () {} + : handleOpenLocationSettings, + ), + StreamBuilder( + builder: (context, snapshot) { + if (snapshot.hasData) { + final state = snapshot.data; + + if (state == BluetoothState.stateOn) { + return IconButton( + tooltip: 'Bluetooth ON', + icon: Icon(Icons.bluetooth_connected), + onPressed: () {}, + color: Colors.lightBlueAccent, + ); + } + + if (state == BluetoothState.stateOff) { + return IconButton( + tooltip: 'Bluetooth OFF', + icon: Icon(Icons.bluetooth), + onPressed: handleOpenBluetooth, + color: Colors.red, + ); + } + + return IconButton( + icon: Icon(Icons.bluetooth_disabled), + tooltip: 'Bluetooth State Unknown', + onPressed: () {}, + color: Colors.grey, + ); + } + + return SizedBox.shrink(); + }, + stream: controller.bluetoothState.stream, + initialData: BluetoothState.stateUnknown, + ), + ], + )), + ), + body: IndexedStack( + index: currentIndex, + children: [ + TabScanning(), + TabBroadcasting(), + ], + ), + bottomNavigationBar: BottomNavigationBar( + currentIndex: currentIndex, + onTap: (index) { + setState(() { + currentIndex = index; + }); + + if (currentIndex == 0) { + controller.startScanning(); + } else { + controller.pauseScanning(); + controller.startBroadcasting(); + } + }, + items: [ + BottomNavigationBarItem( + icon: Icon(Icons.list), + label: 'Scan', + ), + BottomNavigationBarItem( + icon: Icon(Icons.bluetooth_audio), + label: 'Broadcast', + ), + ], + ), + ); + } + + handleOpenLocationSettings() async { + if (Platform.isAndroid) { + await flutterBeacon.openLocationSettings; + } else if (Platform.isIOS) { + await showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text('Location Services Off'), + content: Text( + 'Please enable Location Services on Settings > Privacy > Location Services.', + ), + actions: [ + FlatButton( + onPressed: () => Navigator.pop(context), + child: Text('OK'), + ), + ], + ); + }, + ); + } + } + + handleOpenBluetooth() async { + if (Platform.isAndroid) { + try { + await flutterBeacon.openBluetoothSettings; + } on PlatformException catch (e) { + print(e); + } + } else if (Platform.isIOS) { + await showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text('Bluetooth is Off'), + content: Text('Please enable Bluetooth on Settings > Bluetooth.'), + actions: [ + FlatButton( + onPressed: () => Navigator.pop(context), + child: Text('OK'), + ), + ], + ); + }, + ); + } + } +} diff --git a/example/pubspec.yaml b/example/pubspec.yaml index ee4aba3b..36f23087 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -9,55 +9,14 @@ dependencies: flutter: sdk: flutter - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.2 - dev_dependencies: flutter_test: sdk: flutter flutter_beacon: path: ../ + get: ^3.20.0 -# For information on the generic Dart part of this file, see the -# following page: https://www.dartlang.org/tools/pub/pubspec - -# The following section is specific to Flutter. flutter: - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.io/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.io/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.io/custom-fonts/#from-packages