some cleanup and adding functions
This commit is contained in:
parent
65a1ce14d1
commit
1d16f01cc3
|
@ -42,3 +42,65 @@ app.*.map.json
|
|||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
|
||||
## custom
|
||||
|
||||
**/ios/**
|
||||
**/macos/**
|
||||
**/windows/**
|
||||
**/linux/**
|
||||
|
||||
## https://github.com/flutter/flutter/blob/master/.gitignore
|
||||
|
||||
# iOS/XCode related
|
||||
**/ios/**/*.mode1v3
|
||||
**/ios/**/*.mode2v3
|
||||
**/ios/**/*.moved-aside
|
||||
**/ios/**/*.pbxuser
|
||||
**/ios/**/*.perspectivev3
|
||||
**/ios/**/*sync/
|
||||
**/ios/**/.sconsign.dblite
|
||||
**/ios/**/.tags*
|
||||
**/ios/**/.vagrant/
|
||||
**/ios/**/DerivedData/
|
||||
**/ios/**/Icon?
|
||||
**/ios/**/Pods/
|
||||
**/ios/**/.symlinks/
|
||||
**/ios/**/profile
|
||||
**/ios/**/xcuserdata
|
||||
**/ios/.generated/
|
||||
**/ios/Flutter/.last_build_id
|
||||
**/ios/Flutter/App.framework
|
||||
**/ios/Flutter/Flutter.framework
|
||||
**/ios/Flutter/Flutter.podspec
|
||||
**/ios/Flutter/Generated.xcconfig
|
||||
**/ios/Flutter/ephemeral
|
||||
**/ios/Flutter/app.flx
|
||||
**/ios/Flutter/app.zip
|
||||
**/ios/Flutter/flutter_assets/
|
||||
**/ios/Flutter/flutter_export_environment.sh
|
||||
**/ios/ServiceDefinitions.json
|
||||
**/ios/Runner/GeneratedPluginRegistrant.*
|
||||
|
||||
# macOS
|
||||
**/Flutter/ephemeral/
|
||||
**/Pods/
|
||||
**/macos/Flutter/GeneratedPluginRegistrant.swift
|
||||
**/macos/Flutter/ephemeral
|
||||
**/xcuserdata/
|
||||
|
||||
# Windows
|
||||
**/windows/flutter/generated_plugin_registrant.cc
|
||||
**/windows/flutter/generated_plugin_registrant.h
|
||||
|
||||
# Linux
|
||||
**/linux/flutter/generated_plugin_registrant.cc
|
||||
**/linux/flutter/generated_plugin_registrant.h
|
||||
|
||||
# Exceptions to above rules.
|
||||
!**/ios/**/default.mode1v3
|
||||
!**/ios/**/default.mode2v3
|
||||
!**/ios/**/default.pbxuser
|
||||
!**/ios/**/default.perspectivev3
|
||||
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
|
||||
!/dev/ci/**/Gemfile.lock
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<!--<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>-->
|
||||
<!-- Needed to communicate with already-paired Bluetooth devices. (Android 12 upwards)-->
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<application
|
||||
|
|
|
@ -11,6 +11,32 @@ Future<Map<String, dynamic>> readJson() async {
|
|||
return data_;
|
||||
}
|
||||
|
||||
/*Future<void> checkNameAvailability(String input) async {
|
||||
// await cloudServiceAPI.loadConfig();
|
||||
List<dynamic> devices = await cloudServiceAPI.getDevices();
|
||||
for (Map<String, dynamic> selected in devices) {
|
||||
if (selected["id"] == input) {
|
||||
await showNameAvailabilityStatus(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
await showNameAvailabilityStatus(false);
|
||||
}
|
||||
|
||||
Future<void> showNameAvailabilityStatus(bool status) async {
|
||||
String statusText = status
|
||||
? "die eingegebene ID ist verfügbar"
|
||||
: "die eingegebene ID ist nicht verfügbar";
|
||||
Fluttertoast.showToast(
|
||||
msg: statusText,
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
timeInSecForIosWeb: 2,
|
||||
backgroundColor: Colors.grey[200],
|
||||
textColor: Colors.black,
|
||||
fontSize: 16.0);
|
||||
}*/
|
||||
|
||||
class CloudServiceAPI {
|
||||
static late final Map<String, dynamic> credentials;
|
||||
static late Future loadJson;
|
||||
|
@ -50,22 +76,22 @@ class CloudServiceAPI {
|
|||
};
|
||||
}
|
||||
Future<List> getDevices() async {
|
||||
var url = Uri.https(address, '/api/devices');
|
||||
Uri url = Uri.https(address, '/api/devices');
|
||||
Response r = await get(url, headers: headers);
|
||||
return json.decode(r.body) as List<dynamic>;
|
||||
}
|
||||
Future<Map<String, dynamic>> getDeviceInfo(deviceID) async {
|
||||
var url = Uri.https(address, '/api/devices/$deviceID');
|
||||
Uri url = Uri.https(address, '/api/devices/$deviceID');
|
||||
Response r = await get(url, headers: headers);
|
||||
return json.decode(r.body) as Map<String, dynamic>;
|
||||
}
|
||||
Future<Map<String, dynamic>> getInformation() async {
|
||||
var url = Uri.https(address, '/api/app');
|
||||
Uri url = Uri.https(address, '/api/app');
|
||||
Response r = await get(url, headers: headers);
|
||||
return json.decode(r.body) as Map<String, dynamic>;
|
||||
}
|
||||
Future<bool> createDevice(id, primaryThumbprint, secondaryThumbprint) async{
|
||||
var url = Uri.https(address, '/api/devices');
|
||||
Uri url = Uri.https(address, '/api/devices');
|
||||
Response r = await post(
|
||||
url,
|
||||
headers: headers,
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
|
||||
|
||||
class BluetoothDeviceListEntry extends ListTile {
|
||||
BluetoothDeviceListEntry({super.key,
|
||||
required BluetoothDevice device,
|
||||
int? rssi,
|
||||
GestureTapCallback? onTap,
|
||||
GestureLongPressCallback? onLongPress,
|
||||
bool enabled = true,
|
||||
}) : super(
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
enabled: enabled,
|
||||
leading: Icon(device.isConnected ? Icons.bluetooth_connected : Icons.bluetooth),
|
||||
title: Text(device.name ?? "~ UNKNOWN_DEVICE ~"),
|
||||
subtitle: Text(device.address.toString()),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
rssi != null
|
||||
? Container(
|
||||
margin: const EdgeInsets.all(8.0),
|
||||
child: DefaultTextStyle(
|
||||
style: _computeTextStyle(rssi),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(rssi.toString()),
|
||||
const Text('dBm'),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox(width: 0, height: 0),
|
||||
device.isBonded
|
||||
? const Icon(Icons.link)
|
||||
: const SizedBox(width: 0, height: 0),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.settings)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
static TextStyle _computeTextStyle(int rssi) {
|
||||
/**/ if (rssi >= -35) {
|
||||
return TextStyle(color: Colors.greenAccent[700]);
|
||||
} else if (rssi >= -45) {
|
||||
return TextStyle(
|
||||
color: Color.lerp(
|
||||
Colors.greenAccent[700], Colors.lightGreen, -(rssi + 35) / 10));
|
||||
} else if (rssi >= -55) {
|
||||
return TextStyle(
|
||||
color: Color.lerp(
|
||||
Colors.lightGreen, Colors.lime[600], -(rssi + 45) / 10));
|
||||
} else if (rssi >= -65) {
|
||||
return TextStyle(
|
||||
color: Color.lerp(Colors.lime[600], Colors.amber, -(rssi + 55) / 10));
|
||||
} else if (rssi >= -75) {
|
||||
return TextStyle(
|
||||
color: Color.lerp(
|
||||
Colors.amber, Colors.deepOrangeAccent, -(rssi + 65) / 10));
|
||||
} else if (rssi >= -85) {
|
||||
return TextStyle(
|
||||
color: Color.lerp(
|
||||
Colors.deepOrangeAccent, Colors.redAccent, -(rssi + 75) / 10));
|
||||
} else {
|
||||
return const TextStyle(color: Colors.redAccent);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,37 +1,369 @@
|
|||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_provisioning_for_iot/widgets/bluetooth_discovery.dart';
|
||||
import 'package:flutter_provisioning_for_iot/objects/cloud_service_api.dart';
|
||||
import 'package:flutter_provisioning_for_iot/widgets/switch_widget.dart';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_provisioning_for_iot/objects/cloud_service_api.dart';
|
||||
import 'package:flutter_provisioning_for_iot/screens/bluetooth_device_list_entry.dart';
|
||||
|
||||
import 'package:app_settings/app_settings.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
|
||||
class BluetoothScreen extends StatefulWidget {
|
||||
final bool start = true;
|
||||
|
||||
const BluetoothScreen({super.key});
|
||||
|
||||
@override
|
||||
State<BluetoothScreen> createState() => _BluetoothScreen();
|
||||
}
|
||||
|
||||
// debugPrint("r: ${r.device.name} ${r.device.address} ${r.rssi}");
|
||||
class _BluetoothScreen extends State<BluetoothScreen> {
|
||||
|
||||
final textFieldValueHolder = TextEditingController();
|
||||
CloudServiceAPI cloudServiceAPI = CloudServiceAPI();
|
||||
List<BluetoothDiscoveryResult> results =
|
||||
List<BluetoothDiscoveryResult>.empty(growable: true);
|
||||
//BluetoothManager bluetoothManager = BluetoothManager();
|
||||
|
||||
late Stream<BluetoothDiscoveryResult> _stream;
|
||||
late StreamSubscription<BluetoothDiscoveryResult> _streamSubscription;
|
||||
late Stream<Uint8List> _connectionStream;
|
||||
late StreamSubscription<Uint8List> _connectionStreamSubscription;
|
||||
BluetoothDevice? _bluetoothDevice;
|
||||
BluetoothConnection? _bluetoothConnection;
|
||||
final List<BluetoothDiscoveryResult> _discoveryResults =
|
||||
List<BluetoothDiscoveryResult>.empty(growable: true);
|
||||
String _messageBuffer = "";
|
||||
bool isDiscovering = false;
|
||||
bool isConnecting = false;
|
||||
bool isDisconnecting = false;
|
||||
bool isConnected = false;
|
||||
|
||||
ButtonStyle buttonStyle = ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.black,
|
||||
backgroundColor: const Color(0xFFFDE100), // Text Color (Foreground color)
|
||||
);
|
||||
String inputName = "";
|
||||
bool initScan = true;
|
||||
bool scanState = false;
|
||||
bool widgetScanState = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
isDiscovering = widget.start;
|
||||
if (isDiscovering) {
|
||||
_startDiscovery();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Avoid memory leak (`setState` after dispose) and cancel discovery
|
||||
_streamSubscription.cancel();
|
||||
if (isConnected) {
|
||||
isDisconnecting = true;
|
||||
_bluetoothConnection?.dispose();
|
||||
_bluetoothConnection = null;
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _disconnectDevice(BluetoothDevice device) async {
|
||||
if (_bluetoothConnection != null) {
|
||||
await _bluetoothConnection!.finish();
|
||||
_bluetoothConnection = null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _connectDevice(BluetoothDevice device) async {
|
||||
String address = device.address;
|
||||
if (_bluetoothConnection != null) {
|
||||
await _bluetoothConnection!.finish();
|
||||
_bluetoothConnection = null;
|
||||
}
|
||||
_bluetoothConnection = await BluetoothConnection.toAddress(address);
|
||||
debugPrint("Connected to the device");
|
||||
setState(() {
|
||||
isConnecting = false;
|
||||
isDisconnecting = false;
|
||||
});
|
||||
|
||||
_connectionStream = _bluetoothConnection!.input!;
|
||||
_connectionStreamSubscription = _connectionStream.listen(_connectionOnListen);
|
||||
_connectionStreamSubscription.onDone(_connectionOnDone);
|
||||
}
|
||||
|
||||
void _connectionOnDone() {
|
||||
/*
|
||||
// Example: Detect which side closed the connection
|
||||
// There should be `isDisconnecting` flag to show are we are (locally)
|
||||
// in middle of disconnecting process, should be set before calling
|
||||
// `dispose`, `finish` or `close`, which all causes to disconnect.
|
||||
// If we except the disconnection, `onDone` should be fired as result.
|
||||
// If we didn't except this (no flag set), it means closing by remote.
|
||||
*/
|
||||
if (isDisconnecting) {
|
||||
debugPrint("Disconnecting locally!");
|
||||
} else {
|
||||
debugPrint("Disconnected remotely!");
|
||||
}
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
|
||||
void _connectionOnListen(Uint8List data) {
|
||||
debugPrint("received: $data");
|
||||
debugPrint("received decoded: ${const Utf8Decoder().convert(data)}");
|
||||
// Allocate buffer for parsed data
|
||||
int backspacesCounter = 0;
|
||||
for (var byte in data) {
|
||||
if (byte == 8 || byte == 127) {
|
||||
backspacesCounter++;
|
||||
}
|
||||
}
|
||||
Uint8List buffer = Uint8List(data.length - backspacesCounter);
|
||||
int bufferIndex = buffer.length;
|
||||
|
||||
// Apply backspace control character
|
||||
backspacesCounter = 0;
|
||||
for (int i = data.length - 1; i >= 0; i--) {
|
||||
if (data[i] == 8 || data[i] == 127) {
|
||||
backspacesCounter++;
|
||||
} else {
|
||||
if (backspacesCounter > 0) {
|
||||
backspacesCounter--;
|
||||
} else {
|
||||
buffer[--bufferIndex] = data[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create message if there is new line character
|
||||
String dataString = String.fromCharCodes(buffer);
|
||||
int index = buffer.indexOf(13);
|
||||
if (~index != 0) {
|
||||
setState(() {
|
||||
_messageBuffer = dataString.substring(index);
|
||||
});
|
||||
} else {
|
||||
_messageBuffer = (backspacesCounter > 0
|
||||
? _messageBuffer.substring(
|
||||
0, _messageBuffer.length - backspacesCounter)
|
||||
: _messageBuffer + dataString);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _startDiscovery() async {
|
||||
_stream = FlutterBluetoothSerial.instance.startDiscovery();
|
||||
_streamSubscription = _stream.listen(_discoveryOnListen);
|
||||
_streamSubscription.onDone(_discoveryOnDone);
|
||||
}
|
||||
|
||||
void _discoveryOnDone() {
|
||||
setState(() {
|
||||
isDiscovering = false;
|
||||
//debugPrint(isDiscovering as String?);
|
||||
});
|
||||
}
|
||||
|
||||
void _discoveryOnListen(BluetoothDiscoveryResult event) {
|
||||
setState(() {
|
||||
final int existingIndex = _discoveryResults.indexWhere(
|
||||
(element) => element.device.address == event.device.address);
|
||||
if (existingIndex >= 0) {
|
||||
_discoveryResults[existingIndex] = event;
|
||||
} else {
|
||||
_discoveryResults.add(event);
|
||||
}
|
||||
debugPrint("event: ${event.device.address} ${event.device.name}");
|
||||
|
||||
String deviceAddress = "64:BC:58:61:56:B0";
|
||||
if (event.device.address == deviceAddress) {
|
||||
_bluetoothDevice = event.device;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _restartDiscovery() async {
|
||||
setState(() {
|
||||
_discoveryResults.clear();
|
||||
isDiscovering = true;
|
||||
});
|
||||
_startDiscovery();
|
||||
}
|
||||
|
||||
Future<void> _sendData() async {
|
||||
String output = "0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ !§%&()=?#!?";
|
||||
_bluetoothConnection!.output
|
||||
.add(Uint8List.fromList(const Utf8Encoder().convert("$output \r\n")));
|
||||
await _bluetoothConnection!.output.allSent;
|
||||
debugPrint("sent: $output");
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
//bool isDark = true;
|
||||
return Theme(
|
||||
data: ThemeData.dark(), //isDark ? ThemeData.dark() : ThemeData.light(),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: isDiscovering
|
||||
? const Text('Bluetooth Devices (searching...)')
|
||||
: const Text('Bluetooth Devices'),
|
||||
actions: <Widget>[
|
||||
isDiscovering
|
||||
? FittedBox(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(16.0),
|
||||
child: const CircularProgressIndicator(
|
||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
||||
),
|
||||
),
|
||||
)
|
||||
: IconButton(
|
||||
icon: const Icon(Icons.replay),
|
||||
onPressed: _restartDiscovery,
|
||||
)
|
||||
],
|
||||
),
|
||||
|
||||
body: // Center(
|
||||
/*child:*/ SingleChildScrollView(
|
||||
physics: const ScrollPhysics(),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
_SingleSection(
|
||||
title: "Setup",
|
||||
children: [
|
||||
const _CustomListTile(
|
||||
title: "Please Enable Bluetooth",
|
||||
icon: Icons.info_outline_rounded,
|
||||
),
|
||||
_CustomListTile(
|
||||
title: "Bluetooth Settings",
|
||||
icon: Icons.bluetooth_connected,
|
||||
onTap: () async {
|
||||
await AppSettings.openBluetoothSettings();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
await Permission.bluetoothConnect.request();
|
||||
await Permission.bluetoothScan.request();
|
||||
if (await Permission.bluetoothScan.request().isGranted) {
|
||||
// Either the permission was already granted before or the user just granted it.
|
||||
debugPrint("Location Permission is granted");
|
||||
} else {
|
||||
debugPrint("Location Permission is denied.");
|
||||
}
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("Request Permissions"),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
_restartDiscovery();
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("Restart Scan"),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
_sendData();
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("Send Data"),
|
||||
),
|
||||
const Divider(),
|
||||
const _SingleSection(
|
||||
title: "Bluetooth Devices",
|
||||
children: [],
|
||||
),
|
||||
const Divider(),
|
||||
ListView.builder(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: _discoveryResults.length,
|
||||
itemBuilder: (BuildContext context, index) {
|
||||
BluetoothDiscoveryResult result = _discoveryResults[index];
|
||||
final device = result.device;
|
||||
final address = device.address;
|
||||
return BluetoothDeviceListEntry(
|
||||
device: device,
|
||||
rssi: result.rssi,
|
||||
onTap: () {
|
||||
debugPrint(_bluetoothConnection?.isConnected.toString());
|
||||
if (_bluetoothConnection?.isConnected ?? false) {
|
||||
_disconnectDevice(device);
|
||||
} else {
|
||||
_connectDevice(device);
|
||||
}
|
||||
|
||||
//Navigator.of(context).pop(result.device);
|
||||
},
|
||||
onLongPress: () async {
|
||||
try {
|
||||
bool bonded = false;
|
||||
if (device.isBonded) {
|
||||
debugPrint('Unbonding from ${device.address}...');
|
||||
await FlutterBluetoothSerial.instance
|
||||
.removeDeviceBondWithAddress(address);
|
||||
debugPrint(
|
||||
'Unbonding from ${device.address} has succed');
|
||||
} else {
|
||||
debugPrint('Bonding with ${device.address}...');
|
||||
bonded = (await FlutterBluetoothSerial.instance
|
||||
.bondDeviceAtAddress(address))!;
|
||||
debugPrint(
|
||||
'Bonding with ${device.address} has ${bonded ? 'succed' : 'failed'}.');
|
||||
}
|
||||
setState(() {
|
||||
_discoveryResults[_discoveryResults.indexOf(result)] =
|
||||
BluetoothDiscoveryResult(
|
||||
device: BluetoothDevice(
|
||||
name: device.name ?? '',
|
||||
address: address,
|
||||
type: device.type,
|
||||
bondState: bonded
|
||||
? BluetoothBondState.bonded
|
||||
: BluetoothBondState.none,
|
||||
),
|
||||
rssi: result.rssi);
|
||||
});
|
||||
} catch (ex) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Error occured while bonding'),
|
||||
content: Text(ex.toString()),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: const Text("Close"),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
//),
|
||||
),
|
||||
);
|
||||
}
|
||||
/*
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
@ -60,6 +392,19 @@ class _BluetoothScreen extends State<BluetoothScreen> {
|
|||
),
|
||||
child: Column(
|
||||
children: [
|
||||
const Text(
|
||||
"General",
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
_CustomListTile(
|
||||
title: "Dark Mode",
|
||||
icon: CupertinoIcons.moon,
|
||||
trailing:
|
||||
CupertinoSwitch(value: false, onChanged: (value) {})),
|
||||
]
|
||||
),
|
||||
TextField(
|
||||
controller: textFieldValueHolder,
|
||||
decoration: const InputDecoration(
|
||||
|
@ -81,6 +426,39 @@ class _BluetoothScreen extends State<BluetoothScreen> {
|
|||
child: const Text("check name"),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
//_startDiscovery();
|
||||
initScan();
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("discover devices"),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
//_startDiscovery();
|
||||
_sendData();
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("send data"),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
//_startDiscovery();
|
||||
dispose();
|
||||
},
|
||||
style: buttonStyle,
|
||||
child: const Text("dispose"),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -92,7 +470,7 @@ class _BluetoothScreen extends State<BluetoothScreen> {
|
|||
bottom: BorderSide(width: 1.5, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
child: Row(children: [
|
||||
child: Row(children: [
|
||||
const Text(
|
||||
"Toggle Scan",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
|
@ -106,7 +484,7 @@ class _BluetoothScreen extends State<BluetoothScreen> {
|
|||
setState(() {
|
||||
widgetScanState = toggleState;
|
||||
});
|
||||
})
|
||||
})
|
||||
]),
|
||||
),
|
||||
// BluetoothDiscovery(start: initScan, deviceID: inputName),
|
||||
|
@ -114,37 +492,64 @@ class _BluetoothScreen extends State<BluetoothScreen> {
|
|||
),
|
||||
//),
|
||||
),
|
||||
|
||||
),
|
||||
);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
Future<void> checkNameAvailability(String input) async {
|
||||
// await cloudServiceAPI.loadConfig();
|
||||
List<dynamic> devices = await cloudServiceAPI.getDevices();
|
||||
for (Map<String, dynamic> selected in devices) {
|
||||
if (selected["id"] == input) {
|
||||
await showNameAvailabilityStatus(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
await showNameAvailabilityStatus(false);
|
||||
}
|
||||
class _CustomListTile extends StatelessWidget {
|
||||
final String title;
|
||||
final IconData icon;
|
||||
final VoidCallback? onTap;
|
||||
//final Widget? trailing;
|
||||
|
||||
Future<void> showNameAvailabilityStatus(bool status) async {
|
||||
String statusText = status
|
||||
? "die eingegebene ID ist verfügbar"
|
||||
: "die eingegebene ID ist nicht verfügbar";
|
||||
Fluttertoast.showToast(
|
||||
msg: statusText,
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
timeInSecForIosWeb: 2,
|
||||
backgroundColor: Colors.grey[200],
|
||||
textColor: Colors.black,
|
||||
fontSize: 16.0);
|
||||
const _CustomListTile({
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.icon,
|
||||
this.onTap,
|
||||
//this.trailing,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
title: Text(title),
|
||||
leading: Icon(icon),
|
||||
onTap: onTap,
|
||||
//trailing: trailing,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BluetoothScan{
|
||||
class _SingleSection extends StatelessWidget {
|
||||
final String? title;
|
||||
final List<Widget> children;
|
||||
|
||||
const _SingleSection({
|
||||
Key? key,
|
||||
this.title,
|
||||
required this.children,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (title != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
title!,
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: children,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
|
||||
|
||||
import './bluetooth_device_list_entry.dart';
|
||||
|
||||
class BluetoothScreen extends StatefulWidget {
|
||||
/// If true, discovery starts on page start, otherwise user must press action button.
|
||||
final bool start;
|
||||
|
||||
const BluetoothScreen({this.start = true});
|
||||
|
||||
@override
|
||||
_BluetoothScreen createState() => new _BluetoothScreen();
|
||||
}
|
||||
|
||||
class _BluetoothScreen extends State<BluetoothScreen> {
|
||||
StreamSubscription<BluetoothDiscoveryResult>? _streamSubscription;
|
||||
List<BluetoothDiscoveryResult> results =
|
||||
List<BluetoothDiscoveryResult>.empty(growable: true);
|
||||
bool isDiscovering = false;
|
||||
|
||||
_BluetoothScreen();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
isDiscovering = widget.start;
|
||||
if (isDiscovering) {
|
||||
_startDiscovery();
|
||||
}
|
||||
}
|
||||
|
||||
void _restartDiscovery() {
|
||||
setState(() {
|
||||
results.clear();
|
||||
isDiscovering = true;
|
||||
});
|
||||
|
||||
_startDiscovery();
|
||||
}
|
||||
|
||||
void _startDiscovery() {
|
||||
_streamSubscription =
|
||||
FlutterBluetoothSerial.instance.startDiscovery().listen((r) {
|
||||
setState(() {
|
||||
final existingIndex = results.indexWhere(
|
||||
(element) => element.device.address == r.device.address);
|
||||
if (existingIndex >= 0)
|
||||
results[existingIndex] = r;
|
||||
else
|
||||
results.add(r);
|
||||
});
|
||||
});
|
||||
|
||||
_streamSubscription!.onDone(() {
|
||||
setState(() {
|
||||
isDiscovering = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// @TODO . One day there should be `_pairDevice` on long tap on something... ;)
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Avoid memory leak (`setState` after dispose) and cancel discovery
|
||||
_streamSubscription?.cancel();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: isDiscovering
|
||||
? Text('Discovering devices')
|
||||
: Text('Discovered devices'),
|
||||
actions: <Widget>[
|
||||
isDiscovering
|
||||
? FittedBox(
|
||||
child: Container(
|
||||
margin: new EdgeInsets.all(16.0),
|
||||
child: CircularProgressIndicator(
|
||||
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
||||
),
|
||||
),
|
||||
)
|
||||
: IconButton(
|
||||
icon: Icon(Icons.replay),
|
||||
onPressed: _restartDiscovery,
|
||||
)
|
||||
],
|
||||
),
|
||||
body: ListView.builder(
|
||||
itemCount: results.length,
|
||||
itemBuilder: (BuildContext context, index) {
|
||||
BluetoothDiscoveryResult result = results[index];
|
||||
final device = result.device;
|
||||
final address = device.address;
|
||||
return BluetoothDeviceListEntry(
|
||||
device: device,
|
||||
rssi: result.rssi,
|
||||
onTap: () {
|
||||
Navigator.of(context).pop(result.device);
|
||||
},
|
||||
onLongPress: () async {
|
||||
try {
|
||||
bool bonded = false;
|
||||
if (device.isBonded) {
|
||||
print('Unbonding from ${device.address}...');
|
||||
await FlutterBluetoothSerial.instance
|
||||
.removeDeviceBondWithAddress(address);
|
||||
print('Unbonding from ${device.address} has succed');
|
||||
} else {
|
||||
print('Bonding with ${device.address}...');
|
||||
bonded = (await FlutterBluetoothSerial.instance
|
||||
.bondDeviceAtAddress(address))!;
|
||||
print(
|
||||
'Bonding with ${device.address} has ${bonded ? 'succed' : 'failed'}.');
|
||||
}
|
||||
setState(() {
|
||||
results[results.indexOf(result)] = BluetoothDiscoveryResult(
|
||||
device: BluetoothDevice(
|
||||
name: device.name ?? '',
|
||||
address: address,
|
||||
type: device.type,
|
||||
bondState: bonded
|
||||
? BluetoothBondState.bonded
|
||||
: BluetoothBondState.none,
|
||||
),
|
||||
rssi: result.rssi);
|
||||
});
|
||||
} catch (ex) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Error occured while bonding'),
|
||||
content: Text("${ex.toString()}"),
|
||||
actions: <Widget>[
|
||||
new TextButton(
|
||||
child: new Text("Close"),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
50
pubspec.lock
50
pubspec.lock
|
@ -1,6 +1,14 @@
|
|||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
app_settings:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: app_settings
|
||||
sha256: "7a5b880e2dd41dba8877108180380a1d28d874c231f7c0f9022127a4061b88e1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.8"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -224,6 +232,46 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
permission_handler:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: permission_handler
|
||||
sha256: "33c6a1253d1f95fd06fa74b65b7ba907ae9811f9d5c1d3150e51417d04b8d6a8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.2.0"
|
||||
permission_handler_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_android
|
||||
sha256: "8028362b40c4a45298f1cbfccd227c8dd6caf0e27088a69f2ba2ab15464159e2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.2.0"
|
||||
permission_handler_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_apple
|
||||
sha256: "9c370ef6a18b1c4b2f7f35944d644a56aa23576f23abee654cf73968de93f163"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.0.7"
|
||||
permission_handler_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
sha256: "68abbc472002b5e6dfce47fe9898c6b7d8328d58b5d2524f75e277c07a97eb84"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.9.0"
|
||||
permission_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_windows
|
||||
sha256: f67cab14b4328574938ecea2db3475dad7af7ead6afab6338772c5f88963e38b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.2"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -415,4 +463,4 @@ packages:
|
|||
version: "0.2.0+2"
|
||||
sdks:
|
||||
dart: ">=2.18.0 <4.0.0"
|
||||
flutter: ">=3.0.0"
|
||||
flutter: ">=3.0.1"
|
||||
|
|
|
@ -41,6 +41,8 @@ dependencies:
|
|||
shared_preferences: ^2.0.13
|
||||
http: ^0.13.5
|
||||
fluttertoast: ^8.1.1
|
||||
app_settings: ^4.1.8
|
||||
permission_handler: ^10.2.0
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
|
|
Loading…
Reference in New Issue