Reputation: 292
I am trying to update an array in flutter. I found out that it is not possible directly.
I have understood that I must first create a list[]
, transfer my document fields value into the created list, then I must update the information in my list and only then can I can update my Firebase Firestore array with my updated list.
My code is below. Problem is, when I use my function the simulator crashes and I am getting the error lost connection.
I am looking for any advice. Many thanks.
List listTest =[];
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
class DetailScreen_CheckList_V3 extends StatefulWidget {
//final Map listName;
final docID;
const DetailScreen_CheckList_V3( this.docID,{
Key key}) : super(key: key);
@override
_DetailScreen_CheckList_V3State createState() => _DetailScreen_CheckList_V3State(docID);
}
class _DetailScreen_CheckList_V3State extends State<DetailScreen_CheckList_V3> {
// Map listName;
var docID;
_DetailScreen_CheckList_V3State( //this.listName,
this.docID);
@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text('Your list items'),
leading:
InkWell(
child:
Icon(Icons.fast_rewind_outlined),
onTap: () {
Navigator.pop(context);
},),
),
body: MyBody(context, docID),
floatingActionButton: FloatingActionButton(
onPressed: () {
showAddNewItemToAList();
setState(() {});
},
child: const Icon(Icons.add),
backgroundColor: Colors.blue,
),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
);
}
Widget MyBody(BuildContext context, var docID) {
return SingleChildScrollView(
child: Column(
children: [
Container(
height: MediaQuery
.of(context)
.size
.height / 1.4,
width: MediaQuery
.of(context)
.size
.width,
child: StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('lists')
.doc(docID)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else {
DocumentSnapshot data = snapshot.requireData;
return ListView.builder(
itemCount: data['allItems'].length,
itemBuilder: (context, index) {
return Card(
child:
InkWell(
child: ListTile(
leading: data['allItems'][index]['itemChecked'] ==
'Yes' ? Icon(
Icons.check_box,
color: Colors.blue,) : Icon(
Icons.check_box_outline_blank),
title:
Text(
(data['allItems'][index]['itemName'])),
onTap: () {
String checked = data['allItems'][index]['itemChecked'];
String myItemName = data['allItems'][index]['itemName'];
String myListName = data['listName'];
listTest.addAll(data['allItems']);
print('before');
print (listTest);
setState(() {
if (checked == 'Yes') {
checked = 'No';
listTest[index]['itemChecked'] = checked;
print('after');
print(listTest);
myTest(myListName,index);
}
else {
checked = 'Yes';
listTest[index]['itemChecked'] = checked;
print('after');
print(listTest);
myTest(myListName,index);
}
});
}
),
onTap: () {
},)
);
});
}
}))
]),
);
}
void showAddNewItemToAList() {
TextEditingController _noteField = new TextEditingController();
showDialog(
context: context,
builder: (BuildContext context) {
return CustomAlertDialog(
content: Container(
width: MediaQuery
.of(context)
.size
.width / 1.3,
height: MediaQuery
.of(context)
.size
.height / 4,
child: Column(
children: [
TextField(
controller: _noteField,
maxLines: 4,
decoration: InputDecoration(
border: const OutlineInputBorder(
borderSide:
const BorderSide(color: Colors.black, width: 1.0),
),
),
),
SizedBox(height: 10),
Material(
elevation: 5.0,
borderRadius: BorderRadius.circular(25.0),
color: Colors.white,
child: MaterialButton(
minWidth: MediaQuery
.of(context)
.size
.width / 1.5,
onPressed: () {
if (_noteField.text != '') {
setState(() {
AddObjectItemToArray(_noteField.text);
});
Navigator.of(context).pop();
}
else {
return;
}
},
padding: EdgeInsets.fromLTRB(10.0, 15.0, 10.0, 15.0),
child: Text(
'Add Item',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
)
],
),
),
);
});
}
Future AddObjectItemToArray(newItemName,) async {
AllItems _allItems = AllItems(newItemName, 'No');
FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('lists')
.doc(docID).update({
"allItems": FieldValue.arrayUnion([_allItems.toMap()])
},);
}
Future ModifyCheckedStatus(newItemName, newCheckedStatus,
currentListName) async {
AllItems _allItems = AllItems(newItemName, newCheckedStatus);
FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('lists')
.doc(docID).update(
{'listName': currentListName,
"allItems": ([_allItems.toMap()]),
}, //SetOptions(merge: true),
);
}
Future myTest(currentListName,index) async {
FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('lists')
.doc(docID).set(
{'listName': currentListName,
"allItems": [listTest],
},//SetOptions(merge: true)
);
}
}
Upvotes: 1
Views: 125
Reputation: 1633
So I guess your AddObjectToArray method is working well, you are correctly using arrayUnion. But then the ModifyCheckStatus is not working as expected because you are still using arrayUnion.
ArrayUnion adds an object to the array. What you have to do in ModifyCheckStatus is to extract all items, toggle the checked status of a particular item, then update the entire items list in Firebase (instead of using arrayUnion). This should only be in ModifyCheckStatus.
Something like the following
Future ModifyCheckedStatus(newItemName, newCheckedStatus,
currentListName) async {
// TODO: obtain all items
// changed the checked status of a particular item
allItems.where((i) => i.name == item.name).forEach((i) {
i.checked = !i.checked;
});
// update as you did
FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('lists')
.doc(docID)
.update({"allItems": allItems});
}
Upvotes: 1