123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871 |
- import 'dart:async';
- import 'dart:io';
- import 'dart:math';
- import 'package:android_intent/android_intent.dart';
- import 'package:flutter/cupertino.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter_blue/flutter_blue.dart';
- import 'package:provider/provider.dart';
- import 'package:sport/pages/my/device_info_page.dart';
- import 'package:sport/provider/bluetooth.dart';
- import 'package:sport/widgets/button_cancel.dart';
- import 'package:sport/widgets/button_primary.dart';
- import 'package:sport/widgets/image.dart';
- import 'package:sport/widgets/misc.dart';
- import 'package:url_launcher/url_launcher.dart';
- class SearchDeviceDialog extends StatefulWidget {
- @override
- State<StatefulWidget> createState() => _SearchDeviceDialog();
- }
- class _SearchDeviceDialog extends State<SearchDeviceDialog> {
- @override
- void dispose() {
- super.dispose();
- FlutterBlue.instance.stopScan();
- }
- @override
- Widget build(BuildContext context) {
- return Dialog(
- elevation: 0,
- shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
- child: Padding(
- padding: const EdgeInsets.all(8.0),
- child: ConstrainedBox(
- constraints: BoxConstraints(maxHeight: 350, minWidth: double.infinity),
- child: Column(
- children: <Widget>[
- Stack(
- alignment: Alignment.center,
- children: <Widget>[
- Center(
- child: Padding(
- padding: const EdgeInsets.all(6.0),
- child: Text("请选择鞋子", style: Theme.of(context).textTheme.headline3),
- ),
- ),
- Positioned(
- right: 0,
- top: 0,
- child: GestureDetector(
- behavior: HitTestBehavior.opaque,
- onTap: () => Navigator.pop(context),
- child: Padding(
- padding: const EdgeInsets.all(6.0),
- child: Image.asset("lib/assets/img/btn_close_big.png"),
- ),
- ),
- ),
- ],
- ),
- Divider(),
- Expanded(
- child: 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 Column(
- mainAxisSize: MainAxisSize.min,
- children: <Widget>[
- SizedBox(
- height: 60,
- ),
- Image.asset("lib/assets/img/pop_image_close.png"),
- SizedBox(
- height: 20,
- ),
- Text(
- '搜索失败,请确认蓝牙是否打开',
- style: Theme.of(context).textTheme.bodyText2.copyWith(color: Color(0xff666666)),
- ),
- SizedBox(
- height: 10,
- ),
- GestureDetector(
- behavior: HitTestBehavior.opaque,
- onTap: () async {
- if (Platform.isAndroid) {
- AndroidIntent intent = AndroidIntent(action: "android.bluetooth.adapter.action.REQUEST_ENABLE");
- await intent.launch();
- } else if (Platform.isIOS) {
- launch("App-Prefs:root=Bluetooth ");
- }
- },
- child: Center(
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: <Widget>[
- Text(
- '打开蓝牙设置',
- style: Theme.of(context).textTheme.bodyText2.copyWith(color: Theme.of(context).accentColor),
- ),
- SizedBox(
- width: 6,
- ),
- arrowRight6()
- ],
- ),
- ),
- ),
- ],
- );
- }
- }
- class FindDevicesScreen extends StatefulWidget {
- @override
- State<StatefulWidget> createState() => _FindDevicesScreen();
- }
- class _FindDevicesScreen extends State<FindDevicesScreen> with SingleTickerProviderStateMixin {
- AnimationController _animationController;
- Animation _animation;
- bool _search = true;
- StreamSubscription streamSubscription;
- @override
- void initState() {
- _animationController = AnimationController(duration: Duration(seconds: 2), vsync: this);
- _animation = Tween(begin: .0, end: 1.0).animate(_animationController)
- ..addStatusListener((status) {
- if (status == AnimationStatus.completed) {
- _animationController.repeat();
- }
- });
- //开始动画
- _animationController.forward();
- super.initState();
- startScan();
- streamSubscription = Provider.of<Bluetooth>(context, listen: false).stateStream.listen((event) async {
- await Future.delayed(Duration(seconds: 2));
- Navigator.pop(context);
- });
- }
- startScan() async {
- setState(() {
- _search = true;
- });
- FlutterBlue.instance.startScan(timeout: Duration(seconds: 10)).whenComplete(() {
- if (mounted)
- setState(() {
- _search = false;
- });
- });
- }
- @override
- void dispose() {
- _animationController?.dispose();
- super.dispose();
- streamSubscription?.cancel();
- }
- @override
- Widget build(BuildContext context) {
- return Column(
- children: <Widget>[
- Expanded(
- child: SingleChildScrollView(
- child: Column(
- children: <Widget>[
- // if (!_search)
- // StreamBuilder<List<BluetoothDevice>>(
- // stream: Stream.fromFuture(FlutterBlue.instance.connectedDevices),
- // initialData: [],
- // builder: (c, snapshot) => Column(
- // children: snapshot.data
- // .map((d) => Column(
- // children: <Widget>[
- // ListTile(
- // title: Column(
- // mainAxisAlignment: MainAxisAlignment.start,
- // crossAxisAlignment: CrossAxisAlignment.start,
- // children: <Widget>[
- // Text(
- // d.name,
- // overflow: TextOverflow.ellipsis,
- // style: Theme.of(context).textTheme.headline3,
- // ),
- // SizedBox(
- // height: 4,
- // ),
- // Text(
- // d.id.toString(),
- // style: Theme.of(context).textTheme.caption,
- // ),
- // ],
- // ),
- // trailing: StreamBuilder<BluetoothDeviceState>(
- // stream: d.state,
- // initialData: BluetoothDeviceState.disconnected,
- // builder: (c, snapshot) {
- // return GestureDetector(
- // onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => DeviceInfoPage())),
- // child: Container(
- // width: 58,
- // height: 30,
- // alignment: Alignment.center,
- // decoration: BoxDecoration(
- // shape: BoxShape.rectangle,
- // borderRadius: BorderRadius.all(Radius.circular(100)),
- // color: Theme.of(context).accentColor),
- // child: Text(
- // snapshot.data == BluetoothDeviceState.connected ? "已连接" : "未连接",
- // style: TextStyle(color: Colors.white, fontSize: 12),
- // ),
- // ),
- // );
- // },
- // ),
- // ),
- // Divider()
- // ],
- // ))
- // .toList(),
- // ),
- // ),
- // if (!_search)
- StreamBuilder<List<ScanResult>>(
- stream: FlutterBlue.instance.scanResults,
- initialData: [],
- builder: (c, snapshot) {
- var bluetooth = Provider.of<Bluetooth>(context, listen: false);
- var list = snapshot.data.where((element) => element.device.name.isNotEmpty).toList();
- list.sort((a, b) => bluetooth.device == a.device ? -1 : 1);
- return Column(
- children: list.isEmpty == true
- ? [
- _search
- ? Padding(
- padding: const EdgeInsets.symmetric(vertical: 30),
- child: Column(
- children: <Widget>[
- Stack(
- alignment: Alignment.center,
- children: <Widget>[
- RotationTransition(turns: _animation, child: Image.asset("lib/assets/img/pop_image_circle.png")),
- Image.asset("lib/assets/img/pop_image_shoes.png")
- ],
- ),
- SizedBox(
- height: 12,
- ),
- Text("搜索中...")
- ],
- ),
- )
- : Padding(
- padding: const EdgeInsets.all(30.0),
- child: Center(
- child: Column(
- children: <Widget>[
- Image.asset("lib/assets/img/pop_image_noequipment.png"),
- SizedBox(
- height: 10,
- ),
- Text("暂无设备")
- ],
- ),
- ))
- ]
- : list
- .map(
- (r) => Column(
- children: <Widget>[
- ScanResultTile(
- result: r,
- onTap: () async {
- await Provider.of<Bluetooth>(context, listen: false).saveDevice(r.device);
- setState(() {});
- },
- ),
- Divider()
- ],
- ),
- )
- .toList(),
- );
- },
- ),
- ],
- ),
- ),
- ),
- Padding(
- padding: const EdgeInsets.all(8.0),
- child: StreamBuilder<bool>(
- stream: FlutterBlue.instance.isScanning,
- initialData: false,
- builder: (c, snapshot) {
- if (snapshot.data) {
- return CancelButton(
- height: 35,
- content: "搜索中...",
- callback: () {
- FlutterBlue.instance.stopScan();
- setState(() {
- _search = false;
- });
- },
- );
- } else {
- return PrimaryButton(height: 35, content: "重新搜索", callback: () => startScan());
- }
- },
- ),
- ),
- ],
- );
- }
- }
- class DeviceScreen extends StatelessWidget {
- const DeviceScreen({Key key, 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);
- c.value.listen((value) {
- print(value.length);
- });
- },
- 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 FlatButton(
- 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),
- );
- },
- ),
- RaisedButton(
- onPressed: () {
- Provider.of<Bluetooth>(context, listen: false).queryDeviceStep();
- },
- child: Text("同步步数"),
- ),
- RaisedButton(
- onPressed: () {
- Provider.of<Bluetooth>(context, listen: false).resetData();
- },
- child: Text("清零"),
- ),
- RaisedButton(
- onPressed: () {
- Provider.of<Bluetooth>(context, listen: false).setupGameMode(true);
- },
- child: Text("游戏模式开"),
- ),
- RaisedButton(
- onPressed: () {
- Provider.of<Bluetooth>(context, listen: false).setupGameMode(false);
- },
- child: Text("游戏模式关"),
- ),
- RaisedButton(
- onPressed: () {
- Provider.of<Bluetooth>(context, listen: false).vibrate(Provider.of<Bluetooth>(context, listen: false).vibrateNotifier.value);
- },
- child: Text("发送震动"),
- ),
- ValueListenableBuilder(
- valueListenable: Provider.of<Bluetooth>(context, listen: false).vibrateNotifier,
- builder: (BuildContext context, int v, Widget child) => Row(
- children: <Widget>[
- Slider(
- divisions: 9,
- value: v.toDouble(),
- min: 100,
- max: 1000,
- onChanged: (double value) {
- Provider.of<Bluetooth>(context, listen: false).vibrateNotifier.value = value.toInt();
- },
- ),
- Text("$v")
- ],
- ),
- ),
- ValueListenableBuilder(
- valueListenable: Provider.of<Bluetooth>(context, listen: false).actionNotifier,
- builder: (BuildContext context, int value, Widget child) => Text("当前动作: $value"),
- ),
- ValueListenableBuilder(
- valueListenable: Provider.of<Bluetooth>(context, listen: false).stepTotalNotifier,
- builder: (BuildContext context, int value, Widget child) => Text("同步步数: $value"),
- ),
- ValueListenableBuilder(
- valueListenable: Provider.of<Bluetooth>(context, listen: false).stepNotifier,
- builder: (BuildContext context, int value, Widget child) => Text("相对步数: $value"),
- ),
- ValueListenableBuilder(
- valueListenable: Provider.of<Bluetooth>(context, listen: false).byteNotifier,
- builder: (BuildContext context, List<int> value, Widget child) => Text("接收: $value"),
- ),
- ],
- ),
- ),
- );
- }
- }
- class ScanResultTile extends StatelessWidget {
- const ScanResultTile({Key key, 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,
- style: Theme.of(context).textTheme.headline3,
- ),
- SizedBox(
- height: 4,
- ),
- 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 null;
- }
- 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 null;
- }
- List<String> res = [];
- data.forEach((id, bytes) {
- res.add('${id.toUpperCase()}: ${getNiceHexArray(bytes)}');
- });
- return res.join(', ');
- }
- @override
- Widget build(BuildContext context) {
- return ListTile(
- contentPadding: EdgeInsets.symmetric(horizontal: 12),
- title: _buildTitle(context),
- // leading: Text(result.rssi.toString()),
- trailing: StreamBuilder<BluetoothDeviceState>(
- stream: result.device.state,
- initialData: BluetoothDeviceState.connecting,
- builder: (c, snapshot) {
- if (snapshot.data == BluetoothDeviceState.connected) {
- return Container(
- width: 64,
- height: 30,
- alignment: Alignment.center,
- decoration: BoxDecoration(shape: BoxShape.rectangle, borderRadius: BorderRadius.all(Radius.circular(100)), color: Theme.of(context).accentColor),
- child: Text(
- snapshot.data == BluetoothDeviceState.connected ? "已连接" : "未连接",
- style: TextStyle(color: Colors.white, fontSize: 12),
- ),
- );
- }
- int status = Provider.of<Bluetooth>(context, listen: false).device == result.device
- ? 1
- : snapshot.data == BluetoothDeviceState.disconnected
- ? 0
- : snapshot.data == BluetoothDeviceState.connecting ? 1 : snapshot.data == BluetoothDeviceState.connected ? 2 : 3;
- return status == 1
- ? Padding(
- padding: const EdgeInsets.symmetric(horizontal: 4.0),
- child: SizedBox(
- width: 12,
- height: 12,
- child: CircularProgressIndicator(
- strokeWidth: 2,
- ),
- ),
- )
- : GestureDetector(
- onTap: onTap,
- child: Container(
- width: 64,
- height: 30,
- alignment: Alignment.center,
- decoration: BoxDecoration(
- borderRadius: BorderRadius.all(Radius.circular(100)),
- border: Border.all(
- color: Theme.of(context).accentColor,
- )),
- child: Center(
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: <Widget>[
- Text(
- status == 1 ? "连接中" : status == 0 ? "连接" : "已连接",
- style: TextStyle(
- color: Theme.of(context).accentColor,
- fontSize: 12,
- ),
- strutStyle: fixedLine,
- )
- ],
- ),
- ),
- ),
- );
- },
- ),
- // 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) ?? 'N/A'),
- // _buildAdvRow(context, 'Service UUIDs',
- // (result.advertisementData.serviceUuids.isNotEmpty) ? result.advertisementData.serviceUuids.join(', ').toUpperCase() : 'N/A'),
- // _buildAdvRow(context, 'Service Data', getNiceServiceData(result.advertisementData.serviceData) ?? 'N/A'),
- // ],
- );
- }
- }
- class ServiceTile extends StatelessWidget {
- final BluetoothService service;
- final List<CharacteristicTile> characteristicTiles;
- const ServiceTile({Key key, this.service, 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.body1.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, this.characteristic, 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.body1.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, 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.body1.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.subhead,
- ),
- trailing: Icon(
- Icons.error,
- color: Theme.of(context).primaryTextTheme.subhead.color,
- ),
- ),
- );
- }
- }
|