diff --git a/.gitignore b/.gitignore index 24476c5..7c452cc 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,65 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release + +## custom + +**/ios/** +**/macos/** +**/windows/** +**/linux/** + +## https://github.com/flutter/flutter/blob/master/.gitignore + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/.last_build_id +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/ephemeral +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# macOS +**/Flutter/ephemeral/ +**/Pods/ +**/macos/Flutter/GeneratedPluginRegistrant.swift +**/macos/Flutter/ephemeral +**/xcuserdata/ + +# Windows +**/windows/flutter/generated_plugin_registrant.cc +**/windows/flutter/generated_plugin_registrant.h + +# Linux +**/linux/flutter/generated_plugin_registrant.cc +**/linux/flutter/generated_plugin_registrant.h + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages +!/dev/ci/**/Gemfile.lock diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 0441917..70a62fd 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -6,6 +6,7 @@ + > readJson() async { return data_; } +/*Future checkNameAvailability(String input) async { + // await cloudServiceAPI.loadConfig(); + List devices = await cloudServiceAPI.getDevices(); + for (Map selected in devices) { + if (selected["id"] == input) { + await showNameAvailabilityStatus(true); + return; + } + } + await showNameAvailabilityStatus(false); + } + + Future showNameAvailabilityStatus(bool status) async { + String statusText = status + ? "die eingegebene ID ist verfügbar" + : "die eingegebene ID ist nicht verfügbar"; + Fluttertoast.showToast( + msg: statusText, + toastLength: Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + timeInSecForIosWeb: 2, + backgroundColor: Colors.grey[200], + textColor: Colors.black, + fontSize: 16.0); + }*/ + class CloudServiceAPI { static late final Map credentials; static late Future loadJson; @@ -50,22 +76,22 @@ class CloudServiceAPI { }; } Future getDevices() async { - var url = Uri.https(address, '/api/devices'); + Uri url = Uri.https(address, '/api/devices'); Response r = await get(url, headers: headers); return json.decode(r.body) as List; } Future> getDeviceInfo(deviceID) async { - var url = Uri.https(address, '/api/devices/$deviceID'); + Uri url = Uri.https(address, '/api/devices/$deviceID'); Response r = await get(url, headers: headers); return json.decode(r.body) as Map; } Future> getInformation() async { - var url = Uri.https(address, '/api/app'); + Uri url = Uri.https(address, '/api/app'); Response r = await get(url, headers: headers); return json.decode(r.body) as Map; } Future createDevice(id, primaryThumbprint, secondaryThumbprint) async{ - var url = Uri.https(address, '/api/devices'); + Uri url = Uri.https(address, '/api/devices'); Response r = await post( url, headers: headers, diff --git a/lib/screens/bluetooth_device_list_entry.dart b/lib/screens/bluetooth_device_list_entry.dart new file mode 100644 index 0000000..7135d44 --- /dev/null +++ b/lib/screens/bluetooth_device_list_entry.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart'; + +class BluetoothDeviceListEntry extends ListTile { + BluetoothDeviceListEntry({super.key, + required BluetoothDevice device, + int? rssi, + GestureTapCallback? onTap, + GestureLongPressCallback? onLongPress, + bool enabled = true, + }) : super( + onTap: onTap, + onLongPress: onLongPress, + enabled: enabled, + leading: Icon(device.isConnected ? Icons.bluetooth_connected : Icons.bluetooth), + title: Text(device.name ?? "~ UNKNOWN_DEVICE ~"), + subtitle: Text(device.address.toString()), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + rssi != null + ? Container( + margin: const EdgeInsets.all(8.0), + child: DefaultTextStyle( + style: _computeTextStyle(rssi), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(rssi.toString()), + const Text('dBm'), + ], + ), + ), + ) + : const SizedBox(width: 0, height: 0), + device.isBonded + ? const Icon(Icons.link) + : const SizedBox(width: 0, height: 0), + IconButton( + onPressed: () {}, + icon: const Icon(Icons.settings) + ), + ], + ), + ); + + static TextStyle _computeTextStyle(int rssi) { + /**/ if (rssi >= -35) { + return TextStyle(color: Colors.greenAccent[700]); + } else if (rssi >= -45) { + return TextStyle( + color: Color.lerp( + Colors.greenAccent[700], Colors.lightGreen, -(rssi + 35) / 10)); + } else if (rssi >= -55) { + return TextStyle( + color: Color.lerp( + Colors.lightGreen, Colors.lime[600], -(rssi + 45) / 10)); + } else if (rssi >= -65) { + return TextStyle( + color: Color.lerp(Colors.lime[600], Colors.amber, -(rssi + 55) / 10)); + } else if (rssi >= -75) { + return TextStyle( + color: Color.lerp( + Colors.amber, Colors.deepOrangeAccent, -(rssi + 65) / 10)); + } else if (rssi >= -85) { + return TextStyle( + color: Color.lerp( + Colors.deepOrangeAccent, Colors.redAccent, -(rssi + 75) / 10)); + } else { + return const TextStyle(color: Colors.redAccent); + } + } +} \ No newline at end of file diff --git a/lib/screens/bluetooth_screen.dart b/lib/screens/bluetooth_screen.dart index 34f0cf5..1d8541b 100644 --- a/lib/screens/bluetooth_screen.dart +++ b/lib/screens/bluetooth_screen.dart @@ -1,37 +1,369 @@ import 'dart:async'; -import 'dart:developer'; -import 'dart:io'; -import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart'; -import 'package:fluttertoast/fluttertoast.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_provisioning_for_iot/widgets/bluetooth_discovery.dart'; -import 'package:flutter_provisioning_for_iot/objects/cloud_service_api.dart'; -import 'package:flutter_provisioning_for_iot/widgets/switch_widget.dart'; +import 'dart:convert'; +import 'dart:typed_data'; +import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_provisioning_for_iot/objects/cloud_service_api.dart'; +import 'package:flutter_provisioning_for_iot/screens/bluetooth_device_list_entry.dart'; + +import 'package:app_settings/app_settings.dart'; +import 'package:permission_handler/permission_handler.dart'; + + class BluetoothScreen extends StatefulWidget { + final bool start = true; + const BluetoothScreen({super.key}); @override State createState() => _BluetoothScreen(); } +// debugPrint("r: ${r.device.name} ${r.device.address} ${r.rssi}"); class _BluetoothScreen extends State { - final textFieldValueHolder = TextEditingController(); CloudServiceAPI cloudServiceAPI = CloudServiceAPI(); - List results = - List.empty(growable: true); + //BluetoothManager bluetoothManager = BluetoothManager(); + + late Stream _stream; + late StreamSubscription _streamSubscription; + late Stream _connectionStream; + late StreamSubscription _connectionStreamSubscription; + BluetoothDevice? _bluetoothDevice; + BluetoothConnection? _bluetoothConnection; + final List _discoveryResults = + List.empty(growable: true); + String _messageBuffer = ""; + bool isDiscovering = false; + bool isConnecting = false; + bool isDisconnecting = false; + bool isConnected = false; + ButtonStyle buttonStyle = ElevatedButton.styleFrom( foregroundColor: Colors.black, backgroundColor: const Color(0xFFFDE100), // Text Color (Foreground color) ); - String inputName = ""; - bool initScan = true; - bool scanState = false; - bool widgetScanState = false; + @override + void initState() { + super.initState(); + isDiscovering = widget.start; + if (isDiscovering) { + _startDiscovery(); + } + } + + @override + void dispose() { + // Avoid memory leak (`setState` after dispose) and cancel discovery + _streamSubscription.cancel(); + if (isConnected) { + isDisconnecting = true; + _bluetoothConnection?.dispose(); + _bluetoothConnection = null; + } + super.dispose(); + } + + Future _disconnectDevice(BluetoothDevice device) async { + if (_bluetoothConnection != null) { + await _bluetoothConnection!.finish(); + _bluetoothConnection = null; + } + } + + Future _connectDevice(BluetoothDevice device) async { + String address = device.address; + if (_bluetoothConnection != null) { + await _bluetoothConnection!.finish(); + _bluetoothConnection = null; + } + _bluetoothConnection = await BluetoothConnection.toAddress(address); + debugPrint("Connected to the device"); + setState(() { + isConnecting = false; + isDisconnecting = false; + }); + + _connectionStream = _bluetoothConnection!.input!; + _connectionStreamSubscription = _connectionStream.listen(_connectionOnListen); + _connectionStreamSubscription.onDone(_connectionOnDone); + } + + void _connectionOnDone() { + /* + // Example: Detect which side closed the connection + // There should be `isDisconnecting` flag to show are we are (locally) + // in middle of disconnecting process, should be set before calling + // `dispose`, `finish` or `close`, which all causes to disconnect. + // If we except the disconnection, `onDone` should be fired as result. + // If we didn't except this (no flag set), it means closing by remote. + */ + if (isDisconnecting) { + debugPrint("Disconnecting locally!"); + } else { + debugPrint("Disconnected remotely!"); + } + if (mounted) { + setState(() {}); + } + } + + void _connectionOnListen(Uint8List data) { + debugPrint("received: $data"); + debugPrint("received decoded: ${const Utf8Decoder().convert(data)}"); + // Allocate buffer for parsed data + int backspacesCounter = 0; + for (var byte in data) { + if (byte == 8 || byte == 127) { + backspacesCounter++; + } + } + Uint8List buffer = Uint8List(data.length - backspacesCounter); + int bufferIndex = buffer.length; + + // Apply backspace control character + backspacesCounter = 0; + for (int i = data.length - 1; i >= 0; i--) { + if (data[i] == 8 || data[i] == 127) { + backspacesCounter++; + } else { + if (backspacesCounter > 0) { + backspacesCounter--; + } else { + buffer[--bufferIndex] = data[i]; + } + } + } + + // Create message if there is new line character + String dataString = String.fromCharCodes(buffer); + int index = buffer.indexOf(13); + if (~index != 0) { + setState(() { + _messageBuffer = dataString.substring(index); + }); + } else { + _messageBuffer = (backspacesCounter > 0 + ? _messageBuffer.substring( + 0, _messageBuffer.length - backspacesCounter) + : _messageBuffer + dataString); + } + } + + Future _startDiscovery() async { + _stream = FlutterBluetoothSerial.instance.startDiscovery(); + _streamSubscription = _stream.listen(_discoveryOnListen); + _streamSubscription.onDone(_discoveryOnDone); + } + + void _discoveryOnDone() { + setState(() { + isDiscovering = false; + //debugPrint(isDiscovering as String?); + }); + } + + void _discoveryOnListen(BluetoothDiscoveryResult event) { + setState(() { + final int existingIndex = _discoveryResults.indexWhere( + (element) => element.device.address == event.device.address); + if (existingIndex >= 0) { + _discoveryResults[existingIndex] = event; + } else { + _discoveryResults.add(event); + } + debugPrint("event: ${event.device.address} ${event.device.name}"); + + String deviceAddress = "64:BC:58:61:56:B0"; + if (event.device.address == deviceAddress) { + _bluetoothDevice = event.device; + } + }); + } + + Future _restartDiscovery() async { + setState(() { + _discoveryResults.clear(); + isDiscovering = true; + }); + _startDiscovery(); + } + + Future _sendData() async { + String output = "0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ !§%&()=?#!?"; + _bluetoothConnection!.output + .add(Uint8List.fromList(const Utf8Encoder().convert("$output \r\n"))); + await _bluetoothConnection!.output.allSent; + debugPrint("sent: $output"); + } + + @override + Widget build(BuildContext context) { + //bool isDark = true; + return Theme( + data: ThemeData.dark(), //isDark ? ThemeData.dark() : ThemeData.light(), + child: Scaffold( + appBar: AppBar( + title: isDiscovering + ? const Text('Bluetooth Devices (searching...)') + : const Text('Bluetooth Devices'), + actions: [ + isDiscovering + ? FittedBox( + child: Container( + margin: const EdgeInsets.all(16.0), + child: const CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Colors.white), + ), + ), + ) + : IconButton( + icon: const Icon(Icons.replay), + onPressed: _restartDiscovery, + ) + ], + ), + + body: // Center( + /*child:*/ SingleChildScrollView( + physics: const ScrollPhysics(), + child: Column( + children: [ + _SingleSection( + title: "Setup", + children: [ + const _CustomListTile( + title: "Please Enable Bluetooth", + icon: Icons.info_outline_rounded, + ), + _CustomListTile( + title: "Bluetooth Settings", + icon: Icons.bluetooth_connected, + onTap: () async { + await AppSettings.openBluetoothSettings(); + }, + ), + ], + ), + const Divider(), + ElevatedButton( + onPressed: () async { + await Permission.bluetoothConnect.request(); + await Permission.bluetoothScan.request(); + if (await Permission.bluetoothScan.request().isGranted) { + // Either the permission was already granted before or the user just granted it. + debugPrint("Location Permission is granted"); + } else { + debugPrint("Location Permission is denied."); + } + }, + style: buttonStyle, + child: const Text("Request Permissions"), + ), + ElevatedButton( + onPressed: () async { + _restartDiscovery(); + }, + style: buttonStyle, + child: const Text("Restart Scan"), + ), + ElevatedButton( + onPressed: () async { + _sendData(); + }, + style: buttonStyle, + child: const Text("Send Data"), + ), + const Divider(), + const _SingleSection( + title: "Bluetooth Devices", + children: [], + ), + const Divider(), + ListView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: _discoveryResults.length, + itemBuilder: (BuildContext context, index) { + BluetoothDiscoveryResult result = _discoveryResults[index]; + final device = result.device; + final address = device.address; + return BluetoothDeviceListEntry( + device: device, + rssi: result.rssi, + onTap: () { + debugPrint(_bluetoothConnection?.isConnected.toString()); + if (_bluetoothConnection?.isConnected ?? false) { + _disconnectDevice(device); + } else { + _connectDevice(device); + } + + //Navigator.of(context).pop(result.device); + }, + onLongPress: () async { + try { + bool bonded = false; + if (device.isBonded) { + debugPrint('Unbonding from ${device.address}...'); + await FlutterBluetoothSerial.instance + .removeDeviceBondWithAddress(address); + debugPrint( + 'Unbonding from ${device.address} has succed'); + } else { + debugPrint('Bonding with ${device.address}...'); + bonded = (await FlutterBluetoothSerial.instance + .bondDeviceAtAddress(address))!; + debugPrint( + 'Bonding with ${device.address} has ${bonded ? 'succed' : 'failed'}.'); + } + setState(() { + _discoveryResults[_discoveryResults.indexOf(result)] = + BluetoothDiscoveryResult( + device: BluetoothDevice( + name: device.name ?? '', + address: address, + type: device.type, + bondState: bonded + ? BluetoothBondState.bonded + : BluetoothBondState.none, + ), + rssi: result.rssi); + }); + } catch (ex) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Error occured while bonding'), + content: Text(ex.toString()), + actions: [ + TextButton( + child: const Text("Close"), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + }, + ); + }, + ), + ], + ), + ), + //), + ), + ); + } +/* @override Widget build(BuildContext context) { return Scaffold( @@ -60,6 +392,19 @@ class _BluetoothScreen extends State { ), child: Column( children: [ + const Text( + "General", + textAlign: TextAlign.start, + ), + Row( + children: [ + _CustomListTile( + title: "Dark Mode", + icon: CupertinoIcons.moon, + trailing: + CupertinoSwitch(value: false, onChanged: (value) {})), + ] + ), TextField( controller: textFieldValueHolder, decoration: const InputDecoration( @@ -81,6 +426,39 @@ class _BluetoothScreen extends State { child: const Text("check name"), ), ), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () { + //_startDiscovery(); + initScan(); + }, + style: buttonStyle, + child: const Text("discover devices"), + ), + ), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () { + //_startDiscovery(); + _sendData(); + }, + style: buttonStyle, + child: const Text("send data"), + ), + ), + SizedBox( + width: double.infinity, + child: ElevatedButton( + onPressed: () { + //_startDiscovery(); + dispose(); + }, + style: buttonStyle, + child: const Text("dispose"), + ), + ), ], ), ), @@ -92,7 +470,7 @@ class _BluetoothScreen extends State { bottom: BorderSide(width: 1.5, color: Colors.grey), ), ), - child: Row(children: [ + child: Row(children: [ const Text( "Toggle Scan", style: TextStyle(fontWeight: FontWeight.bold), @@ -106,7 +484,7 @@ class _BluetoothScreen extends State { setState(() { widgetScanState = toggleState; }); - }) + }) ]), ), // BluetoothDiscovery(start: initScan, deviceID: inputName), @@ -114,37 +492,64 @@ class _BluetoothScreen extends State { ), //), ), - ), ); - } + }*/ +} - Future checkNameAvailability(String input) async { - // await cloudServiceAPI.loadConfig(); - List devices = await cloudServiceAPI.getDevices(); - for (Map selected in devices) { - if (selected["id"] == input) { - await showNameAvailabilityStatus(true); - return; - } - } - await showNameAvailabilityStatus(false); - } +class _CustomListTile extends StatelessWidget { + final String title; + final IconData icon; + final VoidCallback? onTap; + //final Widget? trailing; - Future showNameAvailabilityStatus(bool status) async { - String statusText = status - ? "die eingegebene ID ist verfügbar" - : "die eingegebene ID ist nicht verfügbar"; - Fluttertoast.showToast( - msg: statusText, - toastLength: Toast.LENGTH_SHORT, - gravity: ToastGravity.BOTTOM, - timeInSecForIosWeb: 2, - backgroundColor: Colors.grey[200], - textColor: Colors.black, - fontSize: 16.0); + const _CustomListTile({ + Key? key, + required this.title, + required this.icon, + this.onTap, + //this.trailing, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ListTile( + title: Text(title), + leading: Icon(icon), + onTap: onTap, + //trailing: trailing, + ); } } -class BluetoothScan{ +class _SingleSection extends StatelessWidget { + final String? title; + final List children; + + const _SingleSection({ + Key? key, + this.title, + required this.children, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (title != null) + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + title!, + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + ), + Column( + children: children, + ), + ], + ); + } } diff --git a/lib/screens/bluetooth_screen_serialtemplate.dart b/lib/screens/bluetooth_screen_serialtemplate.dart new file mode 100644 index 0000000..302bcc7 --- /dev/null +++ b/lib/screens/bluetooth_screen_serialtemplate.dart @@ -0,0 +1,163 @@ + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart'; + +import './bluetooth_device_list_entry.dart'; + +class BluetoothScreen extends StatefulWidget { + /// If true, discovery starts on page start, otherwise user must press action button. + final bool start; + + const BluetoothScreen({this.start = true}); + + @override + _BluetoothScreen createState() => new _BluetoothScreen(); +} + +class _BluetoothScreen extends State { + StreamSubscription? _streamSubscription; + List results = + List.empty(growable: true); + bool isDiscovering = false; + + _BluetoothScreen(); + + @override + void initState() { + super.initState(); + + isDiscovering = widget.start; + if (isDiscovering) { + _startDiscovery(); + } + } + + void _restartDiscovery() { + setState(() { + results.clear(); + isDiscovering = true; + }); + + _startDiscovery(); + } + + void _startDiscovery() { + _streamSubscription = + FlutterBluetoothSerial.instance.startDiscovery().listen((r) { + setState(() { + final existingIndex = results.indexWhere( + (element) => element.device.address == r.device.address); + if (existingIndex >= 0) + results[existingIndex] = r; + else + results.add(r); + }); + }); + + _streamSubscription!.onDone(() { + setState(() { + isDiscovering = false; + }); + }); + } + + // @TODO . One day there should be `_pairDevice` on long tap on something... ;) + + @override + void dispose() { + // Avoid memory leak (`setState` after dispose) and cancel discovery + _streamSubscription?.cancel(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: isDiscovering + ? Text('Discovering devices') + : Text('Discovered devices'), + actions: [ + isDiscovering + ? FittedBox( + child: Container( + margin: new EdgeInsets.all(16.0), + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(Colors.white), + ), + ), + ) + : IconButton( + icon: Icon(Icons.replay), + onPressed: _restartDiscovery, + ) + ], + ), + body: ListView.builder( + itemCount: results.length, + itemBuilder: (BuildContext context, index) { + BluetoothDiscoveryResult result = results[index]; + final device = result.device; + final address = device.address; + return BluetoothDeviceListEntry( + device: device, + rssi: result.rssi, + onTap: () { + Navigator.of(context).pop(result.device); + }, + onLongPress: () async { + try { + bool bonded = false; + if (device.isBonded) { + print('Unbonding from ${device.address}...'); + await FlutterBluetoothSerial.instance + .removeDeviceBondWithAddress(address); + print('Unbonding from ${device.address} has succed'); + } else { + print('Bonding with ${device.address}...'); + bonded = (await FlutterBluetoothSerial.instance + .bondDeviceAtAddress(address))!; + print( + 'Bonding with ${device.address} has ${bonded ? 'succed' : 'failed'}.'); + } + setState(() { + results[results.indexOf(result)] = BluetoothDiscoveryResult( + device: BluetoothDevice( + name: device.name ?? '', + address: address, + type: device.type, + bondState: bonded + ? BluetoothBondState.bonded + : BluetoothBondState.none, + ), + rssi: result.rssi); + }); + } catch (ex) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Error occured while bonding'), + content: Text("${ex.toString()}"), + actions: [ + new TextButton( + child: new Text("Close"), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ], + ); + }, + ); + } + }, + ); + }, + ), + ); + } +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index d44df50..098534f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + app_settings: + dependency: "direct main" + description: + name: app_settings + sha256: "7a5b880e2dd41dba8877108180380a1d28d874c231f7c0f9022127a4061b88e1" + url: "https://pub.dev" + source: hosted + version: "4.1.8" async: dependency: transitive description: @@ -224,6 +232,46 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.3" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: "33c6a1253d1f95fd06fa74b65b7ba907ae9811f9d5c1d3150e51417d04b8d6a8" + url: "https://pub.dev" + source: hosted + version: "10.2.0" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "8028362b40c4a45298f1cbfccd227c8dd6caf0e27088a69f2ba2ab15464159e2" + url: "https://pub.dev" + source: hosted + version: "10.2.0" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: "9c370ef6a18b1c4b2f7f35944d644a56aa23576f23abee654cf73968de93f163" + url: "https://pub.dev" + source: hosted + version: "9.0.7" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: "68abbc472002b5e6dfce47fe9898c6b7d8328d58b5d2524f75e277c07a97eb84" + url: "https://pub.dev" + source: hosted + version: "3.9.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: f67cab14b4328574938ecea2db3475dad7af7ead6afab6338772c5f88963e38b + url: "https://pub.dev" + source: hosted + version: "0.1.2" platform: dependency: transitive description: @@ -415,4 +463,4 @@ packages: version: "0.2.0+2" sdks: dart: ">=2.18.0 <4.0.0" - flutter: ">=3.0.0" + flutter: ">=3.0.1" diff --git a/pubspec.yaml b/pubspec.yaml index fa1a577..a5af96b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,6 +41,8 @@ dependencies: shared_preferences: ^2.0.13 http: ^0.13.5 fluttertoast: ^8.1.1 + app_settings: ^4.1.8 + permission_handler: ^10.2.0 dev_dependencies: