// // code from: https://github.com/0015/IdeasNProjects/tree/master/ESP32_MICROPHONE/ESP32_INMP441_FLUTTER_BLE_APP/flutter_app_slm // // add to pubspec.yaml // flutter_blue: ^0.7.2 // flutter_progress_dialog: ^0.1.0 // // change android/app/build.gradle // minSdkVersion 19 (around line 42) // // better connection logic // if you're having problems connecting: // try turn on/off tablet/phone bluetooth momentarily // you can do this by swiping top to bottom and // look for bluetooth logo // // still having problem connecting: // turn on/off bluetooth device // // this example uses stream for notification // make sure you're characteristic is able to notify // use nrfconnect from android play store to test and // discover info about your device like: // 1. device name // 2. services // 3. characteristics // 3.1 characteristics able to read // 3.2 characteristics able to write // 3.3 characteristics able to notify // //////////////////// main.dart ////////////////////////// import 'package:flutter/material.dart'; import 'package:flutter_blue/flutter_blue.dart'; import 'package:flutter_progress_dialog/flutter_progress_dialog.dart'; import './device_view.dart'; void main() { runApp(MyApp()); } class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { @override Widget build(BuildContext context) { return ProgressDialog( orientation: ProgressOrientation.vertical, child: MaterialApp( debugShowCheckedModeBanner: false, theme: ThemeData( brightness: Brightness.light, primaryColor: Color.fromRGBO(58, 66, 86, 1)), home: StreamBuilder( stream: FlutterBlue.instance.state, initialData: BluetoothState.unknown, builder: (c, snapshot) { final state = snapshot.data; if (state == BluetoothState.on) { return BluetoothOnScreen(); } else { return BluetoothOffScreen( state: state, ); } }, ), ), ); } } class BluetoothOnScreen extends StatefulWidget { @override _BluetoothOnScreenState createState() => _BluetoothOnScreenState(); } // // change targetDeviceName to match your device // class _BluetoothOnScreenState extends State { static String targetDeviceName = 'Adafruit Bluefruit LE'; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('BLE test, turn on/off tablet ble solves most problems'), ), body: Center( child: RaisedButton( child: Text('Connect to $targetDeviceName'), onPressed: _deviceSearchingStart, ), ), ); } _deviceSearchingStart() { final String s = "Searching Device...\n\n" "if having problems, turn off/on tablet bluetooth\n\n" "unplug plug bluetooth device"; showProgressDialog(loadingText: s); FlutterBlue.instance .startScan(timeout: Duration(seconds: 5)) .whenComplete(() { FlutterBlue.instance.stopScan(); _scanResult(); dismissProgressDialog(); }); } _scanResult() { FlutterBlue.instance.scanResults.first.then((element) { print('Device Num: ${element.length}'); print(FlutterBlue.instance.connectedDevices.toString()); element.forEach((value) { print('**** ${value.device.id} / ${value.device.name} ****'); if (value.device.name == targetDeviceName) { print('**** FOUND Target Device ****'); print(FlutterBlue.instance.connectedDevices.toString()); _connectDevice(value.device); } }); }); } handleError(BluetoothDevice bd) { bd.disconnect(); } _connectDevice(BluetoothDevice _device)async { try { await _device.connect(); } catch (e) { if (e.code != 'already_connected') { throw e; } } finally { Navigator.of(context).push(MaterialPageRoute( builder: (context) => DeviceView( device: _device, ))); } //_device.connect().whenComplete(() { // Navigator.of(context).push(MaterialPageRoute( // builder: (context) => DeviceView( // device: _device, // ))); //}); } } class BluetoothOffScreen extends StatelessWidget { const BluetoothOffScreen({Key key, this.state}) : super(key: key); final BluetoothState state; @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.bluetooth_disabled, size: 200, color: Colors.black, ), Text( 'Bluetooth Adapter is ${state.toString().substring(15)}', //style: Theme.of(context) // .primaryTextTheme // .subtitle1 // .copyWith(color: Colors.black), ) ], ), ), ); } } /////////////////// device_view.dart ////////////////////// import 'dart:convert' show utf8; import 'package:flutter/material.dart'; import 'package:flutter_blue/flutter_blue.dart'; class DeviceView extends StatefulWidget { const DeviceView({Key key, this.device}) : super(key: key); final BluetoothDevice device; @override _DeviceViewState createState() => _DeviceViewState(); } class _DeviceViewState extends State { Stream> stream; bool isReady; String receivedText; double dBAValue = 0; String dBAStringValue; List points = [50]; @override void initState() { super.initState(); isReady = false; _discoverServices(); } _disconnectFromDevice() { if (widget.device == null) { _pop(); return; } widget.device.disconnect().whenComplete(() => _pop()); } _discoverServices() async { if (widget.device == null) { _pop(); return; } // // change the service.uuid to match yours // change the characteristic.uuid to match yours // make sure your characteristics is able to notify // List services = await widget.device.discoverServices(); services.forEach((service) { if (service.uuid.toString() == "6e400001-b5a3-f393-e0a9-e50e24dcca9e") { service.characteristics.forEach((characteristic) { if (characteristic.uuid.toString() == "6e400003-b5a3-f393-e0a9-e50e24dcca9e") { characteristic.setNotifyValue(true); stream = characteristic.value; setState(() { isReady = true; }); } }); } }); } _pop() { Navigator.of(context).pop(true); } Future _onWillPop() { return showDialog( context: context, builder: (context) => AlertDialog( title: Text('Are you sure?'), content: Text('Do you want to disconnect the device and go back?'), actions: [ FlatButton( onPressed: () => Navigator.of(context).pop(false), child: Text('No'), ), FlatButton( onPressed: () => _disconnectFromDevice(), child: Text('Yes'), ) ], ) ?? false); } String _dataParser(List dataFromDevice) { return utf8.decode(dataFromDevice); } _streamBody(AsyncSnapshot> snapshot) { if (snapshot.connectionState == ConnectionState.active) { var currentValue = _dataParser(snapshot.data); dBAValue = double.tryParse(currentValue) ?? 0; dBAStringValue = dBAValue > 100 ? '' + dBAValue.toString() : '0' + dBAValue.toString(); return Text(dBAStringValue); } else { return Center( child: Text( 'ConnectionState is not active', style: TextStyle(fontSize: 24, color: Colors.white), ), ); } } @override Widget build(BuildContext context) { return WillPopScope( onWillPop: _onWillPop, child: Scaffold( appBar: AppBar( title: Text('Demo'), ), body: Container( child: !isReady ? Center( child: Text( 'Waiting...', style: TextStyle(fontSize: 24, color: Colors.white), ), ) : Container( child: StreamBuilder>( stream: stream, builder: (BuildContext context, AsyncSnapshot> snapshot) { if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); } return _streamBody(snapshot); }, ), ))), ); } }