In my flutter app, when a checkbox is clicked to indicate a completed task, ideally, i need that to reflect in the firestore database. There is a collection called 'todo' with documents each having the fields: task_id, user_id, done, task, where task is the actual thing to be done (String) and done is a boolean field. So when the checkbox is clicked, my code calls the function invertDone with parameter task name. What this does is basically uses task name to query the collection to find the correct document and get the docId. Then a snapshot is gotten and the 'done' field of that document is inverted.
Future<void> invertDone(String task) async {
//get docid of task with task name..invert its bool field
todo.where('task', isEqualTo: task).get().then(
(QuerySnapshot snapshot) => {
snapshot.docs.forEach((f) {
todoid = f.reference.id;
print('todoid found is $todoid');
//new
FirebaseFirestore.instance
.runTransaction((transaction) async {
// Get the document
DocumentSnapshot snapshot = await transaction.get(todoid);
if (!snapshot.exists) {
throw Exception("User does not exist!");
}
temp = snapshot.data()['done'];
print('it was $temp');
temp = !temp;
print('now its $temp');
transaction.update(todoid, {'done': temp});
})
.then((value) => print("data updated"))
.catchError((error) => print("Failed to update : $error"));
//new done
}),
},
);
}
This code to update is directly from the documentation and the error being shown is that type 'String' is not a subtype of type 'DocumentReference'.
Also the two print statements are not working. i'm not sure why.
This isn't needed probably but here is the code for the entire screen just for reference
bool temp;
var todoid;
int number = 0;
final auth = FirebaseAuth.instance;
Stream collectionStream =
FirebaseFirestore.instance.collection('todo').snapshots();
CollectionReference main = FirebaseFirestore.instance.collection('maindb');
CollectionReference todo = FirebaseFirestore.instance.collection('todo');
final myController = TextEditingController();
class Todo extends StatefulWidget {
final String docid;
final bool isCaretaker;
Todo({this.docid, this.isCaretaker});
@override
_TodoState createState() => _TodoState();
static const String id = 'todoscreen';
}
Future<void> addTask(String task) async {
Map<String, dynamic> data = {
'task_id': number,
'user_id': docid,
'task': task,
'done': false
};
await todo.add(data);
number++;
}
Future<void> invertDone(String task) async {
//get docid of task with task name..invert its bool field
todo.where('task', isEqualTo: task).get().then(
(QuerySnapshot snapshot) => {
snapshot.docs.forEach((f) {
todoid = f.reference.id;
print('todoid found is $todoid');
//new
FirebaseFirestore.instance
.runTransaction((transaction) async {
// Get the document
DocumentSnapshot snapshot = await transaction.get(todoid);
if (!snapshot.exists) {
throw Exception("User does not exist!");
}
temp = snapshot.data()['done'];
print('it was $temp');
temp = !temp;
print('now its $temp');
transaction.update(todoid, {'done': temp});
// Return the new count
//return temp;
})
.then((value) => print("data updated"))
.catchError((error) => print("Failed to update : $error"));
//new done
}),
},
);
}
_openPopup(context) {
Alert(
context: context,
title: "Add Todo",
style: AlertStyle(titleStyle: Theme.of(context).textTheme.headline1),
content: Column(children: <Widget>[
TextField(
controller: myController,
decoration: InputDecoration(
icon: Icon(Icons.check),
labelText: 'Task',
labelStyle: Theme.of(context).textTheme.headline1,
),
),
]),
buttons: [
DialogButton(
color: Colors.transparent,
onPressed: () {
addTask(myController.text);
Navigator.pop(context);
myController.clear();
//print(myController.text);
},
child: Text(
"ADD TASK",
style: Theme.of(context).textTheme.headline1,
),
)
]).show();
}
class _TodoState extends State<Todo> {
@override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Color(0xFF602247),
toolbarHeight: 50.0,
centerTitle: true,
title: Text(
'VITALITY',
style: Theme.of(context).textTheme.headline3,
),
automaticallyImplyLeading: false,
actions: [
Visibility(
visible: isCaretaker,
child: IconButton(
icon: Icon(Icons.add, color: Colors.white, size: 30.0),
onPressed: () {
_openPopup(context);
})),
],
),
body: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('todo')
.where('user_id', isEqualTo: docid)
.snapshots(),
builder: (context, snapshot) {
return Center(
child: ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
DocumentSnapshot task = snapshot.data.docs[index];
bool _checked = task['done'];
return StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return CheckboxListTile(
title: Text(task['task']),
value: _checked,
onChanged: isCaretaker
? (bool value) {
print('do u want to delete ');
}
: (bool value) {
print('initially checked is $_checked');
setState(() {
_checked = value;
print(_checked.toString());
});
invertDone(task['task']);
},
secondary: const Icon(Icons.hourglass_empty),
);
});
// return ListTile(title: Text(course['task']));
}),
);
},
)
// Center(
// child: Column(
// children: <Widget>[
// Text(widget.docid),
// Text(widget.isCaretaker.toString()),
// ],
// ),
// ),
,
bottomNavigationBar: bottomAppBar(id: widget.docid));
}
}
It seems you are assigning the document id (which is a String
) to todoid
here
todoid = f.reference.id;
when instead you actually need to assign a DocumentReference
to it. So you need to change to this.
todoid = f.reference;
and also ideally change the name of the variable accordingly.