new icon, some refactoring and first working cycle

This commit is contained in:
YamiDoesDev 2023-01-04 14:18:34 +01:00
parent 7ffe408d44
commit a18e86bb22
73 changed files with 1232 additions and 1254 deletions

BIN
AppIcons.zip Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
AppIcons/appstore.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 KiB

BIN
AppIcons/playstore.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 28 KiB

BIN
assets/device/PFC200.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@ -1,5 +1,5 @@
{
"username": "...",
"password": "...",
"address": "..."
"username": "admin",
"password": "nE2Bkf9dwjLASq4VykUNjw7Rivve8W7elowfqYWhuFXaDtLT",
"address": "hfu-semester-projekt-oobp.azurewebsites.net"
}

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_provisioning_for_iot/screens/main_page.dart';
import 'screens/main_screen.dart';
void main() {
runApp(const MainPage());
runApp(const MainScreen());
}

View File

@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
import 'package:flutter_provisioning_for_iot/objects/bluetooth_object.dart';
import 'package:flutter_provisioning_for_iot/screens/bluetooth_device_settings.dart';
class BluetoothDeviceEntry extends ListTile {
BluetoothDeviceEntry({
super.key,
required BuildContext context,
required BluetoothDeviceSettings settingsPage,
required BluetoothObject bluetoothObject,
required BluetoothDevice device,
GestureTapCallback? onTap,
GestureLongPressCallback? onLongPress,
bool enabled = true,
}) : super(
onTap: onTap,
onLongPress: onLongPress,
enabled: enabled,
leading: Icon(bluetoothObject.isConnected ? Icons.bluetooth_connected : Icons.bluetooth),
title: Text(bluetoothObject.name),
subtitle: Text(bluetoothObject.address.toString()),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
device.isBonded ? const Icon(Icons.link) : const SizedBox(width: 0, height: 0),
Container(
margin: const EdgeInsets.all(8.0),
child: DefaultTextStyle(
style: _computeTextStyle(bluetoothObject.rssi),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(bluetoothObject.rssi.toString()),
const Text('dBm'),
],
),
),
),
IconButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => settingsPage)); //const BluetoothDeviceEntry()));
},
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);
}
}
}

View File

@ -0,0 +1,263 @@
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
import 'package:fluttertoast/fluttertoast.dart';
import '../objects/cloud_service_api.dart';
class BluetoothObject {
late int _rssi = -1;
late BluetoothDevice _device;
String _address = "";
String _name = "";
String id = "";
String _primaryThumbprint = "";
String _secondaryThumbprint = "";
late BluetoothConnection? _connection;
late Stream<Uint8List> _connectionStream;
late StreamSubscription<Uint8List> _connectionStreamSubscription;
CloudServiceAPI _cloudServiceAPI = CloudServiceAPI();
late Uint8List _messageBufferBits;
late String _messageBufferChars = "";
bool _isDisconnecting = false;
String get primaryThumbprint {
return _primaryThumbprint;
}
String get secondaryThumbprint {
return _secondaryThumbprint;
}
int get rssi {
return _rssi;
}
String get name {
return _name;
}
String get address {
return _address;
}
BluetoothDevice get device {
return _device;
}
BluetoothConnection? get connection {
return _connection;
}
bool get isConnected {
return (_connection == null ? false : true);
}
BluetoothObject(BluetoothDiscoveryResult result) {
_rssi = result.rssi;
_device = result.device;
_address = _device.address;
_name = _device.name ?? "device.name.UNKNOWN";
_connection = null;
}
Future<void> bondDevice() async {
try {
bool bonded = false;
if (_device.isBonded) {
debugPrint('Unbonding from $_address...');
await FlutterBluetoothSerial.instance.removeDeviceBondWithAddress(_address);
debugPrint('Unbonding from $_address has succed');
} else {
debugPrint('Bonding with $_address...');
bonded = (await FlutterBluetoothSerial.instance.bondDeviceAtAddress(_address))!;
debugPrint('Bonding with $_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();
},
),
],
);
},
);*/
}
}
Future<void> disconnectDevice() async {
_isDisconnecting = true;
if (_connection != null) {
await _connection!.finish();
_connection = null;
}
_isDisconnecting = false;
}
Future<void> connectDevice() async {
if (isConnected) {
await _connection!.finish();
_connection = null;
}
_connection = await BluetoothConnection.toAddress(_address);
debugPrint("Connected to the device");
_connectionStream = _connection!.input!;
_connectionStreamSubscription = _connectionStream.listen(_connectionOnListen);
_connectionStreamSubscription.onDone(_connectionOnDone);
}
void _connectionOnDone() {
if (_isDisconnecting) {
debugPrint("Disconnecting locally!");
} else {
debugPrint("Disconnected remotely!");
}
}
Future<void> _connectionOnListen(Uint8List data) async {
final String dataDecoded = const AsciiDecoder().convert(data);
debugPrint("received: $data");
debugPrint("received decoded: $dataDecoded");
_messageBufferChars += dataDecoded;
if (data[data.length - 1] == 10) {
debugPrint("received buffer: $_messageBufferChars");
int spaceIndex = _messageBufferChars.indexOf(" ");
String firstParameter = "";
String secondParameter = "";
try {
firstParameter = _messageBufferChars.substring(0, spaceIndex);
secondParameter = _messageBufferChars.substring(spaceIndex + 1, _messageBufferChars.length - 2);
} catch (ex) {
debugPrint(ex.toString());
}
debugPrint("we still go on!");
_messageBufferChars = "";
debugPrint("first: $firstParameter");
debugPrint("second: $secondParameter");
if (firstParameter == "fingerprint") {
debugPrint("received final buffer: $secondParameter");
_primaryThumbprint = secondParameter;
debugPrint("{ _secondaryThumbprint: $_secondaryThumbprint }");
debugPrint("{ id: $id, _primaryThumbprint: $_primaryThumbprint, _secondaryThumbprint: $_secondaryThumbprint }");
await _registerDevice();
}
}
/*
// 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) {
_messageBuffer = dataString.substring(index);
} else {
_messageBuffer = (backspacesCounter > 0
? _messageBuffer.substring(0, _messageBuffer.length - backspacesCounter)
: _messageBuffer + dataString);
}
*/
}
Future<void> sendData(String output) async {
if (_connection == null) return;
bool nameAvailable = await _cloudServiceAPI.checkNameAvailability(output);
// String output = "0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ";// !§%&()=?#!?";
// String output = "myDevice123";// !§%&()=?#!?";
if (!nameAvailable) return;
id = output;
_connection!.output.add(Uint8List.fromList(const AsciiEncoder().convert("$output \r\n")));
await _connection!.output.allSent;
debugPrint("sent: $output");
}
Future<void> _registerDevice() async {
bool registered = false;
registered = await _cloudServiceAPI.createDevice(id, _primaryThumbprint, "");
String statusText =
registered ? "das Gerät wurde erfolgreich registriert" : "das Gerät konnte nicht registriert werden";
Fluttertoast.showToast(
msg: statusText,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.BOTTOM,
timeInSecForIosWeb: 2,
backgroundColor: Colors.grey[200],
textColor: Colors.black,
fontSize: 16.0);
}
}
/*setState(() {
_discoveryResults[_discoveryResults.indexOf(
result)] =
BluetoothDiscoveryResult(
device: BluetoothDevice(
name: device.name ?? '',
address: address,
type: device.type,
isConnected: connected,
),
rssi: result.rssi);
});
*/

