The method 'update' was called on null - forms

When I try to update the task values through the edit page, I get the following error
The method 'update' was called on null.
Receiver: null
Tried calling: update(Instance of 'Tasks')
Form key
final GlobalKey<FormState> _formStateKey = GlobalKey<FormState>();
This is one of the fields for entering text
child: TextFormField(
validator: (value) {
if (value.isEmpty || value.trim() == "") {
return 'This field is required';
}
return null;
},
onSaved: (value) {
_taskName = value;
},
controller: TextEditingController(text: widget.name),
),
The code that is triggered when the "edit" button is pressed.
onPressed: (() {
if (_formStateKey.currentState.validate()) {
_formStateKey.currentState.save();
dbHelper
.update(Tasks(widget.id,
_taskDate,
_taskName,
_taskRepeat,
_taskPriority,
_taskScore,
_taskDone))
.then((data) {
setState(() {
goToMainPage();
});
});
}
}),
The update method by which the task is modified.
Future<int> update(Tasks tasks) async {
var dbClient = await db;
return await dbClient.update(
'tasks',
tasks.toMap(),
where: 'id = ?',
whereArgs: [tasks.id],
);
}
Refreshing from the main page works fine.

From my understanding, it seems like dbHelper is null.
Are you initializing dbHelper anywhere because you haven't provided the
code where you do so.
So, to put it simply, you need to put the code below somewhere in your app.
final dbHelper = Tasks(...) // Fill your data in the 3 dots.

Related

Search bar with isar Database

