I am building a todo app, in which I use firebase to store my tasks as documents that I retrieve later using stream builder and store them in a list, I am also using an animated list to load the data in my widget, so I need to animate the list item whenever a new task is added to Firebase or animate whenever a task has been deleted.
@override
void initState() {
super.initState();
items = [];
stream.listen((newSnapshots) {
if (mounted) {
setState(() {
final newItems = newSnapshots.docs;
final newIndices = newItems.map((newItem) {
// Find the index of the new item in the existing list:
int index = items.indexWhere((item) => item.id == newItem.id);
if (index == -1) {
// New item, append at the end
index = items.length;
}
return index;
}).toList();
// Update the items list and insert them in the AnimatedList:
items = newItems; // Update the list with new items
for (final index in newIndices) {
_crud.listKey.currentState?.insertItem(index);
}
});
}
});
}
I tried this method which I got from Google Bard AI, but it's not working and it's giving a range error when I try to add new items.
@override
void initState() {
super.initState();
items = [];
stream.listen((newSnapshots) {
if (mounted) {
setState(() {
items = newSnapshots.docs;
_crud.listKey.currentState?.insertItem(0);
});
}
});
}
This method works, but it animates only the first item of the list, even when the item is not inserted to the first index. Full code is provided below:
class EveryDayList extends StatefulWidget {
const EveryDayList(
{super.key, required this.today, required this.scrollController});
final DateTime today;
final ScrollController scrollController;
@override
State<EveryDayList> createState() => _EveryDayListState();
}
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final _user = FirebaseAuth.instance.currentUser!.email.toString();
class _EveryDayListState extends State<EveryDayList> {
final _crud = CRUD();
final _dL = DatesLogic();
final stream = _firestore
.collection(kUsers)
.doc(_user)
.collection(kTodoList)
.where(kUnder, isEqualTo: false)
.where('repeatDaily', isEqualTo: true)
.orderBy(kPriority, descending: false)
.orderBy(kCreatedAt, descending: true)
.snapshots();
List<DocumentSnapshot> items = [];
// @override
// void initState() {
// super.initState();
// items = [];
// stream.listen((newSnapshots) {
// if (mounted) {
// setState(() {
// items = newSnapshots.docs;
// _crud.listKey.currentState?.insertItem(0);
// });
// }
// });
// }
@override
void initState() {
super.initState();
items = [];
stream.listen((newSnapshots) {
if (mounted) {
setState(() {
final newItems = newSnapshots.docs;
final newIndices = newItems.map((newItem) {
// Find the index of the new item in the existing list:
int index = items.indexWhere((item) => item.id == newItem.id);
if (index == -1) {
// New item, append at the end
index = items.length;
}
return index;
}).toList();
// Update the items list and insert them in the AnimatedList:
items = newItems; // Update the list with new items
for (final index in newIndices) {
_crud.listKey.currentState?.insertItem(index);
}
});
}
});
}
@override
Widget build(BuildContext context) {
return Consumer<DataBase>(builder: (context, dataBase, child) {
return StreamBuilder(
stream: stream,
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data!.docs.isNotEmpty) {
items = snapshot.data!.docs;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: Dropdown(
isEveryDay: true,
remainingTasks: dataBase.isEverydayVisibility
? ''
: snapshot.data!.docs.length.toString(),
name: 'Everyday',
isVisible: dataBase.isEverydayVisibility,
),
),
const SizedBox(
height: 5,
),
Visibility(
visible: dataBase.isEverydayVisibility,
child: AnimatedList(
key: _crud.listKey,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
// itemCount: snapshot.data?.docs.length ?? 0,
initialItemCount: items.length,
itemBuilder: (context, index, animation) {
// DocumentSnapshot todoList =
// snapshot.data!.docs[index];
DocumentSnapshot todoList = items[index];
bool value =
snapshot.data!.docs[index].get(kIsCompleted);
String docId = snapshot.data!.docs[index].reference.id
.toString();
var timestamp =
snapshot.data!.docs[index].get(kCreatedAt);
int priorityNumber = todoList.get(kPriority);
var streak = todoList.get(kStreak);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: FadeTransition(
opacity: animation,
child: TodoTile(
widget: StreakWidget(streak: streak),
borderColour: kColorMapping[priorityNumber] ??
kNoPriorityColour,
appBarTitle:
_dL.getAddedTimeTextEveryDay(timestamp),
description: todoList.get(kDescription),
docId: docId,
onChecked: (context) {
_crud.onCheckboxchecked(
docId, value, false, index);
},
taskName: todoList.get(kTaskName),
onChanged: () {
_crud.onCheckboxchecked(
docId, value, false, index);
},
onRemove: (context) {
_crud.onRemove(docId);
},
onDismissed: () {
_crud.onRemove(docId);
},
onCheck: () {
_crud.onSlidablechecked(docId, value);
},
),
),
);
}),
),
],
);
} else {
return const SizedBox();
}
});
});
}
}