View File

@ -1,32 +1,117 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:http/http.dart';
import '../schemas/device_info_model.dart';
import '../schemas/device_model.dart';
Future<Map<String, dynamic>> readJson() async {
final data = await rootBundle.loadString('config/credentials.json');
var data_ = json.decode(data) as Map<String, dynamic>;
log("loaded json input $data_");
return data_;
final String data = await rootBundle.loadString('config/credentials.json');
final Map<String, dynamic> dataMap = json.decode(data);
log("loaded json input $dataMap");
return dataMap;
}
/*Future<void> checkNameAvailability(String input) async {
class CloudServiceAPI {
static late final Map<String, dynamic> credentials;
static late String _basicAuth;
static late String _address;
static late String _username;
static late String _password;
static late Map<String, String> _headers;
static bool _initState = false;
CloudServiceAPI() {
if (!_initState) {
loadConfig();
_initState = true;
}
}
Future<void> loadConfig() async {
credentials = await readJson();
_username = credentials['username'];
_password = credentials['password'];
_address = credentials['address'];
_basicAuth = 'Basic ${base64.encode(utf8.encode('$_username:$_password'))}';
_headers = {
'authorization': _basicAuth,
'content-type': 'application/json',
'accept': 'application/json',
};
log("init Config $_headers");
}
Future<void> reloadConfig() async {
_basicAuth = 'Basic ${base64.encode(utf8.encode('$_username:$_password'))}';
_headers = {
'authorization': _basicAuth,
'content-type': 'application/json',
'accept': 'application/json',
};
}
Future<List<DeviceModel>> getDevices() async {
Uri url = Uri.https(_address, '/api/devices');
Response responnse = await get(url, headers: _headers);
dynamic jsonObject = json.decode(responnse.body);
Iterable<dynamic> remoteObjectsIterable = Iterable.castFrom(jsonObject);
Iterable<DeviceModel> remoteObjectsMap = remoteObjectsIterable.map((data) => DeviceModel.fromJson(data));
List<DeviceModel> remoteObjectsList = remoteObjectsMap.toList();
return remoteObjectsList;
}
Future<DeviceInfoModel> getDeviceInfo(String deviceID) async {
Uri url = Uri.https(_address, '/api/devices/$deviceID');
Response response = await get(url, headers: _headers);
dynamic jsonObject = json.decode(response.body);
DeviceInfoModel deviceInfoModel = DeviceInfoModel.fromJson(jsonObject);
return deviceInfoModel;
}
Future<Map<String, dynamic>> getInformation() async {
Uri url = Uri.https(_address, '/api/app');
Response response = await get(url, headers: _headers);
return json.decode(response.body) as Map<String, dynamic>;
}
Future<bool> createDevice(String id, String primaryThumbprint, String secondaryThumbprint) async {
Uri url = Uri.https(_address, '/api/devices');
Response response = await post(url,
headers: _headers,
body: jsonEncode(<String, String>{
'id': id,
'primaryThumbprint': primaryThumbprint,
'secondaryThumbprint': secondaryThumbprint
}));
if (response.statusCode == 200) {
return true;
}
debugPrint('Error createDevice: ${response.statusCode.toString()}');
return false;
}
Future<bool> checkNameAvailability(String inputID) 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;
List<DeviceModel> devices = await getDevices();
for (DeviceModel selected in devices) {
if (selected.id == inputID) {
debugPrint("ID 1: ${selected.id}");
debugPrint("ID 2: $inputID");
await showNameAvailabilityStatus(false);
return false;
}
}
await showNameAvailabilityStatus(false);
await showNameAvailabilityStatus(true);
return true;
}
Future<void> showNameAvailabilityStatus(bool status) async {
String statusText = status
? "die eingegebene ID ist verfügbar"
: "die eingegebene ID ist nicht verfügbar";
String statusText = status ? "die eingegebene ID ist verfügbar" : "die eingegebene ID ist nicht verfügbar";
Fluttertoast.showToast(
msg: statusText,
toastLength: Toast.LENGTH_SHORT,
@ -35,97 +120,32 @@ Future<Map<String, dynamic>> readJson() async {
backgroundColor: Colors.grey[200],
textColor: Colors.black,
fontSize: 16.0);
}*/
class CloudServiceAPI {
static late final Map<String, dynamic> credentials;
static late Future loadJson;
static late String basicAuth;
static late String address;
static late String username;
static late String password;
static late Map<String, String> headers;
static bool initState = false;
CloudServiceAPI(){
if(!initState) {
loadJson = loadConfig();
initState = true;
}
}
Future loadConfig() async{
credentials = await readJson();
username = credentials['username'];
password = credentials['password'];
address = credentials['address'];
basicAuth = 'Basic ${base64.encode(utf8.encode('$username:$password'))}';
headers = {
'authorization': basicAuth,
'content-type': 'application/json',
'accept': 'application/json',
};
log("init Config $headers");
String get address {
return _address;
}
void reloadConfig() {
basicAuth = 'Basic ${base64.encode(utf8.encode('$username:$password'))}';
headers = {
'authorization': basicAuth,
'content-type': 'application/json',
'accept': 'application/json',
};
String get username {
return _username;
}
Future<List> getDevices() async {
Uri url = Uri.https(address, '/api/devices');
Response r = await get(url, headers: headers);
return json.decode(r.body) as List<dynamic>;
String get password {
return _password;
}
Future<Map<String, dynamic>> getDeviceInfo(deviceID) async {
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 {
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{
Uri url = Uri.https(address, '/api/devices');
Response r = await post(
url,
headers: headers,
body: jsonEncode(<String, String>{
'id': id,
'primaryThumbprint' : primaryThumbprint,
'secondaryThumbprint' : secondaryThumbprint
})
);
if (r.statusCode == 200){
return true;
}
debugPrint('Error createDevice: ${r.statusCode.toString()}');
return false;
}
String getAddress(){
return address;
}
String getUsername(){
return username;
}
String getPassword(){
return password;
}
void setAddress(String input){
address = input;
set address(String input) {
_address = input;
reloadConfig();
}
void setUsername(String input){
username = input;
set username(String input) {
_username = input;
reloadConfig();
}
void setPassword(String input){
password = input;
set password(String input) {
_password = input;
reloadConfig();
}
}
}

View File

@ -25,4 +25,3 @@ class CustomColor {
return MaterialColor(color.value, swatch);
}
}

View File

@ -0,0 +1,45 @@
class DeviceInfoModel {
final String id;
final String endpoint;
final String status;
final String connectionState;
final String lastActivityTime;
final String primaryThumbprint;
final String secondaryThumbprint;
const DeviceInfoModel({
required this.id,
required this.endpoint,
required this.status,
required this.connectionState,
required this.lastActivityTime,
required this.primaryThumbprint,
required this.secondaryThumbprint,
});
factory DeviceInfoModel.fromJson(Map<String, dynamic> parsedJson) {
return DeviceInfoModel(
id: parsedJson['id'].toString(),
endpoint: parsedJson['endpoint'],
status: parsedJson['status'],
connectionState: parsedJson['connectionState'],
lastActivityTime: parsedJson['lastActivityTime'],
primaryThumbprint: parsedJson['primaryThumbprint'],
secondaryThumbprint: parsedJson['secondaryThumbprint']);
}
@override
String toString() {
return "{"
"id: $id, "
"endpoint: $endpoint, "
"status: $status, "
"connectionState: $connectionState, "
"lastActivityTime: $lastActivityTime, "
"primaryThumbprint: $primaryThumbprint, "
"secondaryThumbprint: $secondaryThumbprint"
"}";
}
}

View File

@ -0,0 +1,36 @@
class DeviceModel {
final String id;
final String endpoint;
final String status;
final String connectionState;
final String lastActivityTime;
const DeviceModel(
{required this.id,
required this.endpoint,
required this.status,
required this.connectionState,
required this.lastActivityTime});
factory DeviceModel.fromJson(Map<String, dynamic> parsedJson) {
return DeviceModel(
id: parsedJson['id'].toString(),
endpoint: parsedJson['endpoint'],
status: parsedJson['status'],
connectionState: parsedJson['connectionState'],
lastActivityTime: parsedJson['lastActivityTime']);
}
@override
String toString() {
return "{"
"id: $id, "
"endpoint: $endpoint, "
"status: $status, "
"connectionState: $connectionState, "
"lastActivityTime: $lastActivityTime, "
"}";
}
}

View File

@ -1,73 +0,0 @@
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);
}
}
}

View File

@ -1,107 +1,121 @@
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
import 'package:flutter/material.dart';
class BluetoothDeviceSettings extends StatefulWidget {
final BluetoothDevice device;
import '../objects/bluetooth_object.dart';
import '../widgets/single_section.dart';
const BluetoothDeviceSettings({super.key, required this.device});
class BluetoothDeviceSettings extends StatefulWidget {
final BluetoothObject bluetoothObject;
const BluetoothDeviceSettings({super.key, required this.bluetoothObject});
@override
State<BluetoothDeviceSettings> createState() => _BluetoothDeviceSettings();
State<StatefulWidget> createState() => BluetoothDeviceSettingsState();
}
class _BluetoothDeviceSettings extends State<BluetoothDeviceSettings> {
class BluetoothDeviceSettingsState extends State<BluetoothDeviceSettings> {
late BluetoothObject _bluetoothObject;
String _messageBuffer = "";
@override
void initState() {
super.initState();
_bluetoothObject = widget.bluetoothObject;
}
String _textInput = "0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ";
String _textOuput = "";
ButtonStyle buttonStyle = ElevatedButton.styleFrom(
foregroundColor: Colors.black,
backgroundColor: const Color(0xFFFDE100), // Text Color (Foreground color)
);
@override
Widget build(BuildContext context) {
debugPrint(widget.device.address);
return Scaffold(
appBar: AppBar(
title: const Text("Bluetooth Devices"),
title: const Text("Device Settings"),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
body: Center(
child: ListView(children: [
Column(children: <Widget>[
SingleSection(
title: "Device Information",
children: [
Table(
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
children: <TableRow>[
TableRow(
decoration: const BoxDecoration(
border: Border(
bottom:
BorderSide(width: 1.5, color: Colors.grey),
),
),
children: [
const TableCell(child: Text("Address")),
TableCell(
child: Container(
alignment: Alignment.centerLeft,
height: 50,
child: Text(
widget.device.address
)
)
),
]),
TableRow(
decoration: const BoxDecoration(
border: Border(
bottom:
BorderSide(width: 1.5, color: Colors.grey),
),
),
children: [
TableCell(
child: Container(
alignment: Alignment.centerLeft,
height: 50,
child: const Text(
"Name"
)
)
),
TableCell(
child: Container(
alignment: Alignment.centerLeft,
height: 50,
child: Text(
widget.device.name.toString()
)
)
),
]),
TableRow(
decoration: const BoxDecoration(
border: Border(
bottom:
BorderSide(width: 1.5, color: Colors.grey),
),
),
children: [
TableCell(
child: Container(
alignment: Alignment.centerLeft,
height: 50,
child: const Text(
"Connected"
)
)
),
TableCell(
child: Container(
alignment: Alignment.centerLeft,
height: 50,
child: Text(
widget.device.isConnected.toString()
)
)
),
]),
],
ListTile(
title: const Text("Device Name"),
subtitle: Text(_bluetoothObject.name),
),
])));
ListTile(
title: const Text("Device Adress"),
subtitle: Text(_bluetoothObject.address),
),
ListTile(
title: const Text("Device Connection State"),
subtitle:
Text(_bluetoothObject.isConnected ? "ConnectionState.DISCONECTED" : "ConnectionState.CONNECED"),
),
],
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
// const EdgeInsets defaultContentPadding = EdgeInsets.symmetric(horizontal: 16.0);
Expanded(
child: ElevatedButton(
onPressed: () async {
if (!_bluetoothObject.isConnected) {
await _bluetoothObject.connectDevice();
}
},
style: buttonStyle,
child: const Text("Connect"),
)),
const SizedBox(width: 16.0),
Expanded(
child: ElevatedButton(
onPressed: () async {
if (_bluetoothObject.isConnected) {
await _bluetoothObject.disconnectDevice();
}
},
style: buttonStyle,
child: const Text("Disconnect"),
))
])),
const Divider(),
ElevatedButton(
onPressed: () async {
_bluetoothObject.sendData(_textInput);
},
style: buttonStyle,
child: const Text("Send Data"),
),
ElevatedButton(
onPressed: () async {
setState(() {
_textOuput = "";
});
},
style: buttonStyle,
child: const Text("Clear Output"),
),
Text(_textOuput),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0),
child: TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter the device name',
),
onChanged: (String newValue) {
_textInput = newValue;
},
),
),
]),
]),
));
}
}

