I'm building a school service tracking app using Flutter. Since I'm a newbie to mobile app development I'm facing lots of errors. One of them is,
After an user logged in to the app, in the dashboard it will show their children profiles in a card widget. Each profile as a toggle switch button to update their attendance.
Whenever they tap on the switch button the data is updating in database. But don't know how to change the tapped toggle switch button's state.
I'm using Google Firebase Firestore database.
This is my database structure.
Profiles(Children) table structure.
Following is the Attendance table structure. Each document will create daily under the name of particular date. Inside each document profile ids stores as sub collections.
Inside each profile sub collection a document will have attendance details under the profile name.
This is how I'm retrieving data from database.
CustomScrollView(
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate([
whiteLogo(), // this is a logo widget
]),
),
StreamBuilder(
stream: Firestore.instance.collection('profiles').snapshots(),
builder: (context, snapshot) => SliverList(
delegate: SliverChildBuilderDelegate((context, index) =>
Container(
child: userCard(context, snapshot.data.documents[index]), // profile card
),
childCount: snapshot.hasData ? snapshot.data.documents.length : 0,
),
)
)
],
)
Following is userCard widget
userCard(BuildContext context, DocumentSnapshot document) {
var attendanceCollection = Firestore.instance.collection('attendance').document('10-01-2019').collection(document.documentID);
var documentId = document["name"].toString().toLowerCase();
var attendanceReference = attendanceCollection.document(documentId);
bool status = true;
return Card(
margin: EdgeInsets.fromLTRB(20.0, 20.0, 20.0, 20.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
elevation: 0.3,
color: Colors.white,
child: Padding(
padding: EdgeInsets.fromLTRB(5.0, 15.0, 5.0, 10.0),
child: Column(
children: <Widget>[
ListTile(
leading: (
Container(
width: 50.0,
height: 50.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
'${document['avatar']}'
)
),
),
)
),
title: Text(document['name']),
trailing: Column(
children: <Widget>[
Text(
'Absent/Present',
style: TextStyle(
color: Color(0xff868686),
fontSize: 12.0
),
),
Switch(value: status, onChanged: (value) {
setState(() {
status = value;
});
//onChange the "attendance_status" value is changing
Firestore.instance.runTransaction((transaction) async {
DocumentSnapshot freshSnap = await transaction.get(attendanceReference);
await transaction.update(freshSnap.reference, {
"attendance_status": value
});
});
},)
],
),
),
horizontalLine(),
textTile('School', 'Class', 13.0, Color(0xff827f7f)),
Container(margin: EdgeInsets.only(bottom: 10.0)),
textTile(document['destination'], document['class'], 16.0, Color(0xff424242)),
horizontalLine(),
buttonWrap(context, document)
],
),
),
);
}
This is the preview of the dashboard.
When I tap on the toggle switch button it update the value in the database. But not the state of the button. Button state is not changing.
I tested by placing the status
variable inside the initState
, but then the state is affecting all buttons.
I want to change the state of specific toggle switch button when user taps on that. Any help would be much appreciated.
initState() {
status = true;
super.initState();
}
In your current implementation the switch state is not reflecting the value of the database as there is no subscription for data that is updating its value. You can make the UserCard
widget stateless and just read the value of the Switch
from attendance collection with a StreamBuilder
instead.
Something like this should do the trick and your attendance switch will set its state depending on the attendance document (and it should be set to false
if the document is not yet created)
Column(
children: <Widget>[
Text(
'Absent/Present',
style: TextStyle(
color: Color(0xff868686),
fontSize: 12.0
),
),
StreamBuilder(
stream: attendanceReference.snapshots(),
initialData: null,
builder: (ctx, snap) {
return Switch(
value: snap.data == null ? false : snap.data["attendance_status"],
onChanged: (value) {
Firestore.instance.runTransaction((transaction) async {
DocumentSnapshot freshSnap = await transaction.get(attendanceReference);
await transaction.update(freshSnap.reference, {
"attendance_status": value
});
});
},
);
},
)