hi and hello everyone ,
i am new with flutter and dart
and i am start using isar database
and everything is good and ok
but i have problem with Search
i create function for search
getAll(String search) async* {
final isar = await db;
final query = isar.books
.where()
.filter()
.titleContains(search)
.build();
await for (final results in query.watch(fireImmediately: true)) {
if (results.isNotEmpty) {
yield results;
}
}
}
and i add Search i home screen like this in TextField :
onChanged: (value) {
isarService.getAll(search: value);
}
,
but not work with me i try everthing but no work
soory for my english
Inside your getAllBooks() just remove .build()
Stream<List<Book>> getAllBooks({String? search}) async* {
print(search);
final isar = await db;
final query = isar.books
.where()
.filter()
.titleContains(search ?? '', caseSensitive: false);
await for (final results in query.watch(fireImmediately: true)) {
if (results.isNotEmpty) {
yield results;
}
}
}
Convert your book_list_screen.dart class in StatefulWidget, and use a variable.
String search = ""; //for searching
Inside your TextField's onChanged()
onChanged: (value) {
setState(() {
search = value;
});
},
and inside your Expanded widget use something like this
Expanded(
child: StreamBuilder<List<Book>>(
stream: widget.isarService.getAllBooks(search: search),
builder: (context, snapshot) {
...
}
),
maybe you should wait for the response:
onChanged: (value) async {
await isarService.getAll(search: value);
}

An alert dialog is not displayed at the widget load, why?

In my application I am reading a qr code with the flutter_barcode_scanner package, and when reading a valid code it is supposed to show me an alert dialog saying that it is correct, otherwise, well, no.
To implement this package, an example tells us that based on an action (such as pressing a button) start the scanning method, and save what is read in a variable. Thus:
Future<void> scanQr() async {
try {
final qrCode = await FlutterBarcodeScanner.scanBarcode(
'#ffffff', 'Cancel', true, ScanMode.QR);
if (!mounted) {
return;
}
setState(() {
this.qrCode = qrCode.isEmpty
? 'EMPTY'
: qrCode == '-1'
? 'INVALID'
: qrCode;
});
} on PlatformException {
qrCode = 'fail';
}
}
We can see what the set state occupies in order to update the value. This in a variable that, as you can see, is called qrCode. Which in the example puts it in a text widget and it works fine. when reading something, it is updated.
So, what I want to do is a simple validation, and based on said validation, show an alertdialog saying if what was read in the qr is right or wrong.
I have my validation in the same setstate, I ask if a certain part of the read string looks like what I want. Likewise, I have an int variable, which when updated in the set state, draws an alertdialog.
Future<void> scanQr() async {
try {
final qrCode = await FlutterBarcodeScanner.scanBarcode(
'#ffffff', 'Cancel', true, ScanMode.QR);
if (!mounted) {
return;
}
setState(() {
this.qrCode = qrCode.isEmpty
? 'EMPTY'
: qrCode == '-1'
? 'INVALID'
: qrCode;
});
if (qrCode.substring(0, 2) == 'somepattern') {
setState(() {
value = 1;
});
} else {
setState(() {
value = 2;
});
}
//saveContact(qrCode);
} on PlatformException {
qrCode = 'fail';
}
}
In the build method I have:
String qrCode = '';
int value = 0;
#override
Widget build(BuildContext context) {
//Future(() async {
(value == 1)
? alerta('Yeah', "This code are right!")
: (value == 2)
? alerta(
'Oh no!', 'An invalid qr code')
: null;
//});
return Scaffold(...
As you can see, I even try it with a future to give it time to draw, but it doesn't show any alertDialog. The text widget with the each is displayed. Even, i print the value, and its right, prints 2 and 1 respectively; but the alert dialog is never showed
Why doesn't it draw my alert dialog? What am I doing wrong?
Instead of writing the alert dialog logic inside build method, you should have it in scanQR method
Future<void> scanQr() async {
try {
final qrCode = await FlutterBarcodeScanner.scanBarcode(
'#ffffff', 'Cancel', true, ScanMode.QR);
if (!mounted) {
return;
}
setState(() {
this.qrCode = qrCode.isEmpty
? 'EMPTY'
: qrCode == '-1'
? 'INVALID'
: qrCode;
});
if (qrCode.substring(0, 2) == 'somepattern') {
setState(() {
value = 1;
alerta('Yeah', "This code are right!");
});
} else {
setState(() {
value = 2;
alerta(
'Oh no!', 'An invalid qr code');
});
}
//saveContact(qrCode);
} on PlatformException {
qrCode = 'fail';
}
}
Try this and let me know.

How can I set values to Firestore from a list

I am trying to make a grading app and I wanted to list students from Firestore and fill their grades and I have got the list for the students list but I cant get the number to work I have a working attendance code that only uses boolean values
here is the working code for booleans
Switch(
value: switchStates[
students[index].data()["id"]],
onChanged: (value) {
setState(() {
stateofvalue = value;
});
onSwitchChanged(
students[index].data()["id"], value);
},
activeColor: Color(0xff2fbd9f),
),
this is how I call the values
CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> markAttendance() {
Map<String, dynamic> resultMap = {};
var formatter = new DateFormat('dd-MM-yyyy');
String todaysDate = formatter.format(now);
switchStates.forEach((key, value) {
keys.add(key);
values.add(value);
});
for (var i = 0; i < keys.length; i++) {
resultMap["${keys[i]}"] = values[i];
}
FirebaseFirestore.instance
.collection("data")
.doc("attendance")
.collection("school")
.doc(todaysDate)
.set(resultMap);
}
and this is how I set th value but now I want to set grades in the same manner how can I manage to do that by replacing Switch() with TextFeild()
Try using TextField with onSubmitted or onChanged callbacks.
TextField(
controller: _controller,
onSubmitted: (text) {
sendMessage(text);
_controller.clear();
},
onChanged: () {},
textInputAction: TextInputAction.send,
)
If you have the user ID, you could try something like this
Firestore.instance
.collection("users")
.document(userId)
.get().then((value) => value.update({
field: value
})
I mean, get the collection and then update it individually

Returning the result of a Future in a synchronous call

I'm using the signature pad in the FlutterFormBuilder package to capture a signature (FlutterFormBuilderSignaturePad), upload it to firebase storage and then return the download url to the application for storage in a document in firestore.
The problem im facing is that the upload takes a couple of seconds to complete (possibly longer on poor connection). I'm trying to await the call so i can pass the download url to the database however its ignoring my attempts.
Ive tried :
Chaining my calls using the .then() and .whenComplete() but valueTransformer still returns a blank string.
added async to the "valueTransformer", "onSaved" and "onChange" methods and awaited the calls
moved the logic to save the signature between the three methods above in order to give the uimage time to upload
onChanges fires a lot so i introduced a _processing flag so it didnt save the image multiple times and cause database timeouts. onChange was returning a url given a few seconds however i couldn't guarantee the signature was complete.
So my widget looking like this:
final SignatureController _controller = SignatureController(
penStrokeWidth: 5,
penColor: Colors.red,
exportBackgroundColor: Colors.blue,
);
String _signature;
File _signatureFile;
bool _processing;
return FormBuilderSignaturePad(
name: 'signature',
controller: _controller,
decoration: InputDecoration(labelText: "signature"),
initialValue: _signatureFile?.readAsBytesSync(),
onSaved: (newValue) async {
//called on save just before valueTransformer
await processSignature(newValue, context);
},
valueTransformer: (value) {
//called when the form is saved
return _signature;
},
onChanged: (value) {
//called frequently as the signature changes
if (_controller.isNotEmpty) {
if (_controller.value.length > 19) {
if (!_processing) {
processSignature(value, context).then((value) {
setState(() {
_processing = false;
});
});
}
}
}
},
)
My future for processing the upload and setting the state
Future<void> processSignature(dynamic signature, BuildContext context) async {
setState(() {
_processing = true;
});
var bytes = await _controller.toPngBytes();
final documentDirectory = await getApplicationDocumentsDirectory();
final file =
File(join(documentDirectory.path, 'signature${database.uid}.png'));
file.writeAsBytesSync(bytes);
var url = await storage.uploadImage(
context: context,
imageToUpload: file,
title: "signature${database.uid}.png",
requestId: database.currentRequest.id);
setState(() {
_signature = url.imageUrl;
_signatureFile = file;
});
}
UPDATES AFTER CHANGES BELOW
Process Signature:
Future<String> processSignature(
dynamic signature, BuildContext context) async {
var bytes = await _controller.toPngBytes();
final documentDirectory = await getApplicationDocumentsDirectory();
final file =
File(join(documentDirectory.path, 'signature${database.uid}.png'));
file.writeAsBytesSync(bytes);
var url = await storage.uploadImage(
context: context,
imageToUpload: file,
title: "signature${database.uid}.png",
requestId: database.currentRequest.id);
return url.imageUrl;
}
Signature Pad Widget:
return FormBuilderSignaturePad(
name: 'signature',
controller: _controller,
decoration: InputDecoration(labelText: "signature"),
initialValue: _signatureFile?.readAsBytesSync(),
onSaved: (newValue) async {},
valueTransformer: (value) async {
final savedUrl = await processSignature(value, context);
return savedUrl;
},
onChanged: (value) {},
);
Method where im seeing the "Future"
_formKey[_currentStep].currentState.save();
if (_formKey[_currentStep].currentState.validate()) {
//request from the database
var request = firestoreDatabase.currentRequest;
//this should be the url however its returning as
//"Future<String>"
var value = _formKey[_currentStep].currentState.value;
request.questions[_currentStep].result =
jsonEncode(_formKey[_currentStep].currentState.value);
request.questions[_currentStep].completedOn =
Timestamp.fromDate(new DateTime.now());
firestoreDatabase.updateRequest(request).then((value) {
if (_currentStep == _totalSteps - 1) {
//pop the screen
Navigator.pop(context);
} else {
setState(() {
_currentStep++;
});
}
It impossible to return async result in sync call. Future means it completes somewhere in future.
Remove processSignature from onChanged (why send signature each time it modified?) and process it in onSaved. Then you can use async/await to send signature to server and wait for result url.
class _SomeWidgetState extends State<SomeWidget> {
/// Form key
final formKey = GlobalKey<FormState>();
/// Contains signature binary daya
Uint8List signatureValue;
#override
void build(...) {
return Column(
children: [
FormBuilderSignaturePad(
...
onSaved(Uint8List value) async {
signatureValue = value;
},
FlatButton(
child: Text('Submit'),
onPressed: () {
_submit();
}
),
],
);
}
/// Submits form
Future< void> _submit() async {
if (formKey.currentState.validate()) {
formKey.currentState.save(); // calls all `onSaved` for each form widgets
// So at this point you have initialized `signatureValue`
try {
final signatureUrl = await processSignature(signatureValue, context); // save into database
await doSomethingWithUrl(signatureUrl); // insert into document
} on SomeExceptionIfRequired catch (e) {
// Show error if occurred
ScaffoldMessenger.of(context).showSnackbar(...);
}
}
}
}

type 'Future<dynamic>' is not a subtype of type 'bool' shared_preferences

Trying to get this switch to still function as well as save and keep over app closure/reopen. Neither of these things is achieved and once the switch is activated, it will throw is an error when trying to change the switch back.
The following assertion was thrown while handling a gesture:
type 'Future<dynamic>' is not a subtype of type 'bool'
-
void onChange(bool value) {
setState(() {
brain.units = value;
});
}
//Shared Prefs saver
void initState() {
super.initState();
_loadValue();
}
_loadValue() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
units = (prefs.getBool('boolValue'));
});
}
_setValue() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
units = (prefs.getBool('boolValue'));
prefs.setBool('boolValue', units);
});
}
Widget build(BuildContext context) {
return Container(
child: Center(
child: SwitchListTile(
title: Text(this.unitName = brain.unitClass(),
value: brain.units,
secondary: Icon(FontAwesomeIcons.balanceScale, color: kIconColor),
onChanged: (
bool value,
) {
onChange(value && _setValue());
},
),
),
);
}
}
Your onChange is expecting a bool as an argument and you are passing in value && _setValue(). Are you sure that this evaluates to a bool?
It looks like the _setValue is returning a Future<dynamic> and this together with the && does not evaluate to a bool and is triggering the assertion error, I suspect.
You could move the _setValue call to your onChange method and then just call that with the new value or separate the call to onChange from the one to _setValue:
onChanged: (bool value,) {
_setValue();
onChange(value);
},
I think your problem ist that the _setValue() in
onChange(value && _setValue());
is not returning anything.
You should try to place this call on top of the onChange() or let it return true at the end.
_setValue() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
units = (prefs.getBool('boolValue'));
prefs.setBool('boolValue', units);
});
return true
}
or if it's not neccessary that _setValue() is done before the onChange(value) call
onChanged: (bool value) {
_setValue();
onChange(value);
},
Just change your code as following,
onChanged: (bool value,) asyc
{
var isValueSet=await _setValue();
onChange(value && isValueSet);
},
Here, _setValue() you are using for passing inside onChange() method has Future return type. which means you have to wait till your _setValue() is not been evaluated. So try assigning the future value as above.
This will solve your issue.