Firebase, Streambuilder and animated list in flutter

43 Views Asked by At

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();
            }
          });
    });
  }
}
0

There are 0 best solutions below