bluetooth 0.1.0 bluetooth: ^0.1.0 copied to clipboard
Bluetooth plugin for Flutter
// 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:bluetooth/bluetooth.dart';
import './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<BluetoothCharacteristicIdentifier, 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: [
Uuid.fromString('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.id]?.cancel();
valueChangedSubscriptions.remove(c.id);
} 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.id] = 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,
)
],
),
),
);
}
}