View File

@ -1,16 +1,15 @@
import 'dart:async';
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:flutter/material.dart';
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
import 'package:permission_handler/permission_handler.dart';
import '../objects/bluetooth_device_entry.dart';
import '../objects/bluetooth_object.dart';
import '../screens/bluetooth_device_settings.dart';
import '../widgets/custom_list_tile.dart';
import '../widgets/single_section.dart';
class BluetoothScreen extends StatefulWidget {
final bool start = true;
@ -23,36 +22,34 @@ class BluetoothScreen extends StatefulWidget {
// debugPrint("r: ${r.device.name} ${r.device.address} ${r.rssi}");
class _BluetoothScreen extends State<BluetoothScreen> {
final List<BluetoothObject> _discoveryResults = [];
CloudServiceAPI cloudServiceAPI = CloudServiceAPI();
//BluetoothManager bluetoothManager = BluetoothManager();
late BluetoothObject? _activeObject;
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;
String textInput = "0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ";
String textOuput = "";
late Stream<BluetoothDiscoveryResult>? _stream;
late StreamSubscription<BluetoothDiscoveryResult>? _streamSubscription;
bool _isDiscovering = false;
bool _enabledPermissions = false;
bool _enabledBluetooth = false;
ButtonStyle buttonStyle = ElevatedButton.styleFrom(
foregroundColor: Colors.black,
backgroundColor: const Color(0xFFFDE100), // Text Color (Foreground color)
);
bool get _hasActiveObject {
return _activeObject == null ? false : true;
}
@override
void initState() {
_enablePermissions();
_enableBluetooth();
super.initState();
isDiscovering = widget.start;
if (isDiscovering) {
_activeObject = null;
_streamSubscription = null;
if (widget.start) {
_startDiscovery();
}
}
@ -60,522 +57,216 @@ class _BluetoothScreen extends State<BluetoothScreen> {
@override
void dispose() {
// Avoid memory leak (`setState` after dispose) and cancel discovery
_streamSubscription.cancel();
if (isConnected) {
isDisconnecting = true;
_bluetoothConnection?.dispose();
_bluetoothConnection = null;
}
_streamSubscription?.cancel();
_activeObject?.disconnectDevice();
super.dispose();
debugPrint("called 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");
Future<void> _enablePermissions() async {
debugPrint("Test");
PermissionStatus bluetoothScan = await Permission.bluetoothScan.request();
PermissionStatus bluetoothConnect = await Permission.bluetoothConnect.request();
bool granted = bluetoothScan.isGranted && bluetoothConnect.isGranted;
setState(() {
isConnecting = false;
isDisconnecting = false;
_enabledPermissions = granted;
});
_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!");
Future<void> _enableBluetooth() async {
if (!_enabledPermissions) {
return;
}
if (mounted) {
setState(() {});
}
}
void _connectionOnListen(Uint8List data) {
debugPrint("received: $data");
debugPrint("received decoded: ${const Utf8Decoder().convert(data)}");
bool? enabled = await FlutterBluetoothSerial.instance.requestEnable();
enabled ??= false;
setState(() {
textOuput += const Utf8Decoder().convert(data);
_enabledBluetooth = enabled!;
});
// 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 {
if (!_enabledPermissions) {
return;
}
setState(() => _isDiscovering = true);
_stream = FlutterBluetoothSerial.instance.startDiscovery();
_streamSubscription = _stream.listen(_discoveryOnListen);
_streamSubscription.onDone(_discoveryOnDone);
_streamSubscription = _stream!.listen(_discoveryOnListen);
_streamSubscription!.onDone(_discoveryOnDone);
}
void _discoveryOnDone() {
setState(() {
isDiscovering = false;
//debugPrint(isDiscovering as String?);
});
Future<void> _discoveryOnDone() async {
setState(() => _isDiscovering = false);
}
void _discoveryOnListen(BluetoothDiscoveryResult event) {
Future<void> _cancelDiscovery() async {
await _streamSubscription?.cancel();
setState(() => _isDiscovering = false);
}
Future<void> _discoveryOnListen(BluetoothDiscoveryResult event) async {
setState(() {
final int existingIndex = _discoveryResults.indexWhere(
(element) => element.device.address == event.device.address);
final int existingIndex =
_discoveryResults.indexWhere((element) => element.device.address == event.device.address);
if (existingIndex >= 0) {
_discoveryResults[existingIndex] = event;
_discoveryResults[existingIndex] = BluetoothObject(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;
_discoveryResults.add(BluetoothObject(event));
}
});
}
Future<void> _restartDiscovery() async {
setState(() {
_discoveryResults.clear();
isDiscovering = true;
});
_startDiscovery();
_discoveryResults.clear();
if (_hasActiveObject) {
_discoveryResults.add(_activeObject!);
}
if (_streamSubscription != null) {
await _cancelDiscovery();
}
await _startDiscovery();
}
Future<void> _sendData() async {
//String output = "0123456789 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ";// !§%&()=?#!?";
String output = textInput;
_bluetoothConnection!.output.add(Uint8List.fromList(const AsciiEncoder().convert("$output \r\n")));
await _bluetoothConnection!.output.allSent;
debugPrint("sent: $output");
Future<void> _toggleConnection(BluetoothObject bluetoothObject) async {
await _cancelDiscovery();
if (_hasActiveObject) {
_activeObject!.disconnectDevice();
_activeObject = bluetoothObject;
} else {
_activeObject = bluetoothObject;
}
_activeObject!.isConnected ? await bluetoothObject.disconnectDevice() : await bluetoothObject.connectDevice();
}
@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,
)
],
),
//stty -F /dev/service 19200 parenb -parodd -cstopb cs8
//cat /dev/service | xargs -n 1 /home/script/automaticcloud.sh
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(),
Text(textOuput),
TextFormField(
decoration: const InputDecoration(
labelText: "BluetoothText"
),
initialValue: textInput,
keyboardType: TextInputType.text,
onChanged: (String newValue) {
textInput = newValue;
},
),
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 {
setState(() {
textOuput = "";
});
},
style: buttonStyle,
child: const Text("Clear Output"),
),
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(
appBar: AppBar(
title: const Text("Bluetooth Devices"),
),
body: RefreshIndicator(
onRefresh: () {
debugPrint("refreshed");
return Future(() => null);
},
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
//child: Padding(
//padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(width: 1.5, color: Colors.grey),
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),
),
),
),
child: Column(
)
: IconButton(
icon: const Icon(Icons.replay),
onPressed: _restartDiscovery,
)
],
),
body: Center(
child: ListView(children: [
SingleChildScrollView(
physics: const ScrollPhysics(),
child: Column(
children: <Widget>[
SingleSection(
title: "Setup",
children: [
const Text(
"General",
textAlign: TextAlign.start,
CustomListTile(
title: _enabledBluetooth ? "Bluetooth Enabled" : "Please Enable Bluetooth (click me)",
icon: _enabledBluetooth ? Icons.check_circle_outline_rounded : Icons.info_outline_rounded,
onTap: () async {
await _enableBluetooth();
},
),
Row(
children: [
_CustomListTile(
title: "Dark Mode",
icon: CupertinoIcons.moon,
trailing:
CupertinoSwitch(value: false, onChanged: (value) {})),
]
CustomListTile(
title: _enabledPermissions ? "Permissions Granted" : "Please Grant Permissions (click me)",
icon: _enabledPermissions ? Icons.check_circle_outline_rounded : Icons.info_outline_rounded,
onTap: () async {
await _enablePermissions();
},
),
TextField(
controller: textFieldValueHolder,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter the name of your new device',
),
),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
var textFieldValue = textFieldValueHolder.value.text;
checkNameAvailability(textFieldValue);
setState(() {
inputName = textFieldValue;
});
},
style: buttonStyle,
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"),
),
CustomListTile(
title: "App Settings (if permissions denied)",
icon: Icons.settings,
onTap: () async {
await AppSettings.openAppSettings();
},
),
],
),
),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(width: 1.5, color: Colors.grey),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
// const EdgeInsets defaultContentPadding = EdgeInsets.symmetric(horizontal: 16.0);
Expanded(
child: 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("Get BT Permissions"),
),
),
const SizedBox(width: 16.0),
Expanded(
child: ElevatedButton(
onPressed: () async {
_restartDiscovery();
},
style: buttonStyle,
child: const Text("Restart Scan"),
),
)
]),
),
child: Row(children: [
const Text(
"Toggle Scan",
style: TextStyle(fontWeight: FontWeight.bold),
),
const Expanded(child: Text("")),
Switch(
value: widgetScanState,
onChanged: (bool toggleState) {
scanState = toggleState;
log("widget state: $scanState");
setState(() {
widgetScanState = toggleState;
});
})
]),
),
// BluetoothDiscovery(start: initScan, deviceID: inputName),
],
),
//),
),
),
);
}*/
}
class _CustomListTile extends StatelessWidget {
final String title;
final IconData icon;
final VoidCallback? onTap;
//final Widget? trailing;
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 _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),
const Divider(),
SingleSection(
title: "Bluetooth Devices",
children: [
const CustomListTile(
title: "short Press for connect / disconnect",
icon: Icons.info_outline_rounded,
),
const CustomListTile(
title: "long press for pair / unpair",
icon: Icons.info_outline_rounded,
),
const Divider(),
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: _discoveryResults.length,
itemBuilder: (BuildContext context, index) {
BluetoothObject bluetoothObject = _discoveryResults[index];
final device = bluetoothObject.device;
return BluetoothDeviceEntry(
settingsPage: BluetoothDeviceSettings(bluetoothObject: bluetoothObject),
context: context,
device: device,
bluetoothObject: bluetoothObject,
onTap: () async {
await _toggleConnection(bluetoothObject);
},
onLongPress: () async {
await bluetoothObject.bondDevice();
},
);
},
),
],
)
],
),
),
Column(
children: children,
),
],
]),
),
//drawer: const Sidebar(),
);
}
}

