123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- // Copyright 2017, Paul DeMarco.
- // All rights reserved. Use of this source code is governed by a
- // BSD-style license that can be found in the LICENSE file.
-
- import 'dart:async';
-
- import 'package:flutter/material.dart';
- import 'package:flutter_blue/flutter_blue.dart';
- import 'package:flutter_blue_example/widgets.dart';
-
- void main() {
- runApp(new FlutterBlueApp());
- }
-
- class FlutterBlueApp extends StatefulWidget {
- FlutterBlueApp({Key key, this.title}) : super(key: key);
-
- final String title;
-
- @override
- _FlutterBlueAppState createState() => new _FlutterBlueAppState();
- }
-
- class _FlutterBlueAppState extends State<FlutterBlueApp> {
- FlutterBlue _flutterBlue = FlutterBlue.instance;
-
- /// Scanning
- StreamSubscription _scanSubscription;
- Map<DeviceIdentifier, ScanResult> scanResults = new Map();
- bool isScanning = false;
-
- /// State
- StreamSubscription _stateSubscription;
- BluetoothState state = BluetoothState.unknown;
-
- /// Device
- BluetoothDevice device;
- bool get isConnected => (device != null);
- StreamSubscription deviceConnection;
- StreamSubscription deviceStateSubscription;
- List<BluetoothService> services = new List();
- Map<Guid, StreamSubscription> valueChangedSubscriptions = {};
- BluetoothDeviceState deviceState = BluetoothDeviceState.disconnected;
-
- @override
- void initState() {
- super.initState();
- // Immediately get the state of FlutterBlue
- _flutterBlue.state.then((s) {
- setState(() {
- state = s;
- });
- });
- // Subscribe to state changes
- _stateSubscription = _flutterBlue.onStateChanged().listen((s) {
- setState(() {
- state = s;
- });
- });
- }
-
- @override
- void dispose() {
- _stateSubscription?.cancel();
- _stateSubscription = null;
- _scanSubscription?.cancel();
- _scanSubscription = null;
- deviceConnection?.cancel();
- deviceConnection = null;
- super.dispose();
- }
-
- _startScan() {
- _scanSubscription = _flutterBlue
- .scan(
- timeout: const Duration(seconds: 5),
- /*withServices: [
- new Guid('0000180F-0000-1000-8000-00805F9B34FB')
- ]*/
- )
- .listen((scanResult) {
- print('localName: ${scanResult.advertisementData.localName}');
- print(
- 'manufacturerData: ${scanResult.advertisementData.manufacturerData}');
- print('serviceData: ${scanResult.advertisementData.serviceData}');
- setState(() {
- scanResults[scanResult.device.id] = scanResult;
- });
- }, onDone: _stopScan);
-
- setState(() {
- isScanning = true;
- });
- }
-
- _stopScan() {
- _scanSubscription?.cancel();
- _scanSubscription = null;
- setState(() {
- isScanning = false;
- });
- }
-
- _connect(BluetoothDevice d) async {
- device = d;
- // Connect to device
- deviceConnection = _flutterBlue
- .connect(device, timeout: const Duration(seconds: 4))
- .listen(
- null,
- onDone: _disconnect,
- );
-
- // Update the connection state immediately
- device.state.then((s) {
- setState(() {
- deviceState = s;
- });
- });
-
- // Subscribe to connection changes
- deviceStateSubscription = device.onStateChanged().listen((s) {
- setState(() {
- deviceState = s;
- });
- if (s == BluetoothDeviceState.connected) {
- device.discoverServices().then((s) {
- setState(() {
- services = s;
- });
- });
- }
- });
- }
-
- _disconnect() {
- // Remove all value changed listeners
- valueChangedSubscriptions.forEach((uuid, sub) => sub.cancel());
- valueChangedSubscriptions.clear();
- deviceStateSubscription?.cancel();
- deviceStateSubscription = null;
- deviceConnection?.cancel();
- deviceConnection = null;
- setState(() {
- device = null;
- });
- }
-
- _readCharacteristic(BluetoothCharacteristic c) async {
- await device.readCharacteristic(c);
- setState(() {});
- }
-
- _writeCharacteristic(BluetoothCharacteristic c) async {
- await device.writeCharacteristic(c, [0x12, 0x34],
- type: CharacteristicWriteType.withResponse);
- setState(() {});
- }
-
- _readDescriptor(BluetoothDescriptor d) async {
- await device.readDescriptor(d);
- setState(() {});
- }
-
- _writeDescriptor(BluetoothDescriptor d) async {
- await device.writeDescriptor(d, [0x12, 0x34]);
- setState(() {});
- }
-
- _setNotification(BluetoothCharacteristic c) async {
- if (c.isNotifying) {
- await device.setNotifyValue(c, false);
- // Cancel subscription
- valueChangedSubscriptions[c.uuid]?.cancel();
- valueChangedSubscriptions.remove(c.uuid);
- } else {
- await device.setNotifyValue(c, true);
- // ignore: cancel_subscriptions
- final sub = device.onValueChanged(c).listen((d) {
- setState(() {
- print('onValueChanged $d');
- });
- });
- // Add to map
- valueChangedSubscriptions[c.uuid] = sub;
- }
- setState(() {});
- }
-
- _refreshDeviceState(BluetoothDevice d) async {
- var state = await d.state;
- setState(() {
- deviceState = state;
- print('State refreshed: $deviceState');
- });
- }
-
- _buildScanningButton() {
- if (isConnected || state != BluetoothState.on) {
- return null;
- }
- if (isScanning) {
- return new FloatingActionButton(
- child: new Icon(Icons.stop),
- onPressed: _stopScan,
- backgroundColor: Colors.red,
- );
- } else {
- return new FloatingActionButton(
- child: new Icon(Icons.search), onPressed: _startScan);
- }
- }
-
- _buildScanResultTiles() {
- return scanResults.values
- .map((r) => ScanResultTile(
- result: r,
- onTap: () => _connect(r.device),
- ))
- .toList();
- }
-
- List<Widget> _buildServiceTiles() {
- return services
- .map(
- (s) => new ServiceTile(
- service: s,
- characteristicTiles: s.characteristics
- .map(
- (c) => new CharacteristicTile(
- characteristic: c,
- onReadPressed: () => _readCharacteristic(c),
- onWritePressed: () => _writeCharacteristic(c),
- onNotificationPressed: () => _setNotification(c),
- descriptorTiles: c.descriptors
- .map(
- (d) => new DescriptorTile(
- descriptor: d,
- onReadPressed: () => _readDescriptor(d),
- onWritePressed: () =>
- _writeDescriptor(d),
- ),
- )
- .toList(),
- ),
- )
- .toList(),
- ),
- )
- .toList();
- }
-
- _buildActionButtons() {
- if (isConnected) {
- return <Widget>[
- new IconButton(
- icon: const Icon(Icons.cancel),
- onPressed: () => _disconnect(),
- )
- ];
- }
- }
-
- _buildAlertTile() {
- return new Container(
- color: Colors.redAccent,
- child: new ListTile(
- title: new Text(
- 'Bluetooth adapter is ${state.toString().substring(15)}',
- style: Theme.of(context).primaryTextTheme.subhead,
- ),
- trailing: new Icon(
- Icons.error,
- color: Theme.of(context).primaryTextTheme.subhead.color,
- ),
- ),
- );
- }
-
- _buildDeviceStateTile() {
- return new ListTile(
- leading: (deviceState == BluetoothDeviceState.connected)
- ? const Icon(Icons.bluetooth_connected)
- : const Icon(Icons.bluetooth_disabled),
- title: new Text('Device is ${deviceState.toString().split('.')[1]}.'),
- subtitle: new Text('${device.id}'),
- trailing: new IconButton(
- icon: const Icon(Icons.refresh),
- onPressed: () => _refreshDeviceState(device),
- color: Theme.of(context).iconTheme.color.withOpacity(0.5),
- ));
- }
-
- _buildProgressBarTile() {
- return new LinearProgressIndicator();
- }
-
- @override
- Widget build(BuildContext context) {
- var tiles = new List<Widget>();
- if (state != BluetoothState.on) {
- tiles.add(_buildAlertTile());
- }
- if (isConnected) {
- tiles.add(_buildDeviceStateTile());
- tiles.addAll(_buildServiceTiles());
- } else {
- tiles.addAll(_buildScanResultTiles());
- }
- return new MaterialApp(
- home: new Scaffold(
- appBar: new AppBar(
- title: const Text('FlutterBlue'),
- actions: _buildActionButtons(),
- ),
- floatingActionButton: _buildScanningButton(),
- body: new Stack(
- children: <Widget>[
- (isScanning) ? _buildProgressBarTile() : new Container(),
- new ListView(
- children: tiles,
- )
- ],
- ),
- ),
- );
- }
- }
|