From e0fb75037ae575bb0c63703299c2039bf970ad65 Mon Sep 17 00:00:00 2001 From: uvok Date: Wed, 30 Jul 2025 20:34:54 +0200 Subject: Shuffle code around / reorder --- lib/badge_app.dart | 50 ----------- lib/device_details.dart | 101 ----------------------- lib/device_scan_select.dart | 100 ---------------------- lib/main.dart | 2 +- lib/model/flutter_blue_plus_device.dart | 5 +- lib/scan_page.dart | 142 -------------------------------- lib/utility.dart | 18 ++++ lib/widgets/badge_app.dart | 50 +++++++++++ lib/widgets/device_details.dart | 101 +++++++++++++++++++++++ lib/widgets/device_scan_select.dart | 86 +++++++++++++++++++ lib/widgets/notifying_list_widget.dart | 27 ++++++ lib/widgets/scan_page.dart | 142 ++++++++++++++++++++++++++++++++ 12 files changed, 426 insertions(+), 398 deletions(-) delete mode 100644 lib/badge_app.dart delete mode 100644 lib/device_details.dart delete mode 100644 lib/device_scan_select.dart delete mode 100644 lib/scan_page.dart create mode 100644 lib/utility.dart create mode 100644 lib/widgets/badge_app.dart create mode 100644 lib/widgets/device_details.dart create mode 100644 lib/widgets/device_scan_select.dart create mode 100644 lib/widgets/notifying_list_widget.dart create mode 100644 lib/widgets/scan_page.dart (limited to 'lib') diff --git a/lib/badge_app.dart b/lib/badge_app.dart deleted file mode 100644 index 37fbf4c..0000000 --- a/lib/badge_app.dart +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2025, uvok cheetah -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -import 'package:uvok_epaper_badge/control/mock_scanner_controller.dart'; -import 'package:uvok_epaper_badge/scan_page.dart'; -import 'package:flutter/material.dart'; - -class BadgeApp extends StatelessWidget { - const BadgeApp({super.key}); - - @override - Widget build(BuildContext context) { - final selectedScanner = MockScannerController(); - - return MaterialApp( - title: 'Scanner', - theme: ThemeData( - // This is the theme of your application. - // - // TRY THIS: Try running your application with "flutter run". You'll see - // the application has a purple toolbar. Then, without quitting the app, - // try changing the seedColor in the colorScheme below to Colors.green - // and then invoke "hot reload" (save your changes or press the "hot - // reload" button in a Flutter-supported IDE, or press "r" if you used - // the command line to start the app). - // - // Notice that the counter didn't reset back to zero; the application - // state is not lost during the reload. To reset the state, use hot - // restart instead. - // - // This works for code too, not just values: Most code changes can be - // tested with just a hot reload. - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - ), - home: ScanPage(title: 'Badge Scanner', deviceScanner: selectedScanner), - ); - } -} diff --git a/lib/device_details.dart b/lib/device_details.dart deleted file mode 100644 index f3754d3..0000000 --- a/lib/device_details.dart +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (C) 2025, uvok cheetah -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -import 'package:uvok_epaper_badge/model/device.dart'; -import 'package:flutter/material.dart'; -import 'package:logger/logger.dart'; -import 'package:uvok_epaper_badge/model/device_connection.dart'; - -var logger = Logger(); - -class DeviceDetailsScreen extends StatefulWidget { - final Device device; - final DeviceConnection deviceConnection; - - const DeviceDetailsScreen({ - super.key, - required this.device, - required this.deviceConnection, - }); - - @override - State createState() { - return DeviceDetailsState(); - } -} - -class DeviceDetailsState extends State { - String connectStatus = ""; - - /// Whether the back button should be active. - bool backActive = false; - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - title: Text("Device details"), - ), - body: Center( - child: Column( - spacing: 20, - children: [ - Text(connectStatus), - ElevatedButton( - onPressed: backActive ? backClick : null, - child: Text("Back"), - ), - ], - ), - ), - ); - } - - @override - void initState() { - super.initState(); - _doConnect(); - } - - void backClick() { - Navigator.pop(context); - } - - @override - void deactivate() { - super.deactivate(); - logger.i("Closing state"); - // widget.device.disconnect().ignore(); - } - - void _doConnect() async { - final dev = widget.device; - - try { - logger.i("Try to connect..."); - await widget.deviceConnection.connect(); - } catch (e) { - logger.e(e); - await widget.deviceConnection.disconnect(); - connectStatus = e.toString(); - } finally { - backActive = true; - if (mounted) { - setState(() {}); - } - } - } -} diff --git a/lib/device_scan_select.dart b/lib/device_scan_select.dart deleted file mode 100644 index a11a79a..0000000 --- a/lib/device_scan_select.dart +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (C) 2025, uvok cheetah -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -import 'package:uvok_epaper_badge/model/device.dart'; -import 'package:flutter/material.dart'; - -abstract class NotifyingListWidget extends StatefulWidget { - final List items; - final ValueChanged onItemSelected; - - const NotifyingListWidget({ - super.key, - required this.items, - required this.onItemSelected, - }); -} - -class DeviceScanSelection extends NotifyingListWidget { - const DeviceScanSelection({ - super.key, - required super.items, - required super.onItemSelected, - }); - - @override - State createState() => _DeviceScanSelectionState(); -} - -class _DeviceScanSelectionState extends State { - int selectedResult = -1; - - @override - Widget build(BuildContext context) { - return Expanded( - child: ListView.separated( - itemCount: widget.items.length, - itemBuilder: (context, index) { - if (index >= widget.items.length) return null; - final Device result = widget.items[index]; - final String name = result.name ?? "???"; - - return ListTile( - title: Text(name), - subtitle: Text(result.address ?? "???"), - trailing: Text('RSSI: ${result.rssi}'), - selectedTileColor: Colors.amber, - selectedColor: Colors.black, - onTap: () { - setState(() { - selectedResult = index; - }); - widget.onItemSelected(result); - }, - selected: selectedResult == index, - ); - }, - separatorBuilder: (BuildContext context, int index) { - return Divider(); - }, - ), - ); - } - - @override - void didUpdateWidget(covariant DeviceScanSelection oldWidget) { - super.didUpdateWidget(oldWidget); - - if (!_deviceListEqual(oldWidget.items, widget.items)) { - setState(() { - selectedResult = -1; - }); - } - } - - bool _deviceListEqual(List oldList, List newList) { - if (oldList.length != newList.length) return false; - for (int i = 0; i < oldList.length; i++) { - if (oldList[i].address != newList[i].address) { - return false; - } - } - return true; - } -} - -String firstGiven(List list) { - return list.firstWhere((s) => s.isNotEmpty, orElse: () => ""); -} diff --git a/lib/main.dart b/lib/main.dart index 39f5e27..3e6c8cd 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -13,7 +13,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -import 'package:uvok_epaper_badge/badge_app.dart'; +import 'package:uvok_epaper_badge/widgets/badge_app.dart'; import 'package:flutter/material.dart'; import 'package:logger/logger.dart'; diff --git a/lib/model/flutter_blue_plus_device.dart b/lib/model/flutter_blue_plus_device.dart index 079d9fa..101c489 100644 --- a/lib/model/flutter_blue_plus_device.dart +++ b/lib/model/flutter_blue_plus_device.dart @@ -1,5 +1,6 @@ import 'package:uvok_epaper_badge/model/device.dart'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:uvok_epaper_badge/utility.dart'; class FlutterBluePlusDevice implements Device { final ScanResult scanResult; @@ -24,7 +25,3 @@ class FlutterBluePlusDevice implements Device { return bleDevice; } } - -String firstGiven(List list) { - return list.firstWhere((s) => s.isNotEmpty, orElse: () => ""); -} diff --git a/lib/scan_page.dart b/lib/scan_page.dart deleted file mode 100644 index 6256728..0000000 --- a/lib/scan_page.dart +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (C) 2025, uvok cheetah -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -import 'package:uvok_epaper_badge/control/scanner_controller.dart'; -import 'package:uvok_epaper_badge/device_details.dart'; -import 'package:uvok_epaper_badge/device_scan_select.dart'; -import 'package:uvok_epaper_badge/model/device.dart'; -import 'package:flutter/material.dart'; -import 'package:logger/logger.dart'; -import 'package:permission_handler/permission_handler.dart'; -import 'package:uvok_epaper_badge/model/device_connection.dart'; -import 'package:uvok_epaper_badge/model/device_connection_factory.dart'; - -var logger = Logger(); - -class ScanPage extends StatefulWidget { - const ScanPage({super.key, required this.title, required this.deviceScanner}); - - // Original doc: Fields in a Widget subclass are always marked "final". - - final String title; - final ScannerController deviceScanner; - - @override - State createState() => _ScanPageState(); -} - -class _ScanPageState extends State { - Device? selectedDevice; - - void _doConnect() async { - final Device? dev = selectedDevice; - if (dev == null) return; - - final DeviceConnection connection = - DeviceConnectionFactory.createConnection(dev); - //??? - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => - DeviceDetailsScreen(device: dev, deviceConnection: connection), - ), - ); - } - - void _doScan() async { - setState(() { - selectedDevice = null; - }); - - // ... - await widget.deviceScanner.startScan(); - } - - Future getPermissions() async { - try { - await Permission.bluetooth.request(); - } catch (e) { - logger.e(e.toString()); - } - } - - @override - void initState() { - super.initState(); - getPermissions(); - } - - @override - Widget build(BuildContext context) { - return StreamBuilder( - stream: widget.deviceScanner.statusStream, - initialData: ScanStatus.idle, - builder: (context, asyncSnapshot) { - bool isScanning = asyncSnapshot.data == ScanStatus.scanning; - return Scaffold( - appBar: AppBar( - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - title: Text(widget.title), - ), - body: Center( - child: Column( - // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" - // action in the IDE, or press "p" in the console), to see the - // wireframe for each widget. - mainAxisAlignment: MainAxisAlignment.center, - spacing: 24, - children: [ - SizedBox(height: 15), - Row( - mainAxisAlignment: MainAxisAlignment.center, - spacing: 15.0, - children: [ - ElevatedButton( - onPressed: isScanning ? null : _doScan, - child: isScanning - ? Text("Scanning...") - : Text("Start scan"), - ), - ElevatedButton( - onPressed: (selectedDevice == null || isScanning) - ? null - : _doConnect, - child: Text("Connect"), - ), - ], - ), - Expanded( - child: StreamBuilder( - stream: widget.deviceScanner.scanResultsStream, - initialData: [], - builder: (context, asyncSnapshot) { - return DeviceScanSelection( - items: asyncSnapshot.data ?? [], - onItemSelected: (item) { - setState(() => selectedDevice = item); - }, - ); - }, - ), - ), - ], - ), - ), - ); - }, - ); - } -} diff --git a/lib/utility.dart b/lib/utility.dart new file mode 100644 index 0000000..3be1f9b --- /dev/null +++ b/lib/utility.dart @@ -0,0 +1,18 @@ +// Copyright (C) 2025, uvok cheetah +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +String firstGiven(List list) { + return list.firstWhere((s) => s.isNotEmpty, orElse: () => ""); +} diff --git a/lib/widgets/badge_app.dart b/lib/widgets/badge_app.dart new file mode 100644 index 0000000..68cfce9 --- /dev/null +++ b/lib/widgets/badge_app.dart @@ -0,0 +1,50 @@ +// Copyright (C) 2025, uvok cheetah +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:uvok_epaper_badge/control/mock_scanner_controller.dart'; +import 'package:uvok_epaper_badge/widgets/scan_page.dart'; +import 'package:flutter/material.dart'; + +class BadgeApp extends StatelessWidget { + const BadgeApp({super.key}); + + @override + Widget build(BuildContext context) { + final selectedScanner = MockScannerController(); + + return MaterialApp( + title: 'Scanner', + theme: ThemeData( + // This is the theme of your application. + // + // TRY THIS: Try running your application with "flutter run". You'll see + // the application has a purple toolbar. Then, without quitting the app, + // try changing the seedColor in the colorScheme below to Colors.green + // and then invoke "hot reload" (save your changes or press the "hot + // reload" button in a Flutter-supported IDE, or press "r" if you used + // the command line to start the app). + // + // Notice that the counter didn't reset back to zero; the application + // state is not lost during the reload. To reset the state, use hot + // restart instead. + // + // This works for code too, not just values: Most code changes can be + // tested with just a hot reload. + colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + ), + home: ScanPage(title: 'Badge Scanner', deviceScanner: selectedScanner), + ); + } +} diff --git a/lib/widgets/device_details.dart b/lib/widgets/device_details.dart new file mode 100644 index 0000000..f3754d3 --- /dev/null +++ b/lib/widgets/device_details.dart @@ -0,0 +1,101 @@ +// Copyright (C) 2025, uvok cheetah +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:uvok_epaper_badge/model/device.dart'; +import 'package:flutter/material.dart'; +import 'package:logger/logger.dart'; +import 'package:uvok_epaper_badge/model/device_connection.dart'; + +var logger = Logger(); + +class DeviceDetailsScreen extends StatefulWidget { + final Device device; + final DeviceConnection deviceConnection; + + const DeviceDetailsScreen({ + super.key, + required this.device, + required this.deviceConnection, + }); + + @override + State createState() { + return DeviceDetailsState(); + } +} + +class DeviceDetailsState extends State { + String connectStatus = ""; + + /// Whether the back button should be active. + bool backActive = false; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + title: Text("Device details"), + ), + body: Center( + child: Column( + spacing: 20, + children: [ + Text(connectStatus), + ElevatedButton( + onPressed: backActive ? backClick : null, + child: Text("Back"), + ), + ], + ), + ), + ); + } + + @override + void initState() { + super.initState(); + _doConnect(); + } + + void backClick() { + Navigator.pop(context); + } + + @override + void deactivate() { + super.deactivate(); + logger.i("Closing state"); + // widget.device.disconnect().ignore(); + } + + void _doConnect() async { + final dev = widget.device; + + try { + logger.i("Try to connect..."); + await widget.deviceConnection.connect(); + } catch (e) { + logger.e(e); + await widget.deviceConnection.disconnect(); + connectStatus = e.toString(); + } finally { + backActive = true; + if (mounted) { + setState(() {}); + } + } + } +} diff --git a/lib/widgets/device_scan_select.dart b/lib/widgets/device_scan_select.dart new file mode 100644 index 0000000..c89d3cf --- /dev/null +++ b/lib/widgets/device_scan_select.dart @@ -0,0 +1,86 @@ +// Copyright (C) 2025, uvok cheetah +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:uvok_epaper_badge/model/device.dart'; +import 'package:flutter/material.dart'; +import 'package:uvok_epaper_badge/widgets/notifying_list_widget.dart'; + +class DeviceScanSelection extends NotifyingListWidget { + const DeviceScanSelection({ + super.key, + required super.items, + required super.onItemSelected, + }); + + @override + State createState() => _DeviceScanSelectionState(); +} + +class _DeviceScanSelectionState extends State { + int selectedResult = -1; + + @override + Widget build(BuildContext context) { + return Expanded( + child: ListView.separated( + itemCount: widget.items.length, + itemBuilder: (context, index) { + if (index >= widget.items.length) return null; + final Device result = widget.items[index]; + final String name = result.name ?? "???"; + + return ListTile( + title: Text(name), + subtitle: Text(result.address ?? "???"), + trailing: Text('RSSI: ${result.rssi}'), + selectedTileColor: Colors.amber, + selectedColor: Colors.black, + onTap: () { + setState(() { + selectedResult = index; + }); + widget.onItemSelected(result); + }, + selected: selectedResult == index, + ); + }, + separatorBuilder: (BuildContext context, int index) { + return Divider(); + }, + ), + ); + } + + @override + void didUpdateWidget(covariant DeviceScanSelection oldWidget) { + super.didUpdateWidget(oldWidget); + + if (!_deviceListEqual(oldWidget.items, widget.items)) { + setState(() { + selectedResult = -1; + }); + } + } + + bool _deviceListEqual(List oldList, List newList) { + if (oldList.length != newList.length) return false; + for (int i = 0; i < oldList.length; i++) { + if (oldList[i].address != newList[i].address) { + return false; + } + } + return true; + } +} diff --git a/lib/widgets/notifying_list_widget.dart b/lib/widgets/notifying_list_widget.dart new file mode 100644 index 0000000..7fdc2b1 --- /dev/null +++ b/lib/widgets/notifying_list_widget.dart @@ -0,0 +1,27 @@ +// Copyright (C) 2025, uvok cheetah +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:flutter/material.dart'; + +abstract class NotifyingListWidget extends StatefulWidget { + final List items; + final ValueChanged onItemSelected; + + const NotifyingListWidget({ + super.key, + required this.items, + required this.onItemSelected, + }); +} diff --git a/lib/widgets/scan_page.dart b/lib/widgets/scan_page.dart new file mode 100644 index 0000000..c557657 --- /dev/null +++ b/lib/widgets/scan_page.dart @@ -0,0 +1,142 @@ +// Copyright (C) 2025, uvok cheetah +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:uvok_epaper_badge/control/scanner_controller.dart'; +import 'package:uvok_epaper_badge/widgets/device_details.dart'; +import 'package:uvok_epaper_badge/widgets/device_scan_select.dart'; +import 'package:uvok_epaper_badge/model/device.dart'; +import 'package:flutter/material.dart'; +import 'package:logger/logger.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:uvok_epaper_badge/model/device_connection.dart'; +import 'package:uvok_epaper_badge/model/device_connection_factory.dart'; + +var logger = Logger(); + +class ScanPage extends StatefulWidget { + const ScanPage({super.key, required this.title, required this.deviceScanner}); + + // Original doc: Fields in a Widget subclass are always marked "final". + + final String title; + final ScannerController deviceScanner; + + @override + State createState() => _ScanPageState(); +} + +class _ScanPageState extends State { + Device? selectedDevice; + + void _doConnect() async { + final Device? dev = selectedDevice; + if (dev == null) return; + + final DeviceConnection connection = + DeviceConnectionFactory.createConnection(dev); + //??? + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + DeviceDetailsScreen(device: dev, deviceConnection: connection), + ), + ); + } + + void _doScan() async { + setState(() { + selectedDevice = null; + }); + + // ... + await widget.deviceScanner.startScan(); + } + + Future getPermissions() async { + try { + await Permission.bluetooth.request(); + } catch (e) { + logger.e(e.toString()); + } + } + + @override + void initState() { + super.initState(); + getPermissions(); + } + + @override + Widget build(BuildContext context) { + return StreamBuilder( + stream: widget.deviceScanner.statusStream, + initialData: ScanStatus.idle, + builder: (context, asyncSnapshot) { + bool isScanning = asyncSnapshot.data == ScanStatus.scanning; + return Scaffold( + appBar: AppBar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + title: Text(widget.title), + ), + body: Center( + child: Column( + // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" + // action in the IDE, or press "p" in the console), to see the + // wireframe for each widget. + mainAxisAlignment: MainAxisAlignment.center, + spacing: 24, + children: [ + SizedBox(height: 15), + Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 15.0, + children: [ + ElevatedButton( + onPressed: isScanning ? null : _doScan, + child: isScanning + ? Text("Scanning...") + : Text("Start scan"), + ), + ElevatedButton( + onPressed: (selectedDevice == null || isScanning) + ? null + : _doConnect, + child: Text("Connect"), + ), + ], + ), + Expanded( + child: StreamBuilder( + stream: widget.deviceScanner.scanResultsStream, + initialData: [], + builder: (context, asyncSnapshot) { + return DeviceScanSelection( + items: asyncSnapshot.data ?? [], + onItemSelected: (item) { + setState(() => selectedDevice = item); + }, + ); + }, + ), + ), + ], + ), + ), + ); + }, + ); + } +} -- cgit v1.2.3