View File

@ -1,163 +0,0 @@
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();
},
),
],
);
},
);
}
},
);
},
),
);
}
}

View File

@ -1,28 +1,28 @@
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_provisioning_for_iot/objects/cloud_service_api.dart';
import 'package:flutter_provisioning_for_iot/widgets/sidebar.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../objects/cloud_service_api.dart';
import '../widgets/sidebar.dart';
Future<Map<String, dynamic>> readJson() async {
final String data = await rootBundle.loadString('config/credentials.json');
final Map<String, dynamic> dataMap = json.decode(data);
log("loaded json input $dataMap");
return dataMap;
}
class CloudService extends StatefulWidget {
const CloudService({Key? key}) : super(key: key);
@override
State<CloudService> createState() => _CloudService();
}
Future<Map<String, dynamic>> readJson() async {
final data = await rootBundle.loadString('config/credentials.json');
var data_ = json.decode(data) as Map<String, dynamic>;
debugPrint(data_.toString());
return data_;
}
class _CloudService extends State<CloudService>{
class _CloudService extends State<CloudService> {
late final Map<String, dynamic> credentials;
CloudServiceAPI cloudServiceAPI = CloudServiceAPI();
static SharedPreferences? preferencesInstance;
@ -35,18 +35,27 @@ class _CloudService extends State<CloudService>{
),
body: Center(
child: TextButton(
onPressed: () async{
var respond1 = await cloudServiceAPI.getDevices();
debugPrint('Devices: ${respond1.toString()}');
var respond2 = await cloudServiceAPI.getInformation();
debugPrint('Information: ${respond2.toString()}');
var respond3 = await cloudServiceAPI.createDevice('1', 'asdas', 'sdwe1');
debugPrint('CreateDevice: ${respond3.toString()}');
}, child: const Text("Example"),
onPressed: () async {
// List<BluetoothObjectRemote> respond1 = await cloudServiceAPI.getDevices();
// debugPrint('Devices: ${respond1[0].toString()}');
// debugPrint('Devices: ${respond1[0].toString()}');
//dynamic respond2 = await cloudServiceAPI.getInformation();
//debugPrint('Information: ${respond2.toString()}');
// dynamic respond3 = await cloudServiceAPI.createDevice('1', 'asdas', 'sdwe1');
// debugPrint('CreateDevice: ${respond3.toString()}');*/
//dynamic respond4 = await cloudServiceAPI.getDeviceInfo("PFC200V3-430EB3");
//debugPrint('Information: ${respond4.toString()}');
//bool respond5 = await cloudServiceAPI.createDevice("PFC200V3-430EB2", "60987668030DF277AD7706D7C1FA683F37230D202A80EE5135B05EF928E3BF88", "");
//debugPrint('Information: ${respond5.toString()}');
},
child: const Text("Example"),
),
),
drawer: const Sidebar(),
);
}
}
}

