I have a Question to Flutter with Bluetooth.
I Use the Flutter_blue package https://pub.dev/packages/flutter_blue
How I can write data external over a Future or a Void.
I use the code of the documentation Example of Flutter_blue.
Just I will extend this with my own void to write data every 5 seconds.
Here is my code that I want to control
import 'dart:convert';
import 'package:flutter_blue/flutter_blue.dart';
class SecondBlue {
BluetoothCharacteristic targetCharacteristic;
SecondBlue(this.targetCharacteristic);
writeData(String data) async {
if (targetCharacteristic == null) return;
List<int> bytes = utf8.encode(data);
await targetCharacteristic.write(bytes);
}
}
i would like after the first bluetooth part provides the connection
write data about this part at any time.
here is the code of flutter_blue to connect the device.
The main Part:
// Copyright 2017, Paul DeMarco.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:flutter_blue_example/widgets.dart';
void main() {
runApp(FlutterBlueApp());
}
class FlutterBlueApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
color: Colors.lightBlue,
home: StreamBuilder<BluetoothState>(
stream: FlutterBlue.instance.state,
initialData: BluetoothState.unknown,
builder: (c, snapshot) {
final state = snapshot.data;
if (state == BluetoothState.on) {
return FindDevicesScreen();
}
return BluetoothOffScreen(state: state);
}),
);
}
}
class BluetoothOffScreen extends StatelessWidget {
const BluetoothOffScreen({Key? key, this.state}) : super(key: key);
final BluetoothState? state;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.lightBlue,
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(
Icons.bluetooth_disabled,
size: 200.0,
color: Colors.white54,
),
Text(
'Bluetooth Adapter is ${state != null ? state.toString().substring(15) : 'not available'}.',
style: Theme.of(context)
.primaryTextTheme
.subhead
?.copyWith(color: Colors.white),
),
],
),
),
);
}
}
class FindDevicesScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Find Devices'),
),
body: RefreshIndicator(
onRefresh: () =>
FlutterBlue.instance.startScan(timeout: Duration(seconds: 4)),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
StreamBuilder<List<BluetoothDevice>>(
stream: Stream.periodic(Duration(seconds: 2))
.asyncMap((_) => FlutterBlue.instance.connectedDevices),
initialData: [],
builder: (c, snapshot) => Column(
children: snapshot.data!
.map((d) => ListTile(
title: Text(d.name),
subtitle: Text(d.id.toString()),
trailing: StreamBuilder<BluetoothDeviceState>(
stream: d.state,
initialData: BluetoothDeviceState.disconnected,
builder: (c, snapshot) {
if (snapshot.data ==
BluetoothDeviceState.connected) {
return RaisedButton(
child: Text('OPEN'),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
DeviceScreen(device: d))),
);
}
return Text(snapshot.data.toString());
},
),
))
.toList(),
),
),
StreamBuilder<List<ScanResult>>(
stream: FlutterBlue.instance.scanResults,
initialData: [],
builder: (c, snapshot) => Column(
children: snapshot.data!
.map(
(r) => ScanResultTile(
result: r,
onTap: () => Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
r.device.connect();
return DeviceScreen(device: r.device);
})),
),
)
.toList(),
),
),
],
),
),
),
floatingActionButton: StreamBuilder<bool>(
stream: FlutterBlue.instance.isScanning,
initialData: false,
builder: (c, snapshot) {
if (snapshot.data!) {
return FloatingActionButton(
child: Icon(Icons.stop),
onPressed: () => FlutterBlue.instance.stopScan(),
backgroundColor: Colors.red,
);
} else {
return FloatingActionButton(
child: Icon(Icons.search),
onPressed: () => FlutterBlue.instance
.startScan(timeout: Duration(seconds: 4)));
}
},
),
);
}
}
class DeviceScreen extends StatelessWidget {
const DeviceScreen({Key? key, required this.device}) : super(key: key);
final BluetoothDevice device;
List<int> _getRandomBytes() {
final math = Random();
return [
math.nextInt(255),
math.nextInt(255),
math.nextInt(255),
math.nextInt(255)
];
}
List<Widget> _buildServiceTiles(List<BluetoothService> services) {
return services
.map(
(s) => ServiceTile(
service: s,
characteristicTiles: s.characteristics
.map(
(c) => CharacteristicTile(
characteristic: c,
onReadPressed: () => c.read(),
onWritePressed: () async {
await c.write(_getRandomBytes(), withoutResponse: true);
await c.read();
},
onNotificationPressed: () async {
await c.setNotifyValue(!c.isNotifying);
await c.read();
},
descriptorTiles: c.descriptors
.map(
(d) => DescriptorTile(
descriptor: d,
onReadPressed: () => d.read(),
onWritePressed: () => d.write(_getRandomBytes()),
),
)
.toList(),
),
)
.toList(),
),
)
.toList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(device.name),
actions: <Widget>[
StreamBuilder<BluetoothDeviceState>(
stream: device.state,
initialData: BluetoothDeviceState.connecting,
builder: (c, snapshot) {
VoidCallback? onPressed;
String text;
switch (snapshot.data) {
case BluetoothDeviceState.connected:
onPressed = () => device.disconnect();
text = 'DISCONNECT';
break;
case BluetoothDeviceState.disconnected:
onPressed = () => device.connect();
text = 'CONNECT';
break;
default:
onPressed = null;
text = snapshot.data.toString().substring(21).toUpperCase();
break;
}
return FlatButton(
onPressed: onPressed,
child: Text(
text,
style: Theme.of(context)
.primaryTextTheme
.button
?.copyWith(color: Colors.white),
));
},
)
],
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
StreamBuilder<BluetoothDeviceState>(
stream: device.state,
initialData: BluetoothDeviceState.connecting,
builder: (c, snapshot) => ListTile(
leading: (snapshot.data == BluetoothDeviceState.connected)
? Icon(Icons.bluetooth_connected)
: Icon(Icons.bluetooth_disabled),
title: Text(
'Device is ${snapshot.data.toString().split('.')[1]}.'),
subtitle: Text('${device.id}'),
trailing: StreamBuilder<bool>(
stream: device.isDiscoveringServices,
initialData: false,
builder: (c, snapshot) => IndexedStack(
index: snapshot.data! ? 1 : 0,
children: <Widget>[
IconButton(
icon: Icon(Icons.refresh),
onPressed: () => device.discoverServices(),
),
IconButton(
icon: SizedBox(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Colors.grey),
),
width: 18.0,
height: 18.0,
),
onPressed: null,
)
],
),
),
),
),
StreamBuilder<int>(
stream: device.mtu,
initialData: 0,
builder: (c, snapshot) => ListTile(
title: Text('MTU Size'),
subtitle: Text('${snapshot.data} bytes'),
trailing: IconButton(
icon: Icon(Icons.edit),
onPressed: () => device.requestMtu(223),
),
),
),
StreamBuilder<List<BluetoothService>>(
stream: device.services,
initialData: [],
builder: (c, snapshot) {
return Column(
children: _buildServiceTiles(snapshot.data!),
);
},
),
],
),
),
);
}
}
and here the second Part:
// Copyright 2017, Paul DeMarco.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
class ScanResultTile extends StatelessWidget {
const ScanResultTile({Key? key, required this.result, this.onTap})
: super(key: key);
final ScanResult result;
final VoidCallback? onTap;
Widget _buildTitle(BuildContext context) {
if (result.device.name.length > 0) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
result.device.name,
overflow: TextOverflow.ellipsis,
),
Text(
result.device.id.toString(),
style: Theme.of(context).textTheme.caption,
)
],
);
} else {
return Text(result.device.id.toString());
}
}
Widget _buildAdvRow(BuildContext context, String title, String value) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(title, style: Theme.of(context).textTheme.caption),
SizedBox(
width: 12.0,
),
Expanded(
child: Text(
value,
style: Theme.of(context)
.textTheme
.caption
?.apply(color: Colors.black),
softWrap: true,
),
),
],
),
);
}
String getNiceHexArray(List<int> bytes) {
return '[${bytes.map((i) => i.toRadixString(16).padLeft(2, '0')).join(', ')}]'
.toUpperCase();
}
String getNiceManufacturerData(Map<int, List<int>> data) {
if (data.isEmpty) {
return 'N/A';
}
List<String> res = [];
data.forEach((id, bytes) {
res.add(
'${id.toRadixString(16).toUpperCase()}: ${getNiceHexArray(bytes)}');
});
return res.join(', ');
}
String getNiceServiceData(Map<String, List<int>> data) {
if (data.isEmpty) {
return 'N/A';
}
List<String> res = [];
data.forEach((id, bytes) {
res.add('${id.toUpperCase()}: ${getNiceHexArray(bytes)}');
});
return res.join(', ');
}
#override
Widget build(BuildContext context) {
return ExpansionTile(
title: _buildTitle(context),
leading: Text(result.rssi.toString()),
trailing: RaisedButton(
child: Text('CONNECT'),
color: Colors.black,
textColor: Colors.white,
onPressed: (result.advertisementData.connectable) ? onTap : null,
),
children: <Widget>[
_buildAdvRow(
context, 'Complete Local Name', result.advertisementData.localName),
_buildAdvRow(context, 'Tx Power Level',
'${result.advertisementData.txPowerLevel ?? 'N/A'}'),
_buildAdvRow(context, 'Manufacturer Data',
getNiceManufacturerData(result.advertisementData.manufacturerData)),
_buildAdvRow(
context,
'Service UUIDs',
(result.advertisementData.serviceUuids.isNotEmpty)
? result.advertisementData.serviceUuids.join(', ').toUpperCase()
: 'N/A'),
_buildAdvRow(context, 'Service Data',
getNiceServiceData(result.advertisementData.serviceData)),
],
);
}
}
class ServiceTile extends StatelessWidget {
final BluetoothService service;
final List<CharacteristicTile> characteristicTiles;
const ServiceTile(
{Key? key, required this.service, required this.characteristicTiles})
: super(key: key);
#override
Widget build(BuildContext context) {
if (characteristicTiles.length > 0) {
return ExpansionTile(
title: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Service'),
Text('0x${service.uuid.toString().toUpperCase().substring(4, 8)}',
style: Theme.of(context).textTheme.body1?.copyWith(
color: Theme.of(context).textTheme.caption?.color))
],
),
children: characteristicTiles,
);
} else {
return ListTile(
title: Text('Service'),
subtitle:
Text('0x${service.uuid.toString().toUpperCase().substring(4, 8)}'),
);
}
}
}
class CharacteristicTile extends StatelessWidget {
final BluetoothCharacteristic characteristic;
final List<DescriptorTile> descriptorTiles;
final VoidCallback? onReadPressed;
final VoidCallback? onWritePressed;
final VoidCallback? onNotificationPressed;
const CharacteristicTile(
{Key? key,
required this.characteristic,
required this.descriptorTiles,
this.onReadPressed,
this.onWritePressed,
this.onNotificationPressed})
: super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder<List<int>>(
stream: characteristic.value,
initialData: characteristic.lastValue,
builder: (c, snapshot) {
final value = snapshot.data;
return ExpansionTile(
title: ListTile(
title: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Characteristic'),
Text(
'0x${characteristic.uuid.toString().toUpperCase().substring(4, 8)}',
style: Theme.of(context).textTheme.body1?.copyWith(
color: Theme.of(context).textTheme.caption?.color))
],
),
subtitle: Text(value.toString()),
contentPadding: EdgeInsets.all(0.0),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(
Icons.file_download,
color: Theme.of(context).iconTheme.color?.withOpacity(0.5),
),
onPressed: onReadPressed,
),
IconButton(
icon: Icon(Icons.file_upload,
color: Theme.of(context).iconTheme.color?.withOpacity(0.5)),
onPressed: onWritePressed,
),
IconButton(
icon: Icon(
characteristic.isNotifying
? Icons.sync_disabled
: Icons.sync,
color: Theme.of(context).iconTheme.color?.withOpacity(0.5)),
onPressed: onNotificationPressed,
)
],
),
children: descriptorTiles,
);
},
);
}
}
class DescriptorTile extends StatelessWidget {
final BluetoothDescriptor descriptor;
final VoidCallback? onReadPressed;
final VoidCallback? onWritePressed;
const DescriptorTile(
{Key? key,
required this.descriptor,
this.onReadPressed,
this.onWritePressed})
: super(key: key);
#override
Widget build(BuildContext context) {
return ListTile(
title: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Descriptor'),
Text('0x${descriptor.uuid.toString().toUpperCase().substring(4, 8)}',
style: Theme.of(context)
.textTheme
.body1
?.copyWith(color: Theme.of(context).textTheme.caption?.color))
],
),
subtitle: StreamBuilder<List<int>>(
stream: descriptor.value,
initialData: descriptor.lastValue,
builder: (c, snapshot) => Text(snapshot.data.toString()),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(
Icons.file_download,
color: Theme.of(context).iconTheme.color?.withOpacity(0.5),
),
onPressed: onReadPressed,
),
IconButton(
icon: Icon(
Icons.file_upload,
color: Theme.of(context).iconTheme.color?.withOpacity(0.5),
),
onPressed: onWritePressed,
)
],
),
);
}
}
class AdapterStateTile extends StatelessWidget {
const AdapterStateTile({Key? key, required this.state}) : super(key: key);
final BluetoothState state;
#override
Widget build(BuildContext context) {
return Container(
color: Colors.redAccent,
child: ListTile(
title: Text(
'Bluetooth adapter is ${state.toString().substring(15)}',
style: Theme.of(context).primaryTextTheme.subhead,
),
trailing: Icon(
Icons.error,
color: Theme.of(context).primaryTextTheme.subhead?.color,
),
),
);
}
}
please help me!!!
Related
enter image description here I get Unexpected Null value error when switching from Home page to Vehicle Add page. I couldn't understand what caused this.
I tried to run it in debug mode, but it was very confusing in debug mode, I couldn't understand it.
home.dart page
import 'package:Car_Price_App/Pages/category_add.dart';
import 'package:Car_Price_App/Pages/vehicle_add.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import '../services/auth_service.dart';
class Home extends StatefulWidget {
const Home({super.key});
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
final user = FirebaseAuth.instance.currentUser;
final email = user?.email;
return Scaffold(
appBar: AppBar(
title: Center(child: Text("Sıfır Araçlar ${email?.toUpperCase()}")),
leading: const Icon(Icons.car_rental),
actions: [
IconButton(onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context)=> CategoryAdd(gelenEmail: email.toString(),)));
print("giden $email");
}, icon: const Icon(Icons.category)),
IconButton(onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context)=> VehicleAdd()));
}, icon: const Icon(Icons.add)),
IconButton(onPressed: () {}, icon: const Icon(Icons.update)),
IconButton(onPressed: () {}, icon: const Icon(Icons.delete)),
],
),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Giriş Yapıldı $email"),
const SizedBox(height: 10,),
ElevatedButton(onPressed: (){
AuthService().signOut();
}, child: const Text("Çıkış Yap",),),
],
),
),
);
}
}
vehicle_add.dart page
import 'dart:io';
import 'dart:math';
import 'dart:core';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:currency_text_input_formatter/currency_text_input_formatter.dart';
import 'package:file_picker/file_picker.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../models/trade_mark_model.dart';
import '../models/type_of_vehicle_model.dart';
import '../models/vehicle_body_type_model.dart';
import '../models/vehicle_model.dart';
import '../models/vehicle_year_model.dart';
class VehicleAdd extends StatefulWidget {
const VehicleAdd({Key? key}) : super(key: key);
#override
State<VehicleAdd> createState() => _VehicleAddState();
}
class _VehicleAddState extends State<VehicleAdd> {
GlobalKey? aracEkleKey = GlobalKey<FormState>();
var db = FirebaseFirestore.instance;
TextEditingController vehicleProperties = TextEditingController();
TextEditingController vehiclePrice = TextEditingController();
String? vasitaTipi = "";
String? kasaTipi = "";
String? marka = "";
String? model = "";
String? yil = "";
PlatformFile? pickedFile;
UploadTask? uploadTask;
Future selectFile() async {
final result = await FilePicker.platform.pickFiles();
if (result == null) return;
setState(() {
pickedFile = result.files.first;
});
}
Future uploadFile() async {
final path = 'aracResimleri/${pickedFile?.name}';
final file = File(pickedFile!.path!);
final ref = FirebaseStorage.instance.ref().child(path);
setState(() {
uploadTask = ref.putFile(file);
});
final snapshot = await uploadTask?.whenComplete(() {});
final urlDownload = await snapshot?.ref.getDownloadURL();
print('Download Link : $urlDownload');
setState(() {
uploadTask = null;
});
}
Stream<List<TypeOfVehicleModel>> readVehicleType() => db.collection("Vasıtalar").snapshots().map((snapshot) => snapshot.docs.map((doc) => TypeOfVehicleModel.fromJson(doc.data())).toList());
Stream<List<VehicleBodyTypeModel>> readBodyType() => db.collection("KasaTipleri").snapshots().map((snapshot) => snapshot.docs.map((doc) => VehicleBodyTypeModel.fromJson(doc.data())).toList());
Stream<List<TradeMarkModel>> readTradeMark() => db.collection("Markalar").snapshots().map((snapshot) => snapshot.docs.map((doc) => TradeMarkModel.fromJson(doc.data())).toList());
Stream<List<VehicleModel>> readVehicleModel() => db.collection("Modeller").snapshots().map((snapshot) => snapshot.docs.map((doc) => VehicleModel.fromJson(doc.data())).toList());
Stream<List<VehicleYearModel>> readVehicleYear() => db.collection("Yıllar").snapshots().map((snapshot) => snapshot.docs.map((doc) => VehicleYearModel.fromJson(doc.data())).toList());
late int _key;
_collapse() {
int? newKey;
do {
_key = Random().nextInt(10000);
} while (newKey == _key);
}
#override
void initState() {
super.initState();
_collapse();
}
#override
Widget build(BuildContext context) {
var screenInfo = MediaQuery.of(context);
final double screenWidth = screenInfo.size.width;
final double screenHeight = screenInfo.size.height;
return Scaffold(
appBar: AppBar(
title: const Text("Araç Ekle"),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20),
child: Center(
child: Form(
autovalidateMode: AutovalidateMode.onUserInteraction,
key: aracEkleKey,
child: SizedBox(
width: screenWidth / 2,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FutureBuilder<List<TypeOfVehicleModel>>(
future: readVehicleType().first,
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text("Something went wrong");
} else if (snapshot.hasData) {
final typeOfVehicle = snapshot.data!;
return ExpansionTile(
key: Key(_key.toString()),
initiallyExpanded: false,
leading: const Icon(Icons.merge_type),
title: const Text("Vasıta Seçiniz"),
children: typeOfVehicle.map(buildTov).toList(),
);
} else {
return const Center(
child: Padding(
padding: EdgeInsets.all(8.0),
child: CircularProgressIndicator(),
),
);
}
},
), // Vasıta Tipi
Text("$vasitaTipi"),
Text("$kasaTipi"),
Text("$marka"),
Text("$model"),
Text("$yil"),
FutureBuilder<List<VehicleBodyTypeModel>>(
future: readBodyType().first,
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text("Something went wrong");
} else if (snapshot.hasData) {
final bodyType = snapshot.data!;
return ExpansionTile(
key: Key(_key.toString()),
initiallyExpanded: false,
leading: const Icon(Icons.type_specimen),
title: const Text("Kasa Tipi Seçiniz"),
children: bodyType.map(buildBodyType).toList(),
);
} else {
return const Center(
child: Padding(
padding: EdgeInsets.all(8.0),
child: CircularProgressIndicator(),
),
);
}
},
), // Kasatipi
FutureBuilder<List<TradeMarkModel>>(
future: readTradeMark().first,
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text("Something went wrong");
} else if (snapshot.hasData) {
final tradeMarka = snapshot.data!;
return ExpansionTile(
key: Key(_key.toString()),
initiallyExpanded: false,
leading: const Icon(Icons.branding_watermark),
title: const Text("Marka Seçiniz"),
children: tradeMarka.map(buildTradeMark).toList(),
);
} else {
return const Center(
child: Padding(
padding: EdgeInsets.all(8.0),
child: CircularProgressIndicator(),
),
);
}
},
), //Markalar
FutureBuilder<List<VehicleModel>>(
future: readVehicleModel().first,
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text("Something went wrong");
} else if (snapshot.hasData) {
final model = snapshot.data!;
return ExpansionTile(
key: Key(_key.toString()),
initiallyExpanded: false,
leading: const Icon(Icons.time_to_leave),
title: const Text("Model Seçiniz"),
children: model.map(buildModel).toList(),
);
} else {
return const Center(
child: Padding(
padding: EdgeInsets.all(8.0),
child: CircularProgressIndicator(),
),
);
}
},
), //Modeller
FutureBuilder<List<VehicleYearModel>>(
future: readVehicleYear().first,
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text("Something went wrong");
} else if (snapshot.hasData) {
final year = snapshot.data!;
return ExpansionTile(
key: Key(_key.toString()),
initiallyExpanded: false,
leading: const Icon(Icons.calendar_month),
title: const Text(
"Yıl Seçiniz",
),
children: year.map(buildYear).toList(),
);
} else {
return const Center(
child: Padding(
padding: EdgeInsets.all(8.0),
child: CircularProgressIndicator(),
),
);
}
},
), //Yıllar
Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
controller: vehicleProperties,
decoration: const InputDecoration(
labelText: "Araç Özellikleri Giriniz",
suffixText: "Araç Özellikleri Giriniz",
border: OutlineInputBorder(),
),
minLines: 1,
maxLines: screenHeight.toInt(),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
// for below version 2 use this
FilteringTextInputFormatter.allow(RegExp(
r'\d',
)),
FilteringTextInputFormatter.digitsOnly,
CurrencyTextInputFormatter(
symbol: '₺',
name: "TL",
)
],
controller: vehiclePrice,
decoration: const InputDecoration(
labelText: "Araç Fiyat Giriniz",
suffixText: "Araç Fiyat Giriniz",
border: OutlineInputBorder(),
),
minLines: 1,
maxLines: screenHeight.toInt(),
),
),
Expanded(
child: Image.file(
File(pickedFile!.path!),
width: double.infinity,
fit: BoxFit.cover,
),
),
ElevatedButton.icon(
icon: const Icon(Icons.image),
onPressed: () {
selectFile();
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Araç Resmi Seç"),
content: Text(pickedFile!.name),
actions: [
TextButton(
onPressed: () async {
uploadFile();
},
child: const Center(
child: Text(
"Galeri'den Seç",
style: TextStyle(color: Colors.black, fontSize: 20),
))),
],
);
});
},
label: const Text("Araç Resimi Ekle"),
),
const SizedBox(
height: 30,
),
StreamBuilder<TaskSnapshot>(
stream: uploadTask!.snapshotEvents,
builder: (context, snapshot) {
if (snapshot.hasData) {
final data = snapshot.data!;
double progress = data.bytesTransferred / data.totalBytes;
return SizedBox(
height: 50,
child: Stack(
fit: StackFit.expand,
children: [
LinearProgressIndicator(
value: progress,
color: Colors.green,
backgroundColor: Colors.grey,
),
Center(
child: Text(
'${(100 * progress).roundToDouble()} %',
style: const TextStyle(color: Colors.white),
),
)
],
),
);
} else {
return const SizedBox(
height: 50,
);
}
},
)
],
),
),
),
),
),
),
// Fab Buton visible olacak doldurulması geren herşey dolduğunda gözükecek
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
// bool kontrolSonucu = aracEkleKey.currentState!.validate();
},
),
);
}
Widget buildTov(TypeOfVehicleModel typeOfVehicleModel) => ListTile(
onTap: () {
setState(() {
_collapse();
vasitaTipi = typeOfVehicleModel.typeOfVehicle;
});
if (kDebugMode) {
print("$vasitaTipi");
}
},
leading: const Icon(
Icons.stars_outlined,
size: 10,
),
title: Text(typeOfVehicleModel.typeOfVehicle.toString()),
); //Araç vasıta tipi
Widget buildBodyType(VehicleBodyTypeModel vehicleBodyTypeModel) => ListTile(
onTap: () {
setState(() {
_collapse();
kasaTipi = vehicleBodyTypeModel.vehicleBodyType;
});
if (kDebugMode) {
print(vehicleBodyTypeModel.vehicleBodyType);
}
},
leading: const Icon(
Icons.stars_outlined,
size: 10,
),
title: Text(vehicleBodyTypeModel.vehicleBodyType.toString()),
); // Araç kasa tipi
Widget buildTradeMark(TradeMarkModel tradeMarkModel) => ListTile(
onTap: () {
setState(() {
_collapse();
marka = tradeMarkModel.tradeMarkName;
});
if (kDebugMode) {
print(tradeMarkModel.tradeMarkName);
}
},
leading: const Icon(
Icons.stars_outlined,
size: 10,
),
title: Text(tradeMarkModel.tradeMarkName.toString()),
); // Araç MArka
Widget buildModel(VehicleModel vehicleModel) => ListTile(
onTap: () {
setState(() {
_collapse();
model = vehicleModel.modelName;
});
if (kDebugMode) {
print(vehicleModel.modelName);
}
},
leading: const Icon(
Icons.stars_outlined,
size: 10,
),
title: Text(vehicleModel.modelName.toString()),
); // Araç Modeli
Widget buildYear(VehicleYearModel vehicleYearModel) => ListTile(
onTap: () {
setState(() {
_collapse();
yil = vehicleYearModel.year;
});
if (kDebugMode) {
print(vehicleYearModel.year);
}
},
leading: const Icon(
Icons.stars_outlined,
size: 10,
),
title: Text(vehicleYearModel.year.toString()),
); // Araç Yılı
Widget buildProgress() => StreamBuilder<TaskSnapshot>(
stream: uploadTask?.snapshotEvents,
builder: (context, snapshot) {
if (snapshot.hasData) {
final data = snapshot.data!;
double progress = data.bytesTransferred / data.totalBytes;
return SizedBox(
height: 50,
child: Stack(
fit: StackFit.expand,
children: [
LinearProgressIndicator(
value: progress,
color: Colors.green,
backgroundColor: Colors.grey,
),
Center(
child: Text(
'${(100 * progress).roundToDouble()} %',
style: const TextStyle(color: Colors.white),
),
)
],
),
);
} else {
return const SizedBox(
height: 50,
);
}
},
);
}
I don't know which variable is causing the problem. I'm inexperienced in flutter.
The error message says on which line the error is. See
That means it's on line 297. If I'm not mistaken that is the line:
child: Image.file(File(pickedFile!.path!),
So pickedFile is null
I just started learning flutter I needed to pass data between pages so I found it easy to do with static variables but now I'm trying to make a setting page. I made a class named Settings like this :
class Settings {
static bool darkMode = false;
static bool addTable = true;
static saveSetting() {
GetStorage().write("darkMode", Settings.darkMode);
GetStorage().write("addTable", Settings.addTable);
}
static setSetting() {
GetStorage.init();
Settings.darkMode = (GetStorage().read("darkMode") ?? false);
Settings.addTable = (GetStorage().read("addTable") ?? true);
}
}
And a Switch in that page like this :
Switch(
value: Settings.addTable,
onChanged: (_) {
setState(() {
Settings.addTable = !Settings.addTable;
Settings.saveSetting();
});
}),
but after reloading the app the values are not saved in GetStorage, strings are saved perfectly except this one.
And the whole code is here :
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get_storage/get_storage.dart';
import 'widgets/menu_button.dart';
void main() async {
await GetStorage.init();
DataManagament dataManagament = DataManagament();
dataManagament.startingApp();
runApp(const MyApp());
}
class DataManagament {
static String reserveString = "";
static List<String> reserveList = [];
static String tableString = "";
static List<String> tableList = [];
void saveReserveList() {
GetStorage().write("reserveList", reserveList.toString());
}
void saveTableList() {
GetStorage().write("tableList", tableList.toString());
}
void startingApp() {
Settings.setSetting;
//reserve
reserveString = (GetStorage().read("reserveList") ?? "");
reserveString == ""
? reserveList = []
: reserveList =
reserveString.substring(1, reserveString.length - 1).split(",");
//table
tableString = (GetStorage().read("tableList") ?? "");
tableString == ""
? tableList = []
: tableList =
tableString.substring(1, tableString.length - 1).split(",");
}
}
class Settings {
static bool darkMode = false;
static bool addTable = true;
static saveSetting() {
GetStorage().write("darkMode", Settings.darkMode);
GetStorage().write("addTable", Settings.addTable);
}
static setSetting() {
Settings.darkMode = (GetStorage().read("darkMode") ?? false);
Settings.addTable = (GetStorage().read("addTable") ?? true);
}
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: IntroPage(),
debugShowCheckedModeBanner: false,
);
}
}
//
//
//************************************************************
//
//
//
//
//************************************************************
// Reserve Page
//
class ReservePage extends StatefulWidget {
const ReservePage({Key? key}) : super(key: key);
#override
State<ReservePage> createState() => _ReservePageState();
}
class _ReservePageState extends State<ReservePage> {
final _nameController = TextEditingController();
final _minuteController = TextEditingController();
final _hourController = TextEditingController();
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: SingleChildScrollView(
child: Column(
children: [
TextField(
controller: _nameController,
textDirection: TextDirection.rtl,
decoration: const InputDecoration(
hintTextDirection: TextDirection.rtl,
border: OutlineInputBorder(),
labelText: 'نام',
),
),
Row(
children: [
Expanded(
flex: 5,
child: TextField(
controller: _hourController,
maxLength: 2,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: 'hour',
),
),
),
const Expanded(
flex: 3,
child: Center(
child: Text(
":",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
),
Expanded(
flex: 5,
child: TextField(
controller: _minuteController,
maxLength: 2,
keyboardType: TextInputType.number,
decoration: const InputDecoration(
labelText: 'minute',
),
),
),
],
),
const SizedBox(height: 20),
Center(
child: OutlinedButton(
onPressed: () {
if (_hourController.text != "" &&
_nameController.text != "") {
setState(() {
DataManagament.reserveList
.add(_nameController.text);
DataManagament.reserveList.add(
"${_hourController.text}:${_minuteController.text}");
_hourController.clear();
_nameController.clear();
_minuteController.clear();
DataManagament().saveReserveList();
});
}
},
child: const Text(
"save",
style: TextStyle(
color: Colors.black,
),
),
style: OutlinedButton.styleFrom(
minimumSize: Size(
size.width / 3,
(size.width / 3) * 0.4,
),
backgroundColor: Colors.green,
),
),
)
],
),
),
title: const Center(child: Text("")),
);
});
},
child: const FittedBox(
child: Icon(
Icons.add,
),
),
),
appBar: AppBar(
backgroundColor: Colors.white,
foregroundColor: Colors.black,
title: const Text(
"",
),
centerTitle: true,
),
body: DataManagament.reserveList.isNotEmpty
? SingleChildScrollView(
child: Column(
children: [
ListView.builder(
shrinkWrap: true,
itemCount: DataManagament.reserveList.length ~/ 2,
itemBuilder: (BuildContext context, int index) {
return Card(
// name 0 , time ,1
child: ListTile(
//name
trailing: Text(
DataManagament.reserveList[index * 2],
),
//time
title: Text(
DataManagament.reserveList[(index * 2) + 1],
),
leading: TextButton(
onPressed: () {
setState(() {
DataManagament.reserveList.removeAt(index * 2);
DataManagament.reserveList.removeAt(index * 2);
DataManagament().saveReserveList();
});
},
child: const Text("delete"),
),
));
},
),
],
),
)
: const Center(
child: Text("..."),
),
);
}
}
//
//
//************************************************************
// Menu Page
//
class MenuPage extends StatelessWidget {
const MenuPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SettingsPage(),
));
},
icon: const Icon(Icons.settings),
),
],
backgroundColor: Colors.white,
foregroundColor: Colors.black,
centerTitle: true,
title: const Text(
"",
),
),
body: Column(
children: [
const Spacer(flex: 1),
Expanded(
child: Center(
child: MenuButton(
func: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const TablePage(),
));
},
text: "tables"),
),
flex: 2),
Expanded(
child: Center(
child: MenuButton(
func: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ReservePage(),
));
},
text: "رزروها"),
),
flex: 2),
Expanded(
child: Center(
child: MenuButton(func: () {}, text: "test"),
),
flex: 2),
const Spacer(
flex: 4,
)
],
),
);
}
}
//
//
//************************************************************
// Tables Page
//
class TablePage extends StatefulWidget {
const TablePage({Key? key}) : super(key: key);
#override
State<TablePage> createState() => _TablePageState();
}
class _TablePageState extends State<TablePage> {
final _nameController = TextEditingController(); // ignore: unused_field
final _minuteController = TextEditingController(); // ignore: unused_field
final _hourController = TextEditingController(); // ignore: unused_field
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: Settings.addTable
? GestureDetector(
onLongPress: () {
setState(() {
Settings.addTable = false;
});
},
child: FloatingActionButton(
onPressed: () {
setState(() {
});
},
child: const Icon(
Icons.add,
),
),
)
: null,
appBar: AppBar(
backgroundColor: Colors.white,
foregroundColor: Colors.black,
title: const Text(
"tables",
),
centerTitle: true,
),
body: DataManagament.tableList.isNotEmpty
? SingleChildScrollView(
child: Column(
children: [
ListView.builder(
shrinkWrap: true,
itemCount: DataManagament.tableList.length,
itemBuilder: (BuildContext context, int index) {
return Card(
// name 0 , time ,1
child: ListTile(
//name
trailing: Row(
children: [
Text(index.toString()),
TextButton(
onPressed: () {
setState(() {
DataManagament.tableList[index] =
_nameController.text;
});
},
child: const Text(""),
),
],
),
//time
title: TextButton(
onPressed: () {},
child: const Text(""),
),
leading: TextButton(
onPressed: () {},
child: Text(
DataManagament.tableList[index].toString()),
),
),
);
},
),
],
),
)
: const Center(
child: Text("..."),
),
);
}
}
//
//
//************************************************************
// Intro Page
//
class IntroPage extends StatefulWidget {
const IntroPage({Key? key}) : super(key: key);
#override
State<IntroPage> createState() => _IntroPageState();
}
class _IntroPageState extends State<IntroPage> {
Timer? _timer;
void startTimer() {
_timer = Timer(const Duration(seconds: 3), () {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const MenuPage(),
));
});
}
#override
void initState() {
super.initState();
startTimer();
}
#override
void dispose() {
_timer!.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Expanded(
child: Center(
child: Image.asset("images/eightball.png"),
),
flex: 4),
const Expanded(
child: Center(
child: CircularProgressIndicator(),
),
),
],
),
);
}
}
//
//
//************************************************************
// Settings Page
//
class SettingsPage extends StatefulWidget {
const SettingsPage({Key? key}) : super(key: key);
#override
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
foregroundColor: Colors.black,
title: const Text("settings"),
),
body: ListView(children: [
Card(
child: ListTile(
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: const [
Text(
"add table",
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
SizedBox(width: 10),
Icon(Icons.add, size: 30),
],
),
leading: Switch(
value: Settings.addTable,
onChanged: (_) {
setState(() {
Settings.addTable = !Settings.addTable;
Settings.saveSetting();
});
}),
),
),
Card(
child: ListTile(
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: const [
Text(
"night mode",
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
SizedBox(width: 20),
Icon(Icons.dark_mode),
],
),
leading: Switch(
value: Settings.darkMode,
onChanged: (_) {
setState(() {
Settings.darkMode = !Settings.darkMode;
Settings.saveSetting();
});
}),
),
),
]),
);
}
}
You need to call
final settings = new Settings();
settings.setSettings();
in a page where you want to access settings
I think you are just reading data from GetStorage inside setSettings instead of writing to storage, so when the app releods or restarts data inside static variables will not be available. as:
static setSetting() {
GetStorage.init();
Settings.darkMode = (GetStorage().read("darkMode") ?? false);
Settings.addTable = (GetStorage().read("addTable") ?? true);
//Here you are just updating your settings variable.
//Update you storage also to keep the selection in storage
}
Hope it works.
I am using this library (https://pub.dev/packages/flutter_blue/example) to control my ESP32 (Arduino). I have two buttons that work perfectly, one to turn off the led and one to turn on the led. My problem is that I want to receive data that the arduino sends (notify). I don’t know how to do it.
main.dart
// Copyright 2017, Paul DeMarco.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:convert' show utf8;
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:minertti/widgets.dart';
void main() {
runApp(FlutterBlueApp());
}
class FlutterBlueApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
color: Colors.lightBlue,
home: StreamBuilder<BluetoothState>(
stream: FlutterBlue.instance.state,
initialData: BluetoothState.unknown,
builder: (c, snapshot) {
final state = snapshot.data;
if (state == BluetoothState.on) {
return FindDevicesScreen();
}
return BluetoothOffScreen(state: state);
}),
);
}
}
class BluetoothOffScreen extends StatelessWidget {
const BluetoothOffScreen({Key? key, this.state}) : super(key: key);
final BluetoothState? state;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.lightBlue,
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(
Icons.bluetooth_disabled,
size: 200.0,
color: Colors.white54,
),
Text(
'Bluetooth Adapter is ${state != null ? state.toString().substring(15) : 'not available'}.',
style: Theme.of(context)
.primaryTextTheme
.subtitle1
?.copyWith(color: Colors.white),
),
],
),
),
);
}
}
class FindDevicesScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Find Devices'),
),
body: RefreshIndicator(
onRefresh: () =>
FlutterBlue.instance.startScan(timeout: Duration(seconds: 4)),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
StreamBuilder<List<BluetoothDevice>>(
stream: Stream.periodic(Duration(seconds: 2))
.asyncMap((_) => FlutterBlue.instance.connectedDevices),
initialData: [],
builder: (c, snapshot) => Column(
children: snapshot.data!
.map((d) => ListTile(
title: Text(d.name),
subtitle: Text(d.id.toString()),
trailing: StreamBuilder<BluetoothDeviceState>(
stream: d.state,
initialData: BluetoothDeviceState.disconnected,
builder: (c, snapshot) {
if (snapshot.data ==
BluetoothDeviceState.connected) {
return RaisedButton(
child: Text('OPEN'),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
DeviceScreen(device: d))),
);
}
return Text(snapshot.data.toString());
},
),
))
.toList(),
),
),
StreamBuilder<List<ScanResult>>(
stream: FlutterBlue.instance.scanResults,
initialData: [],
builder: (c, snapshot) => Column(
children: snapshot.data!
.map(
(r) => ScanResultTile(
result: r,
onTap: () => Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
r.device.connect();
return DeviceScreen(device: r.device);
})),
),
)
.toList(),
),
),
],
),
),
),
floatingActionButton: StreamBuilder<bool>(
stream: FlutterBlue.instance.isScanning,
initialData: false,
builder: (c, snapshot) {
if (snapshot.data!) {
return FloatingActionButton(
child: Icon(Icons.stop),
onPressed: () => FlutterBlue.instance.stopScan(),
backgroundColor: Colors.red,
);
} else {
return FloatingActionButton(
child: Icon(Icons.search),
onPressed: () => FlutterBlue.instance
.startScan(timeout: Duration(seconds: 4)));
}
},
),
);
}
}
class DeviceScreen extends StatelessWidget {
const DeviceScreen({Key? key, required this.device}) : super(key: key);
final BluetoothDevice device;
List<Widget> _buildServiceTiles(List<BluetoothService> services) {
return services
//just show the last service
.sublist(services.length - 1)
.map(
(s) => ServiceTile(
service: s,
characteristicTiles: s.characteristics
.map(
(c) => CharacteristicTile(
characteristic: c,
//turnOff: () => c.read(),
turnOff: () => c.write(utf8.encode("0")),
turnOn: () => c.write(utf8.encode("1")),
extraButton: () => c.write(utf8.encode("2")),
onNotificationPressed: () async {
await c.setNotifyValue(!c.isNotifying);
await c.read();
},
descriptorTiles: c.descriptors
.map(
(d) => DescriptorTile(
descriptor: d,
//turnOff: () => d.read(),
//turnOn: () => d.read(),
),
)
.toList(),
),
)
.toList(),
),
)
.toList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(device.name),
actions: <Widget>[
StreamBuilder<BluetoothDeviceState>(
stream: device.state,
initialData: BluetoothDeviceState.connecting,
builder: (c, snapshot) {
VoidCallback? onPressed;
String text;
switch (snapshot.data) {
case BluetoothDeviceState.connected:
onPressed = () => device.disconnect();
text = 'DISCONNECT';
break;
case BluetoothDeviceState.disconnected:
onPressed = () => device.connect();
text = 'CONNECT';
break;
default:
onPressed = null;
text = snapshot.data.toString().substring(21).toUpperCase();
break;
}
return FlatButton(
onPressed: onPressed,
child: Text(
text,
style: Theme.of(context)
.primaryTextTheme
.button
?.copyWith(color: Colors.white),
));
},
)
],
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
StreamBuilder<BluetoothDeviceState>(
stream: device.state,
initialData: BluetoothDeviceState.connecting,
builder: (c, snapshot) => ListTile(
leading: (snapshot.data == BluetoothDeviceState.connected)
? Icon(Icons.bluetooth_connected)
: Icon(Icons.bluetooth_disabled),
title: Text(
'Device is ${snapshot.data.toString().split('.')[1]}.'),
subtitle: Text('${device.id}'),
trailing: StreamBuilder<bool>(
stream: device.isDiscoveringServices,
initialData: false,
builder: (c, snapshot) => IndexedStack(
index: snapshot.data! ? 1 : 0,
children: <Widget>[
IconButton(
icon: Icon(Icons.refresh),
onPressed: () => device.discoverServices(),
),
IconButton(
icon: SizedBox(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Colors.grey),
),
width: 18.0,
height: 18.0,
),
onPressed: null,
)
],
),
),
),
),
StreamBuilder<int>(
stream: device.mtu,
initialData: 0,
builder: (c, snapshot) => ListTile(
title: Text('MTU Size'),
subtitle: Text('${snapshot.data} bytes'),
trailing: IconButton(
icon: Icon(Icons.edit),
onPressed: () => device.requestMtu(223),
),
),
),
StreamBuilder<List<BluetoothService>>(
stream: device.services,
initialData: [],
builder: (c, snapshot) {
return Column(
children: _buildServiceTiles(snapshot.data!),
);
},
),
],
),
),
);
}
}
device.dart
// Copyright 2017, Paul DeMarco.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:convert' show utf8;
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
class ScanResultTile extends StatelessWidget {
const ScanResultTile({Key? key, required this.result, this.onTap})
: super(key: key);
final ScanResult result;
final VoidCallback? onTap;
Widget _buildTitle(BuildContext context) {
if (result.device.name.length > 0) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
result.device.name,
overflow: TextOverflow.ellipsis,
),
Text(
result.device.id.toString(),
style: Theme.of(context).textTheme.caption,
)
],
);
} else {
return Text(result.device.id.toString());
}
}
Widget _buildAdvRow(BuildContext context, String title, String value) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(title, style: Theme.of(context).textTheme.caption),
SizedBox(
width: 12.0,
),
Expanded(
child: Text(
value,
style: Theme.of(context)
.textTheme
.caption
?.apply(color: Colors.black),
softWrap: true,
),
),
],
),
);
}
String getNiceHexArray(List<int> bytes) {
return '[${bytes.map((i) => i.toRadixString(16).padLeft(2, '0')).join(', ')}]'
.toUpperCase();
}
String getNiceManufacturerData(Map<int, List<int>> data) {
if (data.isEmpty) {
return 'N/A';
}
List<String> res = [];
data.forEach((id, bytes) {
res.add(
'${id.toRadixString(16).toUpperCase()}: ${getNiceHexArray(bytes)}');
});
return res.join(', ');
}
String getNiceServiceData(Map<String, List<int>> data) {
if (data.isEmpty) {
return 'N/A';
}
List<String> res = [];
data.forEach((id, bytes) {
res.add('${id.toUpperCase()}: ${getNiceHexArray(bytes)}');
});
return res.join(', ');
}
#override
Widget build(BuildContext context) {
return ExpansionTile(
title: _buildTitle(context),
leading: Text(result.rssi.toString()),
trailing: RaisedButton(
child: Text('CONNECT'),
color: Colors.black,
textColor: Colors.white,
onPressed: (result.advertisementData.connectable) ? onTap : null,
),
children: <Widget>[
_buildAdvRow(
context, 'Complete Local Name', result.advertisementData.localName),
_buildAdvRow(context, 'Tx Power Level',
'${result.advertisementData.txPowerLevel ?? 'N/A'}'),
_buildAdvRow(context, 'Manufacturer Data',
getNiceManufacturerData(result.advertisementData.manufacturerData)),
_buildAdvRow(
context,
'Service UUIDs',
(result.advertisementData.serviceUuids.isNotEmpty)
? result.advertisementData.serviceUuids.join(', ').toUpperCase()
: 'N/A'),
_buildAdvRow(context, 'Service Data',
getNiceServiceData(result.advertisementData.serviceData)),
],
);
}
}
class ServiceTile extends StatelessWidget {
final BluetoothService service;
final List<CharacteristicTile> characteristicTiles;
const ServiceTile(
{Key? key, required this.service, required this.characteristicTiles})
: super(key: key);
#override
Widget build(BuildContext context) {
if (characteristicTiles.length > 0) {
return ExpansionTile(
title: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Service'),
Text('0x${service.uuid.toString().toUpperCase().substring(4, 8)}',
style: Theme.of(context).textTheme.bodyText1?.copyWith(
color: Theme.of(context).textTheme.caption?.color))
],
),
children: characteristicTiles,
);
} else {
return ListTile(
title: Text('Service'),
subtitle:
Text('0x${service.uuid.toString().toUpperCase().substring(4, 8)}'),
);
}
}
}
class CharacteristicTile extends StatelessWidget {
final BluetoothCharacteristic characteristic;
final List<DescriptorTile> descriptorTiles;
final VoidCallback? turnOff;
final VoidCallback? extraButton;
final VoidCallback? turnOn;
final VoidCallback? onNotificationPressed;
const CharacteristicTile(
{Key? key,
required this.characteristic,
required this.descriptorTiles,
this.turnOff,
this.extraButton,
this.turnOn,
this.onNotificationPressed})
: super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder<List<int>>(
stream: characteristic.value,
initialData: characteristic.lastValue,
builder: (c, snapshot) {
final value = snapshot.data;
return ExpansionTile(
title: ListTile(
title: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Characteristic'),
Text(
'0x${characteristic.uuid.toString().toUpperCase().substring(4, 8)}',
style: Theme.of(context).textTheme.bodyText1?.copyWith(
color: Theme.of(context).textTheme.caption?.color))
],
),
subtitle: Text(value.toString()),
contentPadding: EdgeInsets.all(0.0),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(
Icons.lightbulb_outline,
color: Theme.of(context).iconTheme.color?.withOpacity(0.5),
),
onPressed: turnOff,
),
IconButton(
icon: Icon(Icons.lightbulb,
color: Theme.of(context).iconTheme.color?.withOpacity(0.5)),
onPressed: turnOn,
),
//iconButton for add
IconButton(
icon: Icon(
Icons.add_circle,
color: Theme.of(context).iconTheme.color?.withOpacity(0.5),
),
onPressed: extraButton,
),
IconButton(
icon: Icon(
characteristic.isNotifying
? Icons.sync_disabled
: Icons.sync,
color: Theme.of(context).iconTheme.color?.withOpacity(0.5)),
onPressed: onNotificationPressed,
)
],
),
children: descriptorTiles,
);
},
);
}
}
class DescriptorTile extends StatelessWidget {
final BluetoothDescriptor descriptor;
final VoidCallback? turnOff;
final VoidCallback? turnOn;
final VoidCallback? extraButton;
const DescriptorTile(
{Key? key,
required this.descriptor,
this.turnOff,
this.extraButton,
this.turnOn})
: super(key: key);
#override
Widget build(BuildContext context) {
return ListTile(
title: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Descriptor'),
Text('0x${descriptor.uuid.toString().toUpperCase().substring(4, 8)}',
style: Theme.of(context)
.textTheme
.bodyText1
?.copyWith(color: Theme.of(context).textTheme.caption?.color))
],
),
subtitle: StreamBuilder<List<int>>(
stream: descriptor.value,
initialData: descriptor.lastValue,
builder: (c, snapshot) => Text(snapshot.data.toString()),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(
Icons.lightbulb_outline,
color: Theme.of(context).iconTheme.color?.withOpacity(0.5),
),
onPressed: turnOff,
),
IconButton(
icon: Icon(
Icons.lightbulb,
color: Theme.of(context).iconTheme.color?.withOpacity(0.5),
),
onPressed: turnOn,
),
IconButton(
icon: Icon(
Icons.add_circle,
color: Theme.of(context).iconTheme.color?.withOpacity(0.5),
),
onPressed: extraButton,
)
],
),
);
}
}
class AdapterStateTile extends StatelessWidget {
const AdapterStateTile({Key? key, required this.state}) : super(key: key);
final BluetoothState state;
#override
Widget build(BuildContext context) {
return Container(
color: Colors.redAccent,
child: ListTile(
title: Text(
'Bluetooth adapter is ${state.toString().substring(15)}',
style: Theme.of(context).primaryTextTheme.subtitle1,
),
trailing: Icon(
Icons.error,
color: Theme.of(context).primaryTextTheme.subtitle1?.color,
),
),
);
}
}
ESP32 Code:
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
BLECharacteristic *pCharacteristic;
bool deviceConnected = false;
float txValue = 0;
const int readPin = 32; // Use GPIO number. See ESP32 board pinouts
const int LED = 2; // Could be different depending on the dev board. I used the DOIT ESP32 dev board.
//std::string rxValue; // Could also make this a global var to access it in loop()
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
}
};
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0) {
Serial.println("*********");
Serial.print("Received Value: ");
for (int i = 0; i < rxValue.length(); i++) {
Serial.print(rxValue[i]);
}
Serial.println();
// Do stuff based on the command received from the app
if (rxValue.find("1") != -1) {
Serial.println("Turning ON!");
digitalWrite(LED, HIGH);
}
else if (rxValue.find("0") != -1) {
Serial.println("Turning OFF!");
digitalWrite(LED, LOW);
}
Serial.println();
Serial.println("*********");
}
}
};
void setup() {
Serial.begin(115200);
pinMode(LED, OUTPUT);
// Create the BLE Device
BLEDevice::init("ESP32"); // Give it a name
// Create the BLE Server
BLEServer *pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
// Create the BLE Service
BLEService *pService = pServer->createService(SERVICE_UUID);
// Create a BLE Characteristic
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_TX,
BLECharacteristic::PROPERTY_NOTIFY
);
pCharacteristic->addDescriptor(new BLE2902());
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX,
BLECharacteristic::PROPERTY_WRITE
);
pCharacteristic->setCallbacks(new MyCallbacks());
// Start the service
pService->start();
// Start advertising
pServer->getAdvertising()->start();
Serial.println("Waiting a client connection to notify...");
}
void loop() {
if (deviceConnected) {
// Fabricate some arbitrary junk for now...
txValue = analogRead(readPin) / 3.456; // This could be an actual sensor reading!
// Let's convert the value to a char array:
char txString[8]; // make sure this is big enuffz
dtostrf(txValue, 1, 2, txString); // float_val, min_width, digits_after_decimal, char_buffer
// pCharacteristic->setValue(&txValue, 1); // To send the integer value
// pCharacteristic->setValue("Hello!"); // Sending a test message
pCharacteristic->setValue(txString);
pCharacteristic->notify(); // Send the value to the app!
Serial.print("*** Sent Value: ");
Serial.print(txString);
Serial.println(" ***");
// You can add the rxValue checks down here instead
// if you set "rxValue" as a global var at the top!
// Note you will have to delete "std::string" declaration
// of "rxValue" in the callback function.
// if (rxValue.find("A") != -1) {
// Serial.println("Turning ON!");
// digitalWrite(LED, HIGH);
// }
// else if (rxValue.find("B") != -1) {
// Serial.println("Turning OFF!");
// digitalWrite(LED, LOW);
// }
}
delay(1000);
}
The app correctly receives the information, but in a strange code that I don't understand. How do I convert it to letters and numbers?
In device.dart you can see that there is a part that says: "subtitle: Text(value.toString())"
That is the one that sends the data, but I don't understand it.
...
builder: (c, snapshot) {
final value = snapshot.data;
return ExpansionTile(
title: ListTile(
title: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Characteristic'),
Text(
'0x${characteristic.uuid.toString().toUpperCase().substring(4, 8)}',
style: Theme.of(context).textTheme.bodyText1?.copyWith(
color: Theme.of(context).textTheme.caption?.color))
],
),
subtitle: Text(value.toString()),
...
Arduino sends: 98.96 and I receive this data from value.toString(): [57, 56, 46, 57, 54]
Try using String.fromCharCodes to convert the received data ([57, 56, 46, 57, 54]) to 98.96.
Refer to this utf8 char table: https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/02572bcf-76c4-420a-90ac-65a0c8c71eae/iso-8859-5-php-orange.png
You are receiving Uint8List. Decode that data into string then parse it in double.
Try this:
String decodedData = utf8.decode(receivedData);
where receivedData -> Uint8List
Now in decodedData you will get the required string that you can parse into other data types.
I really don't know how to describe this, I'm just going to paste here my code and my output.
Code:
scan() async {
Position position =
await getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
CollectionReference coll = FirebaseFirestore.instance.collection(
DateTime.now().toString() +
position.latitude.toString() +
position.latitude.toString());
// Start scanning
flutterBlue.startScan(timeout: Duration(seconds: 4));
// Listen to scan results
var subscription = flutterBlue.scanResults.listen((results) {
// do something with scan results
var arr = new List();
for (ScanResult r in results) {
if (arr.contains(r.hashCode) == false) {
arr.add(r.hashCode);
print(arr);
/*
coll
.add({"hash": r.device.hashCode, "rssi": r.rssi})
.then((value) => print("ok! $value"))
.catchError((error) => print(error));
*/
}
}
});
// Stop scanning
flutterBlue.stopScan();
testoBottone = "Scansione completata";
setState(() {});
}
My output:
I/flutter ( 4599): [839208879]
I/flutter ( 4599): [839208879, 1070525250]
I/flutter ( 4599): [839208879]
I/flutter ( 4599): [839208879, 1070525250]
I/flutter ( 4599): [839208879]
I/flutter ( 4599): [839208879, 1070525250]
I/flutter ( 4599): [839208879]
I/flutter ( 4599): [839208879, 1070525250]
I/flutter ( 4599): [839208879]
I/flutter ( 4599): [839208879, 1070525250]
I/flutter ( 4599): [839208879]
I/flutter ( 4599): [839208879, 1070525250]
I/flutter ( 4599): [839208879]
I/flutter ( 4599): [839208879, 1070525250]
I/flutter ( 4599): [839208879]
I/flutter ( 4599): [839208879, 1070525250]
I/flutter ( 4599): [839208879]
I/flutter ( 4599): [839208879, 1070525250]
I/flutter ( 4599): [839208879]
I/flutter ( 4599): [839208879, 1070525250]
I/flutter ( 4599): [839208879]
I/flutter ( 4599): [839208879, 1070525250]
I/flutter ( 4599): [839208879]
I/flutter ( 4599): [839208879, 1070525250]
I/flutter ( 4599): [839208879]
I/flutter ( 4599): [839208879, 1070525250]
I/flutter ( 4599): [839208879]
I/flutter ( 4599): [839208879, 1070525250]
The code should check if I already found a Bluetooth device before pushing data to the cloud, but it doesn't work. Do you know how to fix that?
Thank you, and excuse me for the bad question.
You can check this project. It clearly shows the found Bluetooth device list.
// Copyright 2017, Paul DeMarco.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:flutter_blue_example/widgets.dart';
void main() {
runApp(FlutterBlueApp());
}
class FlutterBlueApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
color: Colors.lightBlue,
home: StreamBuilder<BluetoothState>(
stream: FlutterBlue.instance.state,
initialData: BluetoothState.unknown,
builder: (c, snapshot) {
final state = snapshot.data;
if (state == BluetoothState.on) {
return FindDevicesScreen();
}
return BluetoothOffScreen(state: state);
}),
);
}
}
class BluetoothOffScreen extends StatelessWidget {
const BluetoothOffScreen({Key key, this.state}) : super(key: key);
final BluetoothState state;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.lightBlue,
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(
Icons.bluetooth_disabled,
size: 200.0,
color: Colors.white54,
),
Text(
'Bluetooth Adapter is ${state != null ? state.toString().substring(15) : 'not available'}.',
style: Theme.of(context)
.primaryTextTheme
.subhead
.copyWith(color: Colors.white),
),
],
),
),
);
}
}
class FindDevicesScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Find Devices'),
),
body: RefreshIndicator(
onRefresh: () =>
FlutterBlue.instance.startScan(timeout: Duration(seconds: 4)),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
StreamBuilder<List<BluetoothDevice>>(
stream: Stream.periodic(Duration(seconds: 2))
.asyncMap((_) => FlutterBlue.instance.connectedDevices),
initialData: [],
builder: (c, snapshot) => Column(
children: snapshot.data
.map((d) => ListTile(
title: Text(d.name),
subtitle: Text(d.id.toString()),
trailing: StreamBuilder<BluetoothDeviceState>(
stream: d.state,
initialData: BluetoothDeviceState.disconnected,
builder: (c, snapshot) {
if (snapshot.data ==
BluetoothDeviceState.connected) {
return RaisedButton(
child: Text('OPEN'),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>
DeviceScreen(device: d))),
);
}
return Text(snapshot.data.toString());
},
),
))
.toList(),
),
),
StreamBuilder<List<ScanResult>>(
stream: FlutterBlue.instance.scanResults,
initialData: [],
builder: (c, snapshot) => Column(
children: snapshot.data
.map(
(r) => ScanResultTile(
result: r,
onTap: () => Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
r.device.connect();
return DeviceScreen(device: r.device);
})),
),
)
.toList(),
),
),
],
),
),
),
floatingActionButton: StreamBuilder<bool>(
stream: FlutterBlue.instance.isScanning,
initialData: false,
builder: (c, snapshot) {
if (snapshot.data) {
return FloatingActionButton(
child: Icon(Icons.stop),
onPressed: () => FlutterBlue.instance.stopScan(),
backgroundColor: Colors.red,
);
} else {
return FloatingActionButton(
child: Icon(Icons.search),
onPressed: () => FlutterBlue.instance
.startScan(timeout: Duration(seconds: 4)));
}
},
),
);
}
}
class DeviceScreen extends StatelessWidget {
const DeviceScreen({Key key, this.device}) : super(key: key);
final BluetoothDevice device;
List<int> _getRandomBytes() {
final math = Random();
return [
math.nextInt(255),
math.nextInt(255),
math.nextInt(255),
math.nextInt(255)
];
}
List<Widget> _buildServiceTiles(List<BluetoothService> services) {
return services
.map(
(s) => ServiceTile(
service: s,
characteristicTiles: s.characteristics
.map(
(c) => CharacteristicTile(
characteristic: c,
onReadPressed: () => c.read(),
onWritePressed: () async {
await c.write(_getRandomBytes(), withoutResponse: true);
await c.read();
},
onNotificationPressed: () async {
await c.setNotifyValue(!c.isNotifying);
await c.read();
},
descriptorTiles: c.descriptors
.map(
(d) => DescriptorTile(
descriptor: d,
onReadPressed: () => d.read(),
onWritePressed: () => d.write(_getRandomBytes()),
),
)
.toList(),
),
)
.toList(),
),
)
.toList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(device.name),
actions: <Widget>[
StreamBuilder<BluetoothDeviceState>(
stream: device.state,
initialData: BluetoothDeviceState.connecting,
builder: (c, snapshot) {
VoidCallback onPressed;
String text;
switch (snapshot.data) {
case BluetoothDeviceState.connected:
onPressed = () => device.disconnect();
text = 'DISCONNECT';
break;
case BluetoothDeviceState.disconnected:
onPressed = () => device.connect();
text = 'CONNECT';
break;
default:
onPressed = null;
text = snapshot.data.toString().substring(21).toUpperCase();
break;
}
return FlatButton(
onPressed: onPressed,
child: Text(
text,
style: Theme.of(context)
.primaryTextTheme
.button
.copyWith(color: Colors.white),
));
},
)
],
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
StreamBuilder<BluetoothDeviceState>(
stream: device.state,
initialData: BluetoothDeviceState.connecting,
builder: (c, snapshot) => ListTile(
leading: (snapshot.data == BluetoothDeviceState.connected)
? Icon(Icons.bluetooth_connected)
: Icon(Icons.bluetooth_disabled),
title: Text(
'Device is ${snapshot.data.toString().split('.')[1]}.'),
subtitle: Text('${device.id}'),
trailing: StreamBuilder<bool>(
stream: device.isDiscoveringServices,
initialData: false,
builder: (c, snapshot) => IndexedStack(
index: snapshot.data ? 1 : 0,
children: <Widget>[
IconButton(
icon: Icon(Icons.refresh),
onPressed: () => device.discoverServices(),
),
IconButton(
icon: SizedBox(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Colors.grey),
),
width: 18.0,
height: 18.0,
),
onPressed: null,
)
],
),
),
),
),
StreamBuilder<int>(
stream: device.mtu,
initialData: 0,
builder: (c, snapshot) => ListTile(
title: Text('MTU Size'),
subtitle: Text('${snapshot.data} bytes'),
trailing: IconButton(
icon: Icon(Icons.edit),
onPressed: () => device.requestMtu(223),
),
),
),
StreamBuilder<List<BluetoothService>>(
stream: device.services,
initialData: [],
builder: (c, snapshot) {
return Column(
children: _buildServiceTiles(snapshot.data),
);
},
),
],
),
),
);
}
}
And the widgets.dart:
// Copyright 2017, Paul DeMarco.
// All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
class ScanResultTile extends StatelessWidget {
const ScanResultTile({Key key, this.result, this.onTap}) : super(key: key);
final ScanResult result;
final VoidCallback onTap;
Widget _buildTitle(BuildContext context) {
if (result.device.name.length > 0) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
result.device.name,
overflow: TextOverflow.ellipsis,
),
Text(
result.device.id.toString(),
style: Theme.of(context).textTheme.caption,
)
],
);
} else {
return Text(result.device.id.toString());
}
}
Widget _buildAdvRow(BuildContext context, String title, String value) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(title, style: Theme.of(context).textTheme.caption),
SizedBox(
width: 12.0,
),
Expanded(
child: Text(
value,
style: Theme.of(context)
.textTheme
.caption
.apply(color: Colors.black),
softWrap: true,
),
),
],
),
);
}
String getNiceHexArray(List<int> bytes) {
return '[${bytes.map((i) => i.toRadixString(16).padLeft(2, '0')).join(', ')}]'
.toUpperCase();
}
String getNiceManufacturerData(Map<int, List<int>> data) {
if (data.isEmpty) {
return null;
}
List<String> res = [];
data.forEach((id, bytes) {
res.add(
'${id.toRadixString(16).toUpperCase()}: ${getNiceHexArray(bytes)}');
});
return res.join(', ');
}
String getNiceServiceData(Map<String, List<int>> data) {
if (data.isEmpty) {
return null;
}
List<String> res = [];
data.forEach((id, bytes) {
res.add('${id.toUpperCase()}: ${getNiceHexArray(bytes)}');
});
return res.join(', ');
}
#override
Widget build(BuildContext context) {
return ExpansionTile(
title: _buildTitle(context),
leading: Text(result.rssi.toString()),
trailing: RaisedButton(
child: Text('CONNECT'),
color: Colors.black,
textColor: Colors.white,
onPressed: (result.advertisementData.connectable) ? onTap : null,
),
children: <Widget>[
_buildAdvRow(
context, 'Complete Local Name', result.advertisementData.localName),
_buildAdvRow(context, 'Tx Power Level',
'${result.advertisementData.txPowerLevel ?? 'N/A'}'),
_buildAdvRow(
context,
'Manufacturer Data',
getNiceManufacturerData(
result.advertisementData.manufacturerData) ??
'N/A'),
_buildAdvRow(
context,
'Service UUIDs',
(result.advertisementData.serviceUuids.isNotEmpty)
? result.advertisementData.serviceUuids.join(', ').toUpperCase()
: 'N/A'),
_buildAdvRow(context, 'Service Data',
getNiceServiceData(result.advertisementData.serviceData) ?? 'N/A'),
],
);
}
}
class ServiceTile extends StatelessWidget {
final BluetoothService service;
final List<CharacteristicTile> characteristicTiles;
const ServiceTile({Key key, this.service, this.characteristicTiles})
: super(key: key);
#override
Widget build(BuildContext context) {
if (characteristicTiles.length > 0) {
return ExpansionTile(
title: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Service'),
Text('0x${service.uuid.toString().toUpperCase().substring(4, 8)}',
style: Theme.of(context)
.textTheme
.body1
.copyWith(color: Theme.of(context).textTheme.caption.color))
],
),
children: characteristicTiles,
);
} else {
return ListTile(
title: Text('Service'),
subtitle:
Text('0x${service.uuid.toString().toUpperCase().substring(4, 8)}'),
);
}
}
}
class CharacteristicTile extends StatelessWidget {
final BluetoothCharacteristic characteristic;
final List<DescriptorTile> descriptorTiles;
final VoidCallback onReadPressed;
final VoidCallback onWritePressed;
final VoidCallback onNotificationPressed;
const CharacteristicTile(
{Key key,
this.characteristic,
this.descriptorTiles,
this.onReadPressed,
this.onWritePressed,
this.onNotificationPressed})
: super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder<List<int>>(
stream: characteristic.value,
initialData: characteristic.lastValue,
builder: (c, snapshot) {
final value = snapshot.data;
return ExpansionTile(
title: ListTile(
title: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Characteristic'),
Text(
'0x${characteristic.uuid.toString().toUpperCase().substring(4, 8)}',
style: Theme.of(context).textTheme.body1.copyWith(
color: Theme.of(context).textTheme.caption.color))
],
),
subtitle: Text(value.toString()),
contentPadding: EdgeInsets.all(0.0),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(
Icons.file_download,
color: Theme.of(context).iconTheme.color.withOpacity(0.5),
),
onPressed: onReadPressed,
),
IconButton(
icon: Icon(Icons.file_upload,
color: Theme.of(context).iconTheme.color.withOpacity(0.5)),
onPressed: onWritePressed,
),
IconButton(
icon: Icon(
characteristic.isNotifying
? Icons.sync_disabled
: Icons.sync,
color: Theme.of(context).iconTheme.color.withOpacity(0.5)),
onPressed: onNotificationPressed,
)
],
),
children: descriptorTiles,
);
},
);
}
}
class DescriptorTile extends StatelessWidget {
final BluetoothDescriptor descriptor;
final VoidCallback onReadPressed;
final VoidCallback onWritePressed;
const DescriptorTile(
{Key key, this.descriptor, this.onReadPressed, this.onWritePressed})
: super(key: key);
#override
Widget build(BuildContext context) {
return ListTile(
title: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Descriptor'),
Text('0x${descriptor.uuid.toString().toUpperCase().substring(4, 8)}',
style: Theme.of(context)
.textTheme
.body1
.copyWith(color: Theme.of(context).textTheme.caption.color))
],
),
subtitle: StreamBuilder<List<int>>(
stream: descriptor.value,
initialData: descriptor.lastValue,
builder: (c, snapshot) => Text(snapshot.data.toString()),
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(
Icons.file_download,
color: Theme.of(context).iconTheme.color.withOpacity(0.5),
),
onPressed: onReadPressed,
),
IconButton(
icon: Icon(
Icons.file_upload,
color: Theme.of(context).iconTheme.color.withOpacity(0.5),
),
onPressed: onWritePressed,
)
],
),
);
}
}
class AdapterStateTile extends StatelessWidget {
const AdapterStateTile({Key key, #required this.state}) : super(key: key);
final BluetoothState state;
#override
Widget build(BuildContext context) {
return Container(
color: Colors.redAccent,
child: ListTile(
title: Text(
'Bluetooth adapter is ${state.toString().substring(15)}',
style: Theme.of(context).primaryTextTheme.subhead,
),
trailing: Icon(
Icons.error,
color: Theme.of(context).primaryTextTheme.subhead.color,
),
),
);
}
}
I am creating an e-commerce app where homepage is kind of page where all fields like categories and other info is given.
here is my flow of screens ...
HomeScreen -> CategoryPage -> ProductByCategory -> ProductLandingPage
I am getting error. New to Coding and learning Providers for 1st time, Not able to resolve this issue.
Error: Could not find the correct Provider above this ProductLandingPage Widget
To fix, please:
Ensure the Provider is an ancestor to this ProductLandingPage Widget
Provide types to Provider
Provide types to Consumer
void main() {
runApp(MaterialApp(
home: MultiProvider(
providers: [
ChangeNotifierProvider.value(
value: Cart(),
)
],
child: HomeScreen(),
),
debugShowCheckedModeBanner: false,
));
}
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.redAccent,
title: Text('Factory2Homes'),
actions: <Widget>[
IconButton(icon: Icon(Icons.search), onPressed: () {}),
Consumer<Cart>(
builder: (_, cart, ch) => Badge(
child: ch,
value: cart.itemCount.toString(),
),
child: IconButton(
icon: Icon(
Icons.shopping_cart,
),
onPressed: () {
},
),
),
],
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(height: 500, child: CategoryPage()),
],
),
),
);
}
}
class CategoryPage extends StatefulWidget {
#override
_CategoryPageState createState() => _CategoryPageState();
}
class _CategoryPageState extends State<CategoryPage> {
#override
Widget build(BuildContext context) {
return FutureBuilder<List<AllCategory>>(
future: getCategoryList(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? ListOfCategories(
categories: snapshot.data,
)
: Center(
child: CircularProgressIndicator(
backgroundColor: Colors.red,
));
},
);
}
}
class ListOfCategories extends StatelessWidget {
final List<AllCategory> categories;
ListOfCategories({this.categories});
#override
Widget build(BuildContext context) {
return GridView.builder(
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: categories.length,
itemBuilder: (context, index) {
return InkWell(
onTap: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => ProductByCategory(category: categories[index],)));
},
child: Image.network(categories[index].categoryIcon));
},
);
}
}
class ProductByCategory extends StatefulWidget {
final AllCategory category;
final CarouselSlider carouselslider;
ProductByCategory({this.category, this.carouselslider});
#override
_ProductByCategoryState createState() => _ProductByCategoryState();
}
class _ProductByCategoryState extends State<ProductByCategory> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
Consumer<Cart>(
builder: (_, cart, ch) => Badge(
child: ch,
value: cart.itemCount.toString(),
),
child: IconButton(
icon: Icon(
Icons.shopping_cart,
),
onPressed: () {
},
),
),
IconButton(icon: Icon(Icons.search), onPressed: () {}),
],
),
body: FutureBuilder<List<Product>>(
future: getCategoryByProduct(http.Client(), widget.category.id),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
if (snapshot.hasData) {
return ProductByCategoryScreen(
product: snapshot.data,
);
} else {
return Center(child: CircularProgressIndicator());
}
},
),
);
}
}
class ProductByCategoryScreen extends StatefulWidget {
final List<Product> product;
ProductByCategoryScreen({this.product});
#override
_ProductByCategoryScreenState createState() =>
_ProductByCategoryScreenState();
}
class _ProductByCategoryScreenState extends State<ProductByCategoryScreen> {
#override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: widget.product.length,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ProductLandingPage(widget.product[index])));
},
child:
Card(child: Image.network(widget.product[index].productPhoto)));
},
);
}
}
class ProductLandingPage extends StatefulWidget {
final Product product;
ProductLandingPage(this.product);
#override
_ProductLandingPageState createState() => _ProductLandingPageState();
}
class _ProductLandingPageState extends State<ProductLandingPage> {
#override
Widget build(BuildContext context) {
final cart = Provider.of<Cart>(context, listen: false);
return Scaffold(
appBar: AppBar(
actions: <Widget>[
],
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
color: Colors.green,
height: MediaQuery.of(context).size.height / 2,
child: Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Image.network(widget.product.productPhoto),
),
),
Divider(
thickness: 1,
),
Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(this.widget.product.productName),
),
),
Divider(),
Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 10.0),
child: Text(
'₹' + '${this.widget.product.productSalePrice}',
style: TextStyle(
fontSize: 30, fontWeight: FontWeight.w500),
),
),
],
),
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Center(
child: Text(
'MRP:' + '${this.widget.product.productListPrice}'),
),
),
],
),
),
Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 10.0),
child: Text(
'Description',
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
),
),
],
),
Container(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Text(this.widget.product.productDescription),
),
),
],
),
),
bottomNavigationBar: Container(
width: MediaQuery.of(context).size.width,
height: 45.0,
child: RaisedButton(
onPressed: () {
cart.addItem(
'${widget.product.productId}',
widget.product.productListPrice,
widget.product.productName,
);
},
color: Colors.redAccent,
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.card_travel,
color: Colors.white,
),
SizedBox(
width: 4.0,
),
Text(
"ADD TO CART",
style: TextStyle(color: Colors.white),
),
],
),
),
),
));
}
}
class CartItem {
final String id;
final String title;
final int quantity;
final int price;
CartItem({
this.id,
this.title,
this.quantity,
this.price,
});
}
class Cart with ChangeNotifier {
Map<String, CartItem> _items;
Map<String, CartItem> get items {
return {..._items};
}
int get itemCount{
return _items==null ?0 :_items.length;
}
void addItem(
String productId,
int productListPrice,
String productName,
) {
if (_items.containsKey(productId)) {
_items.update(
productId,
(existingCartItem) => CartItem(
id: existingCartItem.id,
title: existingCartItem.title,
price: existingCartItem.price,
quantity: existingCartItem.quantity + 1,
));
} else {
_items.putIfAbsent(
productId,
() => CartItem(
id: DateTime.now().toString(),
title: productName,
price: productListPrice,
quantity: 1,
));
}
}
}
The idea of Provider is to lift the state management above the widgets so different children can easily access its state. So it would be helpful if you moved the HTTP request from the widget tree (where it will be called every time the UI updates, so users use more bandwidth than needed) to a provider that is created above the tree. Therefore the state doesn't need to passed around from widget to widget.
Try watching this amazing talk from the flutter team to get a better understanding of how to use provider: https://youtu.be/d_m5csmrf7I
Bdw read this StackOverflow answer about why .value isn't what you desire: How to deal with unwanted widget build?
So you should make the app like this
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => Cart(),
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Factory2Homes',
home: HomeScreen(),
),
);
}
}
i got it working by changing main.dart code to below code:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider.value(value: Cart(),
child:MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Factory2Homes',
home: HomeScreen(),
),);
}
}