diff options
| author | uvok | 2025-07-29 16:30:42 +0200 | 
|---|---|---|
| committer | uvok | 2025-07-29 16:30:42 +0200 | 
| commit | 0e92f3c889f7a9f8832aa706aa9c3bfce1bb7891 (patch) | |
| tree | 2545a15022c42ac9adc9e34e691bc9c1caf13b85 | |
| parent | 8dc4939e2eb055e4ba1463f931c9c69284687973 (diff) | |
Reorder classes
| -rw-r--r-- | lib/badge_app.dart | 32 | ||||
| -rw-r--r-- | lib/device_details.dart | 9 | ||||
| -rw-r--r-- | lib/main.dart | 239 | ||||
| -rw-r--r-- | lib/scan_page.dart | 173 | ||||
| -rw-r--r-- | test/widget_test.dart | 5 | 
5 files changed, 216 insertions, 242 deletions
| diff --git a/lib/badge_app.dart b/lib/badge_app.dart new file mode 100644 index 0000000..edc1897 --- /dev/null +++ b/lib/badge_app.dart @@ -0,0 +1,32 @@ +import 'package:badge/scan_page.dart'; +import 'package:flutter/material.dart'; + +class BadgeApp extends StatelessWidget { +  const BadgeApp({super.key}); + +  @override +  Widget build(BuildContext context) { +    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: const ScanPage(title: 'Badge Scanner'), +    ); +  } +} diff --git a/lib/device_details.dart b/lib/device_details.dart index 4f96ed8..d677b29 100644 --- a/lib/device_details.dart +++ b/lib/device_details.dart @@ -19,15 +19,20 @@ class DeviceDetailsScreen extends StatefulWidget {  class DeviceDetailsState extends State<DeviceDetailsScreen> {    String connectStatus = "<Status>"; -  // ??? +  // Just to have a resonable default subscription?    StreamSubscription<BluetoothConnectionState> subs =        Stream<BluetoothConnectionState>.empty().listen((e) => ()); + +  /// Whether the back button should be active.    bool backActive = false;    @override    Widget build(BuildContext context) {      return Scaffold( -      appBar: AppBar(title: Text("Device details")), +      appBar: AppBar( +        backgroundColor: Theme.of(context).colorScheme.inversePrimary, +        title: Text("Device details"), +      ),        body: Center(          child: Column(            spacing: 20, diff --git a/lib/main.dart b/lib/main.dart index 6310660..64aab35 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,245 +1,10 @@ -import 'dart:io' show Platform; - -import 'package:badge/device_details.dart'; -import 'package:badge/device_scan_select.dart'; +import 'package:badge/badge_app.dart';  import 'package:flutter/material.dart'; -import 'package:permission_handler/permission_handler.dart'; -import 'package:flutter_blue_plus/flutter_blue_plus.dart';  import 'package:logger/logger.dart';  var logger = Logger(printer: PrettyPrinter());  void main() { -  runApp(const MyApp()); -} - -class MyApp extends StatelessWidget { -  const MyApp({super.key}); - -  // This widget is the root of your application. -  @override -  Widget build(BuildContext context) { -    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: const MyHomePage(title: 'Badge Scanner'), -    ); -  } -} - -class MyHomePage extends StatefulWidget { -  const MyHomePage({super.key, required this.title}); - -  // This widget is the home page of your application. It is stateful, meaning -  // that it has a State object (defined below) that contains fields that affect -  // how it looks. - -  // This class is the configuration for the state. It holds the values (in this -  // case the title) provided by the parent (in this case the App widget) and -  // used by the build method of the State. Fields in a Widget subclass are -  // always marked "final". - -  final String title; - -  @override -  State<MyHomePage> createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State<MyHomePage> { -  List<ScanResult> scanResults = []; -  bool isScanning = false; - -  ScanResult? selectedDevice; - -  void _doConnect() async { -    var dev = selectedDevice?.device; -    if (dev == null) return; -    //??? -    Navigator.push( -      context, -      MaterialPageRoute( -        builder: (context) => DeviceDetailsScreen(btDevice: dev), -      ), -    ); -  } - -  void _doScan() async { -    var system = await FlutterBluePlus.systemDevices([]); -    for (var d in system) { -      logger.i('${d.platformName} already connected to! ${d.remoteId}'); -      // if (d.platformName == "myBleDevice") { -      //   await r.connect(); // must connect our app -      // } -    } - -    setState(() { -      selectedDevice = null; -      scanResults = []; -      isScanning = true; -    }); -    var subscription = FlutterBluePlus.scanResults.listen( -      onScanResult, -      onError: (e) => logger.e(e), -    ); -    // should probably use this! -    FlutterBluePlus.cancelWhenScanComplete(subscription); - -    // Ehhhh... can't have both -    try { -      if (Platform.isAndroid) { -        await FlutterBluePlus.startScan( -          withKeywords: ["NimBLE"], -          timeout: Duration(seconds: 5), -        ); -      } else { -        // for Linux, which can't do advNames -        // msd doesn't work, either???? -        await FlutterBluePlus.startScan( -          //withMsd: [MsdFilter(0xffff, data: ascii.encode("uvok"))], -          timeout: Duration(seconds: 5), -        ); -      } - -      // wait for scanning to stop -      await FlutterBluePlus.isScanning.where((val) => val == false).first; -    } finally { -      subscription.cancel(); -      setState(() { -        isScanning = false; -      }); -    } -  } - -  void onScanResult(List<ScanResult> results) { -    if (results.isNotEmpty) { -      //ScanResult r = results.last; // the most recently found device -      for (var r in results.where( -        (d) => -            d.rssi > -90 && -            !scanResults.any((ed) => ed.device.remoteId == d.device.remoteId), -      )) { -        logger.i( -          '${r.device.remoteId}: "${r.device.platformName}" / "${r.device.advName}" / "${r.advertisementData.advName}" found!', -        ); -        scanResults.add(r); -      } -      setState(() {}); -    } -  } - -  Future getPermissions() async { -    try { -      await Permission.bluetooth.request(); -    } catch (e) { -      logger.e(e.toString()); -    } -  } - -  void btHandler(BluetoothAdapterState event) { -    logger.i(event); -    switch (event) { -      case BluetoothAdapterState.on: -        break; -      default: -        break; -    } -  } - -  @override -  void initState() { -    super.initState(); -    getPermissions(); -    FlutterBluePlus.adapterState.listen(btHandler); -  } - -  @override -  Widget build(BuildContext context) { -    // This method is rerun every time setState is called, for instance as done -    // by the _incrementCounter method above. -    // -    // The Flutter framework has been optimized to make rerunning build methods -    // fast, so that you can just rebuild anything that needs updating rather -    // than having to individually change instances of widgets. -    return Scaffold( -      appBar: AppBar( -        // TRY THIS: Try changing the color here to a specific color (to -        // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar -        // change color while the other colors stay the same. -        backgroundColor: Theme.of(context).colorScheme.inversePrimary, -        // Here we take the value from the MyHomePage object that was created by -        // the App.build method, and use it to set our appbar title. -        title: Text(widget.title), -      ), -      body: Center( -        // Center is a layout widget. It takes a single child and positions it -        // in the middle of the parent. -        child: Column( -          // Column is also a layout widget. It takes a list of children and -          // arranges them vertically. By default, it sizes itself to fit its -          // children horizontally, and tries to be as tall as its parent. -          // -          // Column has various properties to control how it sizes itself and -          // how it positions its children. Here we use mainAxisAlignment to -          // center the children vertically; the main axis here is the vertical -          // axis because Columns are vertical (the cross axis would be -          // horizontal). -          // -          // 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: <Widget>[ -            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: DeviceScanSelection( -                items: scanResults, -                onItemSelected: (item) { -                  setState(() => selectedDevice = item); -                }, -              ), -            ), -          ], -        ), -      ), -    ); -  } -} - -String firstGiven(List<String> list) { -  return list.firstWhere((s) => s.isNotEmpty, orElse: () => ""); +  runApp(const BadgeApp());  } diff --git a/lib/scan_page.dart b/lib/scan_page.dart new file mode 100644 index 0000000..d21ade7 --- /dev/null +++ b/lib/scan_page.dart @@ -0,0 +1,173 @@ +import 'dart:io' show Platform; + +import 'package:badge/device_details.dart'; +import 'package:badge/device_scan_select.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:logger/logger.dart'; +import 'package:permission_handler/permission_handler.dart'; + +var logger = Logger(printer: PrettyPrinter()); + +class ScanPage extends StatefulWidget { +  const ScanPage({super.key, required this.title}); + +  // Original doc: Fields in a Widget subclass are always marked "final". + +  final String title; + +  @override +  State<ScanPage> createState() => _ScanPageState(); +} + +class _ScanPageState extends State<ScanPage> { +  List<ScanResult> scanResults = []; +  bool isScanning = false; + +  ScanResult? selectedDevice; + +  void _doConnect() async { +    var dev = selectedDevice?.device; +    if (dev == null) return; +    //??? +    Navigator.push( +      context, +      MaterialPageRoute( +        builder: (context) => DeviceDetailsScreen(btDevice: dev), +      ), +    ); +  } + +  void _doScan() async { +    var system = await FlutterBluePlus.systemDevices([]); +    for (var d in system) { +      logger.i('${d.platformName} already connected to! ${d.remoteId}'); +    } + +    setState(() { +      selectedDevice = null; +      scanResults = []; +      isScanning = true; +    }); +    var subscription = FlutterBluePlus.scanResults.listen( +      onScanResult, +      onError: (e) => logger.e(e), +    ); +    // either this, or the cancel in the finally block, should do the same? +    FlutterBluePlus.cancelWhenScanComplete(subscription); + +    try { +      if (Platform.isAndroid) { +        // Ehhhh... can't have both keyword/services +        await FlutterBluePlus.startScan( +          withKeywords: ["NimBLE"], +          timeout: Duration(seconds: 5), +        ); +      } else { +        // for Linux, which can't do advNames (but platformname, for whatever reason) +        // msd doesn't work, either???? +        await FlutterBluePlus.startScan( +          //withMsd: [MsdFilter(0xffff, data: ascii.encode("uvok"))], +          timeout: Duration(seconds: 5), +        ); +      } + +      // wait for scanning to stop +      await FlutterBluePlus.isScanning.where((val) => val == false).first; +    } finally { +      subscription.cancel(); +      setState(() { +        isScanning = false; +      }); +    } +  } + +  void onScanResult(List<ScanResult> results) { +    if (results.isNotEmpty) { +      for (var r in results.where( +        (d) => d.rssi > -90 && !_deviceInResults(d), +      )) { +        logger.i( +          '${r.device.remoteId}: "${r.device.platformName}" / "${r.device.advName}" / "${r.advertisementData.advName}" found!', +        ); +        scanResults.add(r); +      } +      setState(() {}); +    } +  } + +  bool _deviceInResults(ScanResult incomingDev) => scanResults.any( +    (existingDev) => existingDev.device.remoteId == incomingDev.device.remoteId, +  ); + +  Future getPermissions() async { +    try { +      await Permission.bluetooth.request(); +    } catch (e) { +      logger.e(e.toString()); +    } +  } + +  void btHandler(BluetoothAdapterState event) { +    logger.i(event); +    switch (event) { +      case BluetoothAdapterState.on: +        break; +      default: +        break; +    } +  } + +  @override +  void initState() { +    super.initState(); +    getPermissions(); +    FlutterBluePlus.adapterState.listen(btHandler); +  } + +  @override +  Widget build(BuildContext context) { +    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: <Widget>[ +            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: DeviceScanSelection( +                items: scanResults, +                onItemSelected: (item) { +                  setState(() => selectedDevice = item); +                }, +              ), +            ), +          ], +        ), +      ), +    ); +  } +} diff --git a/test/widget_test.dart b/test/widget_test.dart index 11b1c70..58c70d5 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -5,15 +5,14 @@  // gestures. You can also use WidgetTester to find child widgets in the widget  // tree, read text, and verify that the values of widget properties are correct. +import 'package:badge/badge_app.dart';  import 'package:flutter/material.dart';  import 'package:flutter_test/flutter_test.dart'; -import 'package:badge/main.dart'; -  void main() {    testWidgets('Counter increments smoke test', (WidgetTester tester) async {      // Build our app and trigger a frame. -    await tester.pumpWidget(const MyApp()); +    await tester.pumpWidget(const BadgeApp());      // Verify that our counter starts at 0.      expect(find.text('0'), findsOneWidget); | 