View File

@ -1,77 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_provisioning_for_iot/objects/cloud_service_api.dart';
import 'package:flutter_provisioning_for_iot/widgets/sidebar.dart';
import '../objects/create_material_color.dart';
class MainPage extends StatelessWidget {
const MainPage({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Provisioning for IOT',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: CustomColor.createMaterialColor(const Color(0xff263f8c)),
),
home: const MyHomePage(title: 'Provisioning for IOT'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
CloudServiceAPI cloudServiceAPI = CloudServiceAPI();
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
drawer: const Sidebar(),// This trailing comma makes auto-formatting nicer for build methods.
);
}
}

View File

@ -0,0 +1,70 @@
import 'package:flutter/material.dart';
import '../objects/create_material_color.dart';
import '../widgets/sidebar.dart';
class MainScreen extends StatelessWidget {
const MainScreen({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
const String appName = "Provisioning for IOT";
return MaterialApp(
title: appName,
theme: ThemeData(
primarySwatch: CustomColor.createMaterialColor(const Color(0xff263f8c)),
brightness: Brightness.light,
),
home: const HomeScreen(title: appName),
//debugShowCheckedModeBanner: false,
);
}
}
class HomeScreen extends StatefulWidget {
final String title;
const HomeScreen({super.key, required this.title});
@override
State<HomeScreen> createState() => _HomeScreen();
}
class _HomeScreen extends State<HomeScreen> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
"Die HFU präsentiert:",
),
Text(
"Cloud Provisioning for IOT Devices",
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(
width: 10.0,
height: 50.0,
),
const Image(image: AssetImage('assets/device/PFC200.jpg')),
],
),
),
drawer: const Sidebar(), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}

