Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ___________________________
- //main.dart
- ___________________________
- // 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 'dart:math';
- import 'package:flutter/material.dart';
- import 'package:flutter_blue/flutter_blue.dart';
- import 'package:flutter_blue_example/widgets.dart';
- void main() {
- runApp(FlutterBlueApp());
- }
- class FlutterBlueApp extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return MaterialApp(
- color: Colors.lightBlue,
- home: StreamBuilder<BluetoothState>(
- stream: FlutterBlue.instance.state,
- initialData: BluetoothState.unknown,
- builder: (c, snapshot) {
- final state = snapshot.data;
- if (state == BluetoothState.on) {
- return FindDevicesScreen();
- }
- return BluetoothOffScreen(state: state);
- }),
- );
- }
- }
- class BluetoothOffScreen extends StatelessWidget {
- const BluetoothOffScreen({Key? key, this.state}) : super(key: key);
- final BluetoothState? state;
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- backgroundColor: Colors.lightBlue,
- body: Center(
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: <Widget>[
- Icon(
- Icons.bluetooth_disabled,
- size: 200.0,
- color: Colors.white54,
- ),
- Text(
- 'Bluetooth Adapter is ${state != null ? state.toString().substring(15) : 'not available'}.',
- style: Theme.of(context).primaryTextTheme.subtitle1?.copyWith(color: Colors.white),
- ),
- ],
- ),
- ),
- );
- }
- }
- class FindDevicesScreen extends StatelessWidget {
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text('Find Devices'),
- ),
- body: RefreshIndicator(
- onRefresh: () =>
- FlutterBlue.instance.startScan(timeout: Duration(seconds: 4)),
- child: SingleChildScrollView(
- child: Column(
- children: <Widget>[
- StreamBuilder<List<BluetoothDevice>>(
- stream: Stream.periodic(Duration(seconds: 2))
- .asyncMap((_) => FlutterBlue.instance.connectedDevices),
- initialData: [],
- builder: (c, snapshot) => Column(
- children: snapshot.data!
- .map((d) => ListTile(
- title: Text(d.name),
- subtitle: Text(d.id.toString()),
- trailing: StreamBuilder<BluetoothDeviceState>(
- stream: d.state,
- initialData: BluetoothDeviceState.disconnected,
- builder: (c, snapshot) {
- if (snapshot.data ==
- BluetoothDeviceState.connected) {
- return ElevatedButton(
- child: Text('OPEN'),
- onPressed: () => Navigator.of(context).push(
- MaterialPageRoute(
- builder: (context) =>
- DeviceScreen(device: d))),
- );
- }
- return Text(snapshot.data.toString());
- },
- ),
- ))
- .toList(),
- ),
- ),
- StreamBuilder<List<ScanResult>>(
- stream: FlutterBlue.instance.scanResults,
- initialData: [],
- builder: (c, snapshot) => Column(
- children: snapshot.data!
- .map(
- (r) => ScanResultTile(
- result: r,
- onTap: () => Navigator.of(context)
- .push(MaterialPageRoute(builder: (context) {
- r.device.connect();
- return DeviceScreen(device: r.device);
- })),
- ),
- )
- .toList(),
- ),
- ),
- ],
- ),
- ),
- ),
- floatingActionButton: StreamBuilder<bool>(
- stream: FlutterBlue.instance.isScanning,
- initialData: false,
- builder: (c, snapshot) {
- if (snapshot.data!) {
- return FloatingActionButton(
- child: Icon(Icons.stop),
- onPressed: () => FlutterBlue.instance.stopScan(),
- backgroundColor: Colors.red,
- );
- } else {
- return FloatingActionButton(
- child: Icon(Icons.search),
- onPressed: () => FlutterBlue.instance
- .startScan(timeout: Duration(seconds: 4)));
- }
- },
- ),
- );
- }
- }
- class DeviceScreen extends StatelessWidget {
- const DeviceScreen({Key? key, required this.device}) : super(key: key);
- final BluetoothDevice device;
- List<int> _getRandomBytes() {
- final math = Random();
- return [
- math.nextInt(255),
- math.nextInt(255),
- math.nextInt(255),
- math.nextInt(255)
- ];
- }
- List<Widget> _buildServiceTiles(List<BluetoothService> services) {
- return services
- .map(
- (s) => ServiceTile(
- service: s,
- characteristicTiles: s.characteristics
- .map(
- (c) => CharacteristicTile(
- characteristic: c,
- onReadPressed: () => c.read(),
- onWritePressed: () async {
- await c.write(_getRandomBytes(), withoutResponse: true);
- await c.read();
- },
- onNotificationPressed: () async {
- await c.setNotifyValue(!c.isNotifying);
- await c.read();
- },
- descriptorTiles: c.descriptors
- .map(
- (d) => DescriptorTile(
- descriptor: d,
- onReadPressed: () => d.read(),
- onWritePressed: () => d.write(_getRandomBytes()),
- ),
- )
- .toList(),
- ),
- )
- .toList(),
- ),
- )
- .toList();
- }
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: Text(device.name),
- actions: <Widget>[
- StreamBuilder<BluetoothDeviceState>(
- stream: device.state,
- initialData: BluetoothDeviceState.connecting,
- builder: (c, snapshot) {
- VoidCallback? onPressed;
- String text;
- switch (snapshot.data) {
- case BluetoothDeviceState.connected:
- onPressed = () => device.disconnect();
- text = 'DISCONNECT';
- break;
- case BluetoothDeviceState.disconnected:
- onPressed = () => device.connect();
- text = 'CONNECT';
- break;
- default:
- onPressed = null;
- text = snapshot.data.toString().substring(21).toUpperCase();
- break;
- }
- return TextButton(
- onPressed: onPressed,
- child: Text(
- text,
- style: Theme.of(context)
- .primaryTextTheme
- .button
- ?.copyWith(color: Colors.white),
- ));
- },
- )
- ],
- ),
- body: SingleChildScrollView(
- child: Column(
- children: <Widget>[
- StreamBuilder<BluetoothDeviceState>(
- stream: device.state,
- initialData: BluetoothDeviceState.connecting,
- builder: (c, snapshot) => ListTile(
- leading: (snapshot.data == BluetoothDeviceState.connected)
- ? Icon(Icons.bluetooth_connected)
- : Icon(Icons.bluetooth_disabled),
- title: Text(
- 'Device is ${snapshot.data.toString().split('.')[1]}.'),
- subtitle: Text('${device.id}'),
- trailing: StreamBuilder<bool>(
- stream: device.isDiscoveringServices,
- initialData: false,
- builder: (c, snapshot) => IndexedStack(
- index: snapshot.data! ? 1 : 0,
- children: <Widget>[
- IconButton(
- icon: Icon(Icons.refresh),
- onPressed: () => device.discoverServices(),
- ),
- IconButton(
- icon: SizedBox(
- child: CircularProgressIndicator(
- valueColor: AlwaysStoppedAnimation(Colors.grey),
- ),
- width: 18.0,
- height: 18.0,
- ),
- onPressed: null,
- )
- ],
- ),
- ),
- ),
- ),
- StreamBuilder<int>(
- stream: device.mtu,
- initialData: 0,
- builder: (c, snapshot) => ListTile(
- title: Text('MTU Size'),
- subtitle: Text('${snapshot.data} bytes'),
- trailing: IconButton(
- icon: Icon(Icons.edit),
- onPressed: () => device.requestMtu(223),
- ),
- ),
- ),
- StreamBuilder<List<BluetoothService>>(
- stream: device.services,
- initialData: [],
- builder: (c, snapshot) {
- return Column(
- children: _buildServiceTiles(snapshot.data!),
- );
- },
- ),
- ],
- ),
- ),
- );
- }
- }
- ___________________________
- //widgets.dart
- ___________________________
- // 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 'package:flutter/material.dart';
- import 'package:flutter_blue/flutter_blue.dart';
- class ScanResultTile extends StatelessWidget {
- const ScanResultTile({Key? key, required this.result, this.onTap})
- : super(key: key);
- final ScanResult result;
- final VoidCallback? onTap;
- Widget _buildTitle(BuildContext context) {
- if (result.device.name.length > 0) {
- return Column(
- mainAxisAlignment: MainAxisAlignment.start,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: <Widget>[
- Text(
- result.device.name,
- overflow: TextOverflow.ellipsis,
- ),
- Text(
- result.device.id.toString(),
- style: Theme.of(context).textTheme.caption,
- )
- ],
- );
- } else {
- return Text(result.device.id.toString());
- }
- }
- Widget _buildAdvRow(BuildContext context, String title, String value) {
- return Padding(
- padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: <Widget>[
- Text(title, style: Theme.of(context).textTheme.caption),
- SizedBox(
- width: 12.0,
- ),
- Expanded(
- child: Text(
- value,
- style: Theme.of(context)
- .textTheme
- .caption
- ?.apply(color: Colors.black),
- softWrap: true,
- ),
- ),
- ],
- ),
- );
- }
- String getNiceHexArray(List<int> bytes) {
- return '[${bytes.map((i) => i.toRadixString(16).padLeft(2, '0')).join(', ')}]'
- .toUpperCase();
- }
- String getNiceManufacturerData(Map<int, List<int>> data) {
- if (data.isEmpty) {
- return 'N/A';
- }
- List<String> res = [];
- data.forEach((id, bytes) {
- res.add(
- '${id.toRadixString(16).toUpperCase()}: ${getNiceHexArray(bytes)}');
- });
- return res.join(', ');
- }
- String getNiceServiceData(Map<String, List<int>> data) {
- if (data.isEmpty) {
- return 'N/A';
- }
- List<String> res = [];
- data.forEach((id, bytes) {
- res.add('${id.toUpperCase()}: ${getNiceHexArray(bytes)}');
- });
- return res.join(', ');
- }
- @override
- Widget build(BuildContext context) {
- return ExpansionTile(
- title: _buildTitle(context),
- leading: Text(result.rssi.toString()),
- trailing: ElevatedButton(
- child: Text('CONNECT'),
- style: ElevatedButton.styleFrom(primary: Colors.black),
- onPressed: (result.advertisementData.connectable) ? onTap : null,
- ),
- children: <Widget>[
- _buildAdvRow(
- context, 'Complete Local Name', result.advertisementData.localName),
- _buildAdvRow(context, 'Tx Power Level',
- '${result.advertisementData.txPowerLevel ?? 'N/A'}'),
- _buildAdvRow(context, 'Manufacturer Data',
- getNiceManufacturerData(result.advertisementData.manufacturerData)),
- _buildAdvRow(
- context,
- 'Service UUIDs',
- (result.advertisementData.serviceUuids.isNotEmpty)
- ? result.advertisementData.serviceUuids.join(', ').toUpperCase()
- : 'N/A'),
- _buildAdvRow(context, 'Service Data',
- getNiceServiceData(result.advertisementData.serviceData)),
- ],
- );
- }
- }
- class ServiceTile extends StatelessWidget {
- final BluetoothService service;
- final List<CharacteristicTile> characteristicTiles;
- const ServiceTile(
- {Key? key, required this.service, required this.characteristicTiles})
- : super(key: key);
- @override
- Widget build(BuildContext context) {
- if (characteristicTiles.length > 0) {
- return ExpansionTile(
- title: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: <Widget>[
- Text('Service'),
- Text('0x${service.uuid.toString().toUpperCase().substring(4, 8)}',
- style: Theme.of(context).textTheme.bodyText1?.copyWith(
- color: Theme.of(context).textTheme.caption?.color))
- ],
- ),
- children: characteristicTiles,
- );
- } else {
- return ListTile(
- title: Text('Service'),
- subtitle:
- Text('0x${service.uuid.toString().toUpperCase().substring(4, 8)}'),
- );
- }
- }
- }
- class CharacteristicTile extends StatelessWidget {
- final BluetoothCharacteristic characteristic;
- final List<DescriptorTile> descriptorTiles;
- final VoidCallback? onReadPressed;
- final VoidCallback? onWritePressed;
- final VoidCallback? onNotificationPressed;
- const CharacteristicTile(
- {Key? key,
- required this.characteristic,
- required this.descriptorTiles,
- this.onReadPressed,
- this.onWritePressed,
- this.onNotificationPressed})
- : super(key: key);
- @override
- Widget build(BuildContext context) {
- return StreamBuilder<List<int>>(
- stream: characteristic.value,
- initialData: characteristic.lastValue,
- builder: (c, snapshot) {
- final value = snapshot.data;
- return ExpansionTile(
- title: ListTile(
- title: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: <Widget>[
- Text('Characteristic'),
- Text(
- '0x${characteristic.uuid.toString().toUpperCase().substring(4, 8)}',
- style: Theme.of(context).textTheme.bodyText1?.copyWith(
- color: Theme.of(context).textTheme.caption?.color))
- ],
- ),
- subtitle: Text(value.toString()),
- contentPadding: EdgeInsets.all(0.0),
- ),
- trailing: Row(
- mainAxisSize: MainAxisSize.min,
- children: <Widget>[
- IconButton(
- icon: Icon(
- Icons.file_download,
- color: Theme.of(context).iconTheme.color?.withOpacity(0.5),
- ),
- onPressed: onReadPressed,
- ),
- IconButton(
- icon: Icon(Icons.file_upload,
- color: Theme.of(context).iconTheme.color?.withOpacity(0.5)),
- onPressed: onWritePressed,
- ),
- IconButton(
- icon: Icon(
- characteristic.isNotifying
- ? Icons.sync_disabled
- : Icons.sync,
- color: Theme.of(context).iconTheme.color?.withOpacity(0.5)),
- onPressed: onNotificationPressed,
- )
- ],
- ),
- children: descriptorTiles,
- );
- },
- );
- }
- }
- class DescriptorTile extends StatelessWidget {
- final BluetoothDescriptor descriptor;
- final VoidCallback? onReadPressed;
- final VoidCallback? onWritePressed;
- const DescriptorTile(
- {Key? key,
- required this.descriptor,
- this.onReadPressed,
- this.onWritePressed})
- : super(key: key);
- @override
- Widget build(BuildContext context) {
- return ListTile(
- title: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: <Widget>[
- Text('Descriptor'),
- Text('0x${descriptor.uuid.toString().toUpperCase().substring(4, 8)}',
- style: Theme.of(context)
- .textTheme
- .bodyText1
- ?.copyWith(color: Theme.of(context).textTheme.caption?.color))
- ],
- ),
- subtitle: StreamBuilder<List<int>>(
- stream: descriptor.value,
- initialData: descriptor.lastValue,
- builder: (c, snapshot) => Text(snapshot.data.toString()),
- ),
- trailing: Row(
- mainAxisSize: MainAxisSize.min,
- children: <Widget>[
- IconButton(
- icon: Icon(
- Icons.file_download,
- color: Theme.of(context).iconTheme.color?.withOpacity(0.5),
- ),
- onPressed: onReadPressed,
- ),
- IconButton(
- icon: Icon(
- Icons.file_upload,
- color: Theme.of(context).iconTheme.color?.withOpacity(0.5),
- ),
- onPressed: onWritePressed,
- )
- ],
- ),
- );
- }
- }
- class AdapterStateTile extends StatelessWidget {
- const AdapterStateTile({Key? key, required this.state}) : super(key: key);
- final BluetoothState state;
- @override
- Widget build(BuildContext context) {
- return Container(
- color: Colors.redAccent,
- child: ListTile(
- title: Text(
- 'Bluetooth adapter is ${state.toString().substring(15)}',
- style: Theme.of(context).primaryTextTheme.subtitle1,
- ),
- trailing: Icon(
- Icons.error,
- color: Theme.of(context).primaryTextTheme.subtitle1?.color,
- ),
- ),
- );
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement