From 9532e812f84a089cbf7fe8f35b3fa119fa17d728 Mon Sep 17 00:00:00 2001 From: uvok Date: Mon, 21 Jul 2025 15:23:19 +0200 Subject: Add q&d flutter app --- lib/main.dart | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 lib/main.dart (limited to 'lib') diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..86c82eb --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,196 @@ +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: 'Flutter Demo', + 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 createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + final List scanResults = []; + bool isScanning = false; + + void _doScan() async { + setState(() { + scanResults.clear(); + isScanning = true; + }); + var subscription = FlutterBluePlus.onScanResults.listen( + onScanResult, + onError: (e) => logger.e(e), + ); + + await FlutterBluePlus.startScan( + //withServices:[Guid("180D")], // match any of the specified services + //withNames:["Bluno"], // *or* any of the specified names + timeout: Duration(seconds: 10), + ); + + // wait for scanning to stop + await FlutterBluePlus.isScanning.where((val) => val == false).first; + + subscription.cancel(); + setState(() { + isScanning = false; + }); + } + + void onScanResult(results) { + if (results.isNotEmpty) { + ScanResult r = results.last; // the most recently found device + logger.i( + '${r.device.remoteId}: "${r.device.advName}" / "${r.advertisementData.advName}" found!', + ); + setState(() { + scanResults.add(r); + }); + } + } + + 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, + children: [ + ElevatedButton( + onPressed: isScanning ? null : _doScan, + child: isScanning ? Text("Scanning...") : Text("Start scan"), + ), + Expanded( + child: ListView.separated( + //itemCount: scanResults.length, + itemBuilder: (context, index) { + if (index >= scanResults.length) return null; + + final result = scanResults[index]; + final name = result.device.advName.isEmpty + ? Text("") + : Text(result.device.advName); + return ListTile( + title: name, + subtitle: Text(result.device.remoteId.str), + trailing: Text('RSSI: ${result.rssi}'), + ); + }, + separatorBuilder: (BuildContext context, int index) { + return Divider(); + }, + itemCount: scanResults.length, + ), + ), + ], + ), + ), + ); + } +} -- cgit v1.2.3