View File

@ -0,0 +1,118 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_provisioning_for_iot/schemas/device_info_model.dart';
import '../objects/cloud_service_api.dart';
// import 'package:shared_preferences/shared_preferences.dart';
import '../schemas/device_model.dart';
import '../widgets/custom_list_tile.dart';
import '../widgets/sidebar.dart';
import '../widgets/single_section.dart';
Future<Map<String, dynamic>> readJson() async {
final data = await rootBundle.loadString('config/credentials.json');
var data_ = json.decode(data) as Map<String, dynamic>;
debugPrint(data_.toString());
return data_;
}
class RegisteredDevicesScreen extends StatefulWidget {
const RegisteredDevicesScreen({Key? key}) : super(key: key);
@override
State<RegisteredDevicesScreen> createState() => _RegisteredDevicesScreen();
}
class _RegisteredDevicesScreen extends State<RegisteredDevicesScreen> {
late List<DeviceInfoModel>? _registeredInfoDevices;
// late final Map<String, dynamic> _credentials;
final CloudServiceAPI _cloudServiceAPI = CloudServiceAPI();
// static SharedPreferences? preferencesInstance;
bool _isFetching = false;
Future<void> _fetchRegisteredDevices() async {
setState(() {
_isFetching = true;
});
_registeredInfoDevices == null
? _registeredInfoDevices = List.empty(growable: true)
: _registeredInfoDevices!.clear();
List<DeviceModel>? registeredDevices = await _cloudServiceAPI.getDevices();
for (DeviceModel deviceModel in registeredDevices) {
String deviceID = deviceModel.id;
DeviceInfoModel deviceInfoModel = await _cloudServiceAPI.getDeviceInfo(deviceID);
setState(() {
_registeredInfoDevices!.add(deviceInfoModel);
});
}
setState(() {
_isFetching = false;
});
}
@override
void initState() {
_registeredInfoDevices = null;
_fetchRegisteredDevices();
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: _isFetching ? const Text('Registered Devices (searching...)') : const Text('Registered Devices'),
actions: <Widget>[
_isFetching
? FittedBox(
child: Container(
margin: const EdgeInsets.all(16.0),
child: const CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
)
: IconButton(
icon: const Icon(Icons.replay),
onPressed: _fetchRegisteredDevices,
)
],
),
body: Center(
child: ListView(children: [
SingleChildScrollView(
physics: const ScrollPhysics(),
child: Column(
children: <Widget>[
SingleSection(
title: "Registered Bluetooth Devices",
children: [
ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: _registeredInfoDevices == null ? 0 : _registeredInfoDevices!.length,
itemBuilder: (BuildContext context, index) {
DeviceInfoModel entry = _registeredInfoDevices![index];
//DeviceInfoModel entryInfo = await _cloudServiceAPI.getDeviceInfo(entry.id);
return CustomListTile(
title: entry.id,
icon: Icons.devices,
);
},
),
],
)
],
),
),
]),
),
drawer: const Sidebar(),
);
}
}

