I'm getting started with flutter/dart and I'm trying to implement a simple note app using InheritedWidget and TextControllers, but when I add or edit some note it doesn't update the main screen. I printed the new notes list in console and it is updated with the addings and editings but is not updated in main screen, still showing the initial note list ({'title': 'someTitle1', 'text': 'someText1'}, ...).
main.dart :
void main() => runApp(NoteInheritedWidget(
MaterialApp(
title: 'Notes App',
home: HomeList(),
),
));
home screen scaffold body :
List<Map<String, String>> get _notes => NoteInheritedWidget.of(context).notes;
...
body: ListView.builder(
itemCount: _notes.length,
itemBuilder: (context, index) {
return Card(
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 7),
child: ListTile(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => NotePage(noteMode: NoteMode.Editing, index: index))
);
print(_notes);
},
trailing: Icon(Icons.more_vert),
title: _NoteTitle(_notes[index]['title']),
subtitle: _NoteText(_notes[index]['text']),
),
);
},
),
Add/Edit Note page :
enum NoteMode {
Adding,
Editing
}
class NotePage extends StatefulWidget {
final NoteMode noteMode;
final int index;
const NotePage ({this.noteMode, this.index});
#override
_NotePageState createState() => _NotePageState();
}
class _NotePageState extends State<NotePage> {
final TextEditingController _titleController = TextEditingController();
final TextEditingController _textController = TextEditingController();
List<Map<String, String>> get _notes => NoteInheritedWidget.of(context).notes;
#override
void didChangeDependencies() {
if (widget.noteMode == NoteMode.Editing) {
_titleController.text = _notes[widget.index]['text'];
_textController.text = _notes[widget.index]['title'];
}
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.noteMode == NoteMode.Adding ? 'Add Note' : 'Edit Note'
),
centerTitle: true,
backgroundColor: Colors.indigo[700],
),
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
TextField(
controller: _titleController,
decoration: InputDecoration(
hintText: 'Note Title',
border: OutlineInputBorder(),
),
),
SizedBox(height: 20),
TextField(
controller: _textController,
maxLines: 20,
decoration: InputDecoration(
hintText: 'Note Text',
border: OutlineInputBorder(),
),
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
_NoteButton(Icons.save, 'Save', () {
final title = _titleController.text;
final text = _textController.text;
if (widget.noteMode == NoteMode.Adding) {
_notes.add({'title': title, 'text': text});
print(_notes);
} else if (widget.noteMode == NoteMode.Editing) {
_notes[widget.index] = {'title': title, 'text': text};
print(_notes);
}
Navigator.pop(context);
}),
_NoteButton(Icons.clear, 'Discard', () {
Navigator.pop(context);
}),
if (widget.noteMode == NoteMode.Editing)
_NoteButton(Icons.delete, 'Delete', () {
_notes.removeAt(widget.index);
Navigator.pop(context);
}),
],
),
],
),
),
),
);
}
}
InheritedWidget :
class NoteInheritedWidget extends InheritedWidget {
final notes = [
{'title': 'someTitle1', 'text': 'someText1'},
{'title': 'someTitle2', 'text': 'someText2'},
{'title': 'someTitle3', 'text': 'someText3'}
];
NoteInheritedWidget(Widget child) : super(child: child);
static NoteInheritedWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<NoteInheritedWidget>();
}
#override
bool updateShouldNotify(NoteInheritedWidget old) {
return old.notes != notes;
}
}
Home screen after add a note :
HomeScreen
List of notes printed in console after add a note :
I/flutter (18079): [{title: someTitle1, text: someText1}, {title: someTitle2, text: someText2}, {title: someTitle3, text: someText3}, {title: NewAddNoteTitle, text: NewAddNoteText}]
I'm using Android Studio and a real device instead an emulator.
I can't find the error and if you have another way to do this 'update' please show me.
I found a solution using the onPressed method as async and then an empty setState, is there any problem for the code doing this?
code:
child: ListTile(
onTap: () async {
await Navigator.push(context,
MaterialPageRoute(builder: (context) => NotePage(noteMode: NoteMode.Editing, index: index))
);
setState(() {});
print(_notes);
},
...
floatingActionButton: FloatingActionButton(
onPressed: () async {
await Navigator.push(context,
MaterialPageRoute(builder: (context) => NotePage(noteMode: NoteMode.Adding))
);
setState(() {});
print(_notes.length);
print(_notes);
},
Related
Frontend looks of read and write command.
I need to change the design of read and write command on the bluetooth devcie screen.
I used the code from pauldemarco git repository.
But the device screen frontend design does not suit good for my application.
Can anyone share how to change the design of upload and download signal button on the user interface?
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'widgets.dart';
import 'dart:ui' as ui;
import 'package:flutter/rendering.dart';
import 'package:dproject/qrcode.dart';
void main() {
// RenderErrorBox.backgroundColor = Colors.transparent;
ErrorWidget.builder = (FlutterErrorDetails details) => Scaffold(body:Center(child: Text("Click the Refresh Button"),));
runApp(FlutterBlueApp());
// static ErrorWidgetBuilder builder = _defaultErrorWidgetBuilder;
// RenderErrorBox.textStyle = ui.TextStyle(color: Colors.transparent);
}
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: 8)),
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.toString()),
// subtitle: Text(d.id.toString()),
trailing: StreamBuilder<BluetoothDeviceState>(
stream: d.state,
initialData: BluetoothDeviceState.disconnected,
builder: (c, snapshot) {
if (snapshot.data ==
BluetoothDeviceState.connected) {
return ElevatedButton(
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){
List<ScanResult> data = [];
// print("sh");
for(int i=0;i<snapshot.data!.length;i++)
{
// print("sh");
// print(snapshot.data![i]);
if(snapshot.data![i].device.id.toString()=='FC:67:78:5D:96:EF'){
data.add(snapshot.data![i]);
}
}
// print(data);
return Column(
children: data
.map(
(r) => ScanResultTile(
result: r,
onTap: () => Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
r.device.connect();
return DeviceScreen(device: r.device); //FlutterBlueApp();
})),
),
)
.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: 8)));
}
},
),
);
}
}
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) {
List<BluetoothService> data = [];
print("sh%%%%%%%%%%%%%%%%%%%%%%####");
for(int i=0;i<services.length;i++)
{
print("sh####################################");
// print(services[i]);
if(i==2){
data.add(services[i]);
}
}
// print(data);
return data //services
.map(
(s) => ServiceTile(
service: s,
characteristicTiles: s.characteristics
.map(
(c) => CharacteristicTile(
characteristic: c,
onReadPressed: () {
c.read();
},
onWritePressed: () async {
await c.write([1], withoutResponse: false);
await c.read();
// ElevatedButton(
// // style: style,
// onPressed: null,
// child: const Text('Disabled'),
// );
},
onNotificationPressed: () async {
await c.setNotifyValue(!c.isNotifying);
await c.read();
},
descriptorTiles: c.descriptors
.map(
(d) => DescriptorTile(
descriptor: d,
onReadPressed: () => d.read(),
onWritePressed: () => d.write([1]),
),
)
.toList(),
),
)
.toList(),
),
)
.toList();
// print(c.read);
}
// #override
// State<DeviceScreen> createState() => _DeviceScreenState();
// }
// class _DeviceScreenState extends State<DeviceScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("BITLOCK"),//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 TextButton(
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(((){
if(snapshot.data.toString().split('.')[1]=="connected"){
return 'Your Lock is ${snapshot.data.toString().split('.')[1]} to your device.';
}
return 'Your Lock is ${snapshot.data.toString().split('.')[1]} from your device.';
})()),
// 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.lock_open),
onPressed: () => device.requestMtu(223),
),
),
),
StreamBuilder<List<BluetoothService>>(
stream: device.services,
initialData: [],
builder: (c, snapshot) {
return Column(
children: _buildServiceTiles(snapshot.data!),
);
},
),
],
),
),
);
}
}
Thanks in advance.
Here is the solution I found.
You will find widgets.dart in the lib folder in which you are working with main.dart and other files.
You can edit this file according to required design.
In my case, I need to change Iconbutton icon in characteristics tile to change the design of read and write button.
I don't know what happened, but I tried to fix this by making it nullable, but it didn't work.
I wanted to view elements from the database, therefore i put them in "for" loop..
but it still showing me exception _TypeError (type 'Null' is not a subtype of type 'String')
So what should I do to fix this?
This is a screenshot of the exception:
enter image description here
And this is my code:
`import 'pac`kage:blackboard/view/Teacher/Addcourse1.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:blackboard/constraints/textstyle.dart';
import 'package:flutter/material.dart';
import 'package:blackboard/setting/colors.dart';
class CoursesT extends StatefulWidget {
const CoursesT({Key? key}) : super(key: key);
#override
State<CoursesT> createState() => _CoursesTState();
}
class _CoursesTState extends State<CoursesT> {
// Getting Student all Records
final Stream<QuerySnapshot>? studentRecords =
FirebaseFirestore.instance.collection('CourseStudent').snapshots();
// For Deleting Users
CollectionReference? delUser =
FirebaseFirestore.instance.collection('CourseStudent');
Future<void> _delete(id) {
return delUser!
.doc(id)
.delete()
.then((value) => print('User Deleted'))
.catchError((_) => print('Something Error In Deleted User'));
}
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: studentRecords,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
print('Something Wrong in HomePage');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
// Storing Data
final List? firebaseData = [];
snapshot.data?.docs.map((DocumentSnapshot documentSnapshot) {
Map store = documentSnapshot.data() as Map<String, dynamic>;
firebaseData!.add(store);
store['id'] = documentSnapshot.id;
}).toList();
return Scaffold(
appBar: AppBar(
backgroundColor: BBColors.primary6,
title: Text("Your Courses"),
leading: Icon(Icons.menu, color: Colors.white),
actions: [
Icon(
Icons.search,
),
SizedBox(
width: 20,
),
],
),
body: Container(
margin: const EdgeInsets.all(8),
child: SingleChildScrollView(
child: ListView(
shrinkWrap: true,
children: [
for (var i = 0; i < firebaseData!.length; i++) ...[
Card(
elevation: 4.0,
child: Column(
children: [
ListTile(
title: Text(
firebaseData[i]['Course Title'],
),
subtitle: Text(
firebaseData[i]['Course Group'],
),
trailing: IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const AddCourse1(),
),
);
},
icon: const Icon(
Icons.add,
color: BBColors.bg1,
),
),
),
Container(
padding: EdgeInsets.all(16.0),
alignment: Alignment.centerLeft,
child: Text(
firebaseData[i]['Course Description'],
),
),
ButtonBar(
children: [
// IconButton(
// onPressed: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => EditPage(
// docID: firebaseData[i]['id'],
// ),
// ),
// );
// },
// icon: const Icon(
// Icons.edit,
// color: Colors.orange,
// ),
// ),
IconButton(
onPressed: () {
_delete(firebaseData[i]['id']);
//print(firebaseData);
},
icon: const Icon(
Icons.delete,
color: Colors.red,
),
),
],
)
],
)),
], //this is loop
],
),
),
),
);
});
}
}
just check wheather you are getting data from firebase in firebaseData variable and also refactor you code like this
subtitle: Text(firebaseData[i]['Course Group']??"Some Text",),
If your variable return null then it will print the hard-coded text on the right and save app from crashing.
Everything related services at any point might received as "null". I suggest making any variable depends on internet interaction, nullable. So when you use them you can have placeholder values.
For example
String? theUserNameFetchedFromInternet
// while using
Text(theUserNameFetchedFromInternet ?? "john")
This type of handling prevents lots of crash over-time & is there is why dart is null-safety.
I am currently running into an issue with my ble project for flutter. I am using the flutter blue package by Paul DeMarco and the additional page for the app is based on "ThatProject" dust sensor from youtube. I have an Adafruit Feather 32u4 board, and I am attempting to notify the client (my flutter app) that It has a series of numbers to send, but I am not getting any output. I am able to connect to the device, and seem to properly send the service UUID and characteristic UUID, but im not sure if it is coming with proper properties.
I am using the adafruit BLE code to program the board, and I can get the values if I use adafruit's app. I am just trying to get the values on my own flutter app.
I am running into an error as follows:
E/flutter (32139): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: PlatformException(set_notification_error, could not locate CCCD descriptor for characteristic: 6e400002-b5a3-f393-e0a9-e50e24dcca9e, null, null)
Here is my code. I believe the missing CCCD is coming from this part:
import 'dart:async';
import 'dart:convert' show utf8;
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:oscilloscope/oscilloscope.dart';
class SensorPage extends StatefulWidget {
const SensorPage({Key key, this.device}) : super(key: key);
final BluetoothDevice device;
#override
_SensorPageState createState() => _SensorPageState();
}
class _SensorPageState extends State<SensorPage> {
final String SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
final String CHARACTERISTIC_UUID = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";
bool isReady;
Stream<List<int>> stream;
List<double> traceDust = List();
#override
void initState() {
super.initState();
isReady = false;
connectToDevice();
}
connectToDevice() async {
// if (widget.device == null) {
// // _Pop();
// return;
// }
//timeout timer, watchdog timer if you will
new Timer(const Duration(seconds: 15), () {
if (!isReady) {
disconnectFromDevice();
// _Pop();
}
});
await widget.device.connect();
discoverServices();
}
disconnectFromDevice() {
if (widget.device == null) {
//_Pop();
return;
}
widget.device.disconnect();
}
discoverServices() async {
// if (widget.device == null) {
// // _Pop();
// return;
// }
BluetoothCharacteristic ss;
List<BluetoothService> services = await widget.device.discoverServices();
services.forEach((service) {
debugPrint("This Service UUID is!${service.uuid.toString()}");
if (service.uuid.toString() == SERVICE_UUID) {
service.characteristics.forEach((characteristic) {
debugPrint("This char UUID is!${characteristic.uuid.toString()}");
if (characteristic.uuid.toString() == CHARACTERISTIC_UUID) {
debugPrint("Here is !isNotifying: ${!characteristic.isNotifying}");
debugPrint("Here is characteristic.value: ${characteristic.value}");
ss = characteristic;
stream = ss.value;
setState(() {
isReady = true;
});
} //this one
});
} //this one
});
await ss.setNotifyValue(true);
stream = ss.value;
if (!isReady) {
// _Pop();
}
}
Future<bool> _onWillPop() {
return showDialog(
context: context,
builder: (context) =>
new AlertDialog(
title: Text('Are you sure?'),
content: Text('Do you want to disconnect device and go back?'),
actions: <Widget>[
new FlatButton(
onPressed: () => Navigator.of(context).pop(false),
child: new Text('No')),
new FlatButton(
onPressed: () {
disconnectFromDevice();
Navigator.of(context).pop(true);
},
child: new Text('Yes')),
],
) ??
false);
}
// _Pop() {
// Navigator.of(context).pop(true);
// }
String _dataParser(List<int> dataFromDevice) {
debugPrint("current value is-> ${utf8.decode(dataFromDevice)}");
return utf8.decode(dataFromDevice);
}
#override
Widget build(BuildContext context) {
Oscilloscope oscilloscope = Oscilloscope(
showYAxis: true,
padding: 0.0,
backgroundColor: Colors.black,
traceColor: Colors.white,
yAxisMax: 3000.0,
yAxisMin: 0.0,
dataSet: traceDust,
);
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
appBar: AppBar(
title: Text('Optical Dust Sensor'),
),
body: Container(
child: !isReady
? Center(
child: Text(
"Waiting...",
style: TextStyle(fontSize: 24, color: Colors.red),
),
)
: Container(
child: StreamBuilder<List<int>>(
stream: stream,
builder: (BuildContext context,
AsyncSnapshot<List<int>> snapshot) {
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
if (snapshot.connectionState ==
ConnectionState.active) {
debugPrint("snapshot.error: ${snapshot.error}.");
debugPrint("snapshot.data: ${snapshot.error}.");
debugPrint(
"snapshot.connectionState: ${snapshot.connectionState}.");
debugPrint("snapshot.hasdata?: ${snapshot.hasData}.");
var currentValue = _dataParser(snapshot.data);
traceDust.add(double.tryParse(currentValue) ?? 0);
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Current value from Sensor',
style: TextStyle(fontSize: 14)),
Text('$currentValue ug/m3',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24))
]),
),
Expanded(
flex: 1,
child: oscilloscope,
)
],
));
} else {
return Text('Check the stream');
}
},
),
)),
),
);
}
}
// 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 'package:flutter/material.dart';
import 'package:K9Harness/Pages/Sensor_page.dart';
import 'package:K9Harness/Bluetooth/widgets.dart';
import 'package:flutter_blue/flutter_blue.dart';
class MyBluetoothPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
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.toString().substring(15)}.',
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(
scanMode: ScanMode.balanced,
withServices: [
Guid("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
], //FIXME check the other ways where ".startScan" is implemented
timeout: Duration(seconds: 4))
.catchError((error) {
print("error starting scan $error");
}),
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 SensorPage(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(
scanMode: ScanMode.balanced,
withServices: [
Guid("6E400001-B5A3-F393-E0A9-E50E24DCCA9E")
], //FIXME check the other ways where ".startScan" is implemented
timeout: Duration(seconds: 4))
.catchError((error) {
print("error starting scan $error");
}));
}
},
),
);
}
}
class DeviceScreen extends StatelessWidget {
const DeviceScreen({Key key, this.device}) : super(key: key);
final BluetoothDevice device;
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: () => c.write([13, 24]),
onNotificationPressed: () =>
c.setNotifyValue(!c.isNotifying),
descriptorTiles: c.descriptors
.map(
(d) => DescriptorTile(
descriptor: d,
onReadPressed: () => d.read(),
onWritePressed: () => d.write([11, 12]),
),
)
.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),
);
},
),
],
),
),
);
}
}
I figured it out, I had my characteristic UUID to be 6e400002-b5a3-f393-e0a9-e50e24dcca9e and it should have been 6e400003-b5a3-f393-e0a9-e50e24dcca9e because for the feather, the notification/notify characteristic is mandatory to be 0x0003 instead of the 2. It then passed the CCCD properly when I changed this value.
I have a louded information in RealTime database in format for example:
record
0
Club: "Club1"
Name: "Ronaldo"
Place: "London"
date: "25.07.2020"
email: "flutter#gmail.com"
phone: "12345678"
I have created a list that consists of names and clubs and I want to go to the full information according to the form by clicking on the Name, but I can't write the code. Please help to the new programmer
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:.../anketa/tile.dart';
class Anketa extends StatefulWidget {
Anketa({Key key, this.title}) : super(key: key);
final String title;
#override
_AnketaState createState() => _AnketaState();
}
class _AnketaState extends State<Anketa> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Registration form",
style: TextStyle(
fontWeight: FontWeight.w200,
fontSize: 30,
fontFamily: 'Roboto',
fontStyle: FontStyle.italic)),
RegisterStudent(),
]),
)),
);
}
}
class RegisterStudent extends StatefulWidget {
RegisterStudent({Key key}) : super(key: key);
#override
_RegisterStudentState createState() => _RegisterStudentState();
}
class _RegisterStudentState extends State<RegisterStudent> {
final _formKey = GlobalKey<FormState>();
final listOfClubs = ["Club1", "Club2", "Club3", "Club4"];
String dropdownValue = "Club1";
final clubController = TextEditingController();
final nameController = TextEditingController();
final placeController = TextEditingController();
final dateController = TextEditingController();
final emailController = TextEditingController();
final phoneController = TextEditingController();
final rawController = TextEditingController();
final dbRef = FirebaseDatabase.instance.reference().child("record");
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(children: <Widget>[
Padding(
padding: EdgeInsets.all(20.0),
child: TextFormField(
controller: nameController,
decoration: InputDecoration(
labelText: "EnterName",
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
// The validator receives the text that the user has entered.
validator: (value) {
if (value.isEmpty) {
return "Enter name";
}
return null;
},
),
),
Padding(
padding: EdgeInsets.all(20.0),
child: DropdownButtonFormField(
value: dropdownValue,
icon: Icon(Icons.arrow_downward),
decoration: InputDecoration(
labelText: "Club",
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
items: listOfClubs.map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList(),
onChanged: (String newValue) {
setState(() {
this.dropdownValue = newValue;
});
},
validator: (value) {
if (value.isEmpty) {
return 'Club';
}
return null;
},
),
),
Padding(
padding: EdgeInsets.all(20.0),
child: TextFormField(
keyboardType: TextInputType.number,
controller: dateController,
decoration: InputDecoration(
labelText: "Date",
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
// The validator receives the text that the user has entered.
validator: (value) {
if (value.isEmpty) {
return 'Date';
}
return null;
},
),
),
Padding(
padding: EdgeInsets.all(20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
RaisedButton(
color: Colors.lightBlue,
onPressed: () {
if (_formKey.currentState.validate()) {
dbRef.push().set({
"Name": nameController.text,
"date": dateController.text,
"Club": dropdownValue
}).then((_) {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('Add')));
dateController.clear();
nameController.clear();
}).catchError((onError) {
Scaffold.of(context)
.showSnackBar(SnackBar(content: Text(onError)));
});
}
},
child: Text('Enter'),
),
RaisedButton(
color: Colors.amber,
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ListOfNames()),
);
},
child: Text('Go to'),
),
],
)),
])));
}
#override
void dispose() {
super.dispose();
dateController.dispose();
nameController.dispose();
}
}
and this is the second page with a list, from where I want to go to full information
import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';
class ListOfNames extends StatelessWidget {
final dbRef = FirebaseDatabase.instance.reference().child("record");
List<Map<dynamic,dynamic>> lists = List();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey.shade300,
appBar: AppBar(
backgroundColor: Colors.deepPurple,
title: Text("List of students"),
),
body: StreamBuilder(
stream: dbRef.onValue,
builder: (context, AsyncSnapshot<Event> snapshot) {
if (snapshot.hasData) {
lists.clear();
DataSnapshot dataValues = snapshot.data.snapshot;
Map<dynamic, dynamic> values = dataValues.value;
values.forEach((key, values) {
lists.add(values);
});
return new ListView.builder(
shrinkWrap: true,
itemCount: lists.length,
itemBuilder: (BuildContext context, int index) {
itemBuilder: (BuildContext context, int index) {
return ListTile(
title:Text(lists[index]["Name"], style: TextStyle(height: 2.5, fontSize:20.0),),
subtitle:Text(lists[index]['Club'], style: TextStyle(fontSize:16.0),),
onTap: (){
// in this line i have a problem...
Navigator.push(context, MaterialPageRoute(builder: (context) => DetailPage(snapshot.data[index]['Name']),
)
);
},
);
});
}
return CircularProgressIndicator();
})
);
}
}
I want to create such a page:
class DetailPage extends StatelessWidget {
List<Map<dynamic,dynamic>> data ;
DetailPage ({this.data});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(data.name),
// here I want to show the all information from the form about one person from the list
),
);
}
}
In the body of the scaffold of 'DetailsPage' use 'Card' widget with 'Text' widgets to show the info. Like Card(child:Text('${data.clubLoc}').
I got a quick question about a flutter/dart app I am making throwing this certain error.
It has something to do with my showadddialog class. When I press the flatbutton with the text "save" in _showAddDialog() it works fine but my app crashes if I tap out of the alert dialog window without entering anything or if I press the flatbutton named "delete", and both actions give the same error. however, when I restart I can see that the delete button still worked to delete the events from the shared preferences, it just crashed afterward. What could be causing this in my code? Idk where it could be calling a map on null...
Screenshot reference: https://gyazo.com/f894ae742ea50cd714026b1bbe753678
════════ Exception caught by widgets library ═══════════════════════════════════
The following NoSuchMethodError was thrown building HomePage(dirty, dependencies: [_LocalizationsScope-[GlobalKey#42494], _InheritedTheme], state: _HomePageState#acde6):
The method 'map' was called on null.
Receiver: null
Tried calling: map<Widget>(Closure: (dynamic) => ListTile)
The relevant error-causing widget was
HomePage
package:hello_world/main.dart:16
When the exception was thrown, this was the stack
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
#1 _HomePageState.build
package:hello_world/main.dart:135
#2 StatefulElement.build
package:flutter/…/widgets/framework.dart:4334
#3 ComponentElement.performRebuild
package:flutter/…/widgets/framework.dart:4223
#4 Element.rebuild
package:flutter/…/widgets/framework.dart:3947
...
════════════════════════════════════════════════════════════════════════════════
Code here:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:table_calendar/table_calendar.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Calendar',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
CalendarController _controller;
Map<DateTime, List<dynamic>> _events;
List<dynamic> _selectedEvents;
TextEditingController _eventController;
SharedPreferences prefs;
#override
void initState() {
super.initState();
_controller = CalendarController();
_eventController = TextEditingController();
_events = {};
_selectedEvents = [];
initPrefs();
}
initPrefs() async {
prefs = await SharedPreferences.getInstance();
setState(() {
_events = Map<DateTime, List<dynamic>>.from(
decodeMap(json.decode(prefs.getString("events") ?? "{}"))
);
});
}
Map<String, dynamic> encodeMap(Map<DateTime, dynamic> map) {
Map<String, dynamic> newMap = {};
map.forEach((key, value) {
newMap[key.toString()] = map[key];
});
return newMap;
}
Map<DateTime, dynamic> decodeMap(Map<String, dynamic> map) {
Map<DateTime, dynamic> newMap = {};
map.forEach((key, value) {
newMap[DateTime.parse(key)] = map[key];
});
return newMap;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Calendar'),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TableCalendar(
events: _events,
initialCalendarFormat: CalendarFormat.week,
calendarStyle: CalendarStyle(
canEventMarkersOverflow: true,
todayColor: Colors.orange,
selectedColor: Theme.of(context).primaryColor,
todayStyle: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
color: Colors.white
)
),
headerStyle: HeaderStyle(
centerHeaderTitle: true,
formatButtonDecoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.circular(20.0),
),
formatButtonTextStyle: TextStyle(color: Colors.white),
formatButtonShowsNext: false,
),
startingDayOfWeek: StartingDayOfWeek.sunday,
onDaySelected: (date, events) {
setState(() {
_selectedEvents = events;
});
},
builders: CalendarBuilders(
selectedDayBuilder: (context, date, events) => Container(
margin: const EdgeInsets.all(4.0),
alignment: Alignment.center,
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(10.0)
),
child: Text(
date.day.toString(),
style: TextStyle(color: Colors.white),
)
),
todayDayBuilder: (context, date, events) => Container(
margin: const EdgeInsets.all(4.0),
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.circular(10.0)
),
child: Text(
date.day.toString(),
style: TextStyle(color: Colors.white),
)
),
),
calendarController: _controller,
),
..._selectedEvents.map((event) => ListTile(
title: Text(event),
)),
],
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: _showAddDialog,
),
);
}
_showAddDialog() async {
await showDialog(
context: context,
builder: (context) => AlertDialog(
content: TextField(
controller: _eventController,
),
actions: <Widget>[
FlatButton(
child: Text("Save"),
onPressed: () {
if (_eventController.text.isEmpty) return;
if (_events[_controller.selectedDay] != null) {
_events[_controller.selectedDay].add(_eventController.text);
} else {
_events[_controller.selectedDay] = [
_eventController.text
];
}
prefs.setString("events", json.encode(encodeMap(_events)));
_eventController.clear();
Navigator.pop(context);
},
),
FlatButton(
child: Text("Delete Events"),
onPressed: () {
setState(() {
_events.remove(_controller.selectedDay);
prefs.setString("events", json.encode(encodeMap(_events)));
_eventController.clear();
Navigator.pop(context);
},
);
}
)
],
)
);
setState(() {
_selectedEvents = _events[_controller.selectedDay];
});
}
}
I have gone through your code, and handled delete event null exception as per below.
Change your last setState code with below:
setState(() {
_selectedEvents = _events[_controller.selectedDay] ?? [];
});
Conclusion:
_selectedEvents null value can be handled by ?? [] in your code.