View File

@ -2,24 +2,33 @@ import 'package:flutter/material.dart';
import 'package:flutter_provisioning_for_iot/objects/cloud_service_api.dart';
import '../widgets/sidebar.dart';
import '../widgets/single_section.dart';
class Settings extends StatefulWidget {
const Settings({Key? key}) : super(key: key);
static CloudServiceAPI cloudService = CloudServiceAPI();
static String address = cloudService.getAddress();
static String username = cloudService.getUsername();
static String password = cloudService.getPassword();
static String address = cloudService.address;
static String username = cloudService.username;
static String password = cloudService.password;
@override
State<Settings> createState() => _SettingsState();
/// Initializes the Settings with the correct Values from the last session
static Future initSettings() async {
}
static Future initSettings() async {}
}
class _SettingsState extends State<Settings> {
bool _passwordVisible = false;
@override
void initState() {
debugPrint("PASSWORD ${Settings.password}");
super.initState();
_passwordVisible = false;
}
@override
Widget build(BuildContext context) {
return Scaffold(
@ -29,40 +38,80 @@ class _SettingsState extends State<Settings> {
title: const Text("Settings"),
),
body: Center(
child: Column(
children: [
TextFormField(
decoration: const InputDecoration(
labelText: "Address for the Device Manager"),
initialValue: Settings.address,
keyboardType: TextInputType.text,
onChanged: (String newValue) {
Settings.address = newValue;
Settings.cloudService.setAddress(newValue);
},
),
TextFormField(
decoration: const InputDecoration(
labelText: "Username"),
keyboardType: TextInputType.text,
initialValue: Settings.username,
onChanged: (String newValue) {
Settings.username = newValue;
Settings.cloudService.setUsername(newValue);
},
),
TextFormField(
decoration: const InputDecoration(
labelText: "Password"),
keyboardType: TextInputType.text,
initialValue: Settings.password,
onChanged: (String newValue) {
Settings.password = newValue;
Settings.cloudService.setPassword(newValue);
},
)
],
),
child: ListView(children: [
SingleChildScrollView(
physics: const ScrollPhysics(),
child: Column(children: <Widget>[
SingleSection(
title: "Server Settings",
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: TextFormField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter the server url (no https:// and paths)',
labelText: "Address for the Device Manager",
),
initialValue: Settings.address,
keyboardType: TextInputType.text,
onChanged: (String newValue) {
Settings.address = newValue;
Settings.cloudService.address = newValue;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: TextFormField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter the server username',
labelText: "Username",
),
initialValue: Settings.username,
keyboardType: TextInputType.text,
onChanged: (String newValue) {
Settings.username = newValue;
Settings.cloudService.username = newValue;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: TextFormField(
obscureText: _passwordVisible ? false : true,
decoration: InputDecoration(
border: const OutlineInputBorder(),
hintText: 'Enter the server password',
labelText: "Password",
suffixIcon: IconButton(
icon: Icon(_passwordVisible ? Icons.visibility : Icons.visibility_off),
onPressed: () {
setState(() {
_passwordVisible = !_passwordVisible;
});
},
),
filled: true,
),
initialValue: Settings.password,
keyboardType: TextInputType.text,
onChanged: (String newValue) {
Settings.password = newValue;
Settings.cloudService.password = newValue;
},
),
),
],
),
const SingleSection(
title: "Theme Settings",
children: <Widget>[],
)
]),
)
]),
),
drawer: const Sidebar());
}

View File

@ -1,158 +0,0 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_bluetooth_serial/flutter_bluetooth_serial.dart';
import 'package:flutter_provisioning_for_iot/screens/bluetooth_device_settings.dart';
class BluetoothDiscovery extends StatefulWidget {
final bool start;
final String deviceID;
const BluetoothDiscovery({
super.key,
required this.start,
required this.deviceID
});
@override
State<BluetoothDiscovery> createState() => _BluetoothDiscovery();
}
class _BluetoothDiscovery extends State<BluetoothDiscovery> {
StreamSubscription<BluetoothDiscoveryResult>? _streamSubscription;
List<BluetoothDiscoveryResult> results = List<BluetoothDiscoveryResult>.empty(growable: true);
bool isDiscovering = false;
@override
Widget build(BuildContext context) {
debugPrint("test: ${widget.deviceID}");
debugPrint("bool: ${widget.start}");
if(widget.start) {
_startDiscovery();
}
return RefreshIndicator(
onRefresh: () {
debugPrint("refreshed");
return Future(() => null);
},
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: results.length,
itemExtent: 50.0,
itemBuilder:
(BuildContext context, int index) {
var device = results.elementAt(index).device;
var deviceAddress = device.address;
var deviceName = device.name;
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
const Icon(Icons.bluetooth_disabled_rounded),
TextButton(
onPressed: () {
debugPrint(deviceAddress);
},
onLongPress: () {
debugPrint("no need to press for this long...");
},
child: Text("$deviceAddress"
"${(deviceName != "" ? "" : " | $deviceName")}")),
const Expanded(child: Text("")),
IconButton(
icon: const Icon(Icons.settings),
onPressed: () {
debugPrint("this is a warning!");
debugPrint(device.address);
Navigator.push(context, MaterialPageRoute(builder: (context) => BluetoothDeviceSettings(device: device)));
},
),
],
);
},
)
);
}
@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);
}
});
});
debugPrint("results: $results");
_streamSubscription!.onDone(() {
setState(() {
isDiscovering = false;
});
});
}
@override
void dispose() {
// Avoid memory leak (`setState` after dispose) and cancel discovery
_streamSubscription?.cancel();
super.dispose();
}
void initScan() {
_streamSubscription =
FlutterBluetoothSerial.instance.startDiscovery().listen((r) {
debugPrint("r: ${r.device.name} ${r.device.address}");
setState(() {
final existingIndex = results.indexWhere(
(element) => element.device.address == r.device.address);
if (existingIndex >= 0) {
results[existingIndex] = r;
} else {
results.add(r);
}
});
if (r.device.address == "38:F3:2E:41:82:74") {
BluetoothConnection.toAddress(r.device.address).then((connection) {
debugPrint('Connected to the device');
//connection = _connection;
//setState(() {
//isConnecting = false;
//isDisconnecting = false;
});
}
});
_streamSubscription!.onDone(() {
setState(() {
isDiscovering = false;
});
});
}
}

View File

@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
class CustomListTile extends StatelessWidget {
final String title;
final IconData icon;
final VoidCallback? onTap;
//final Widget? trailing;
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,
);
}
}

View File

@ -1,54 +1,62 @@
import 'package:flutter/material.dart';
import 'package:flutter_provisioning_for_iot/screens/bluetooth_screen.dart';
import 'package:flutter_provisioning_for_iot/screens/cloud_service_ui.dart';
import '../screens/main_page.dart';
import '../screens/bluetooth_screen.dart';
import '../screens/cloud_service_ui.dart';
import '../screens/main_screen.dart';
import '../screens/registered_devices_screen.dart';
import '../screens/settings.dart';
class Sidebar extends StatelessWidget {
const Sidebar({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Drawer(
//backgroundColor: Colors.black ,
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
const DrawerHeader(
decoration: BoxDecoration(
color: Colors.white
),
decoration: BoxDecoration(color: Colors.white),
child: Center(
child: Image(
image: AssetImage('assets/logo/m&m_logo.png')
),
child: Image(image: AssetImage('assets/logo/m&m_logo.png')),
),
),
ListTile(
title: const Text("Main Page"),
leading: const Icon(Icons.home),
title: const Text("Home"),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => const MainPage()));
Navigator.push(context, MaterialPageRoute(builder: (context) => const MainScreen()));
},
),
ListTile(
title: const Text("Settings"),
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => const Settings()));
},
),
ListTile(
title: const Text("Bluetooth Test"),
leading: const Icon(Icons.bluetooth_searching),
title: const Text("Connect Device"),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => const BluetoothScreen()));
},
),
ListTile(
leading: const Icon(Icons.devices),
title: const Text("Registered Devices"),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => const RegisteredDevicesScreen()));
},
),
ListTile(
leading: const Icon(Icons.settings),
title: const Text("Settings"),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => const Settings()));
},
),
ListTile(
leading: const Icon(Icons.cloud),
title: const Text("Cloud Service"),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => const CloudService()));
},
)
),
],
),
);

View File

@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
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,
),
],
);
}
}

View File

@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
class SwitchWidget extends StatefulWidget {
const SwitchWidget({super.key});
@override
@ -9,7 +8,6 @@ class SwitchWidget extends StatefulWidget {
}
class _SwitchWidget extends State<SwitchWidget> {
bool switchControl = false;
var textHolder = 'Switch is OFF';
@ -41,4 +39,4 @@ class _SwitchWidget extends State<SwitchWidget> {
inactiveTrackColor: Colors.grey,
);
}
}
}

View File

@ -74,6 +74,7 @@ flutter:
assets:
- config/credentials.json
- assets/logo/m&m_logo.png
- assets/device/PFC200.jpg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware

View File

@ -6,7 +6,7 @@
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_provisioning_for_iot/screens/main_page.dart';
import 'package:flutter_provisioning_for_iot/screens/main_screen.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_provisioning_for_iot/main.dart';