0

I have built a todo app with flutter, getx and firebase. I have a login page, signup page and the todo page where I show a list of todos. I am using firebase_auth package for authentication and cloud_firestore package for saving and retrieving data from firestore. I have 2 models - one for user and other for todo.

For saving the todo in firestore I have the below method -

Future<void> addTodo(String content, String uid) async {
    try {
      await firebaseFirestore
          .collection("users")
          .doc(uid)
          .collection("todos")
          .add({
        "content": content,
        "dateCreated": Timestamp.now(),
        "done": false,
      });
    } catch (e) {
      print(e.toString());
      rethrow;
    }
  }

To retrieve the list of todos from firestore I am using the below method which is returning a stream of todos

Stream<List<TodoModel>> todoStream(String uid) {
    return firebaseFirestore
        .collection("users")
        .doc(uid)
        .collection("todos")
        .orderBy("dateCreated", descending: true)
        .snapshots()
        .map((event) {
      List<TodoModel> retVal = List.empty(growable: true);
      event.docs.forEach((element) {
        TodoModel todo = TodoModel();
        todo.todoId = element.id;
        todo.content = element['content'];
        todo.dateCreated = element['dateCreated'];
        todo.done = element['done'];
        print(todo.toString());
        retVal.add(todo);
      });
      return retVal;
    });
  }

As you can see todo for a particular user is tied to their id.

Now in my todo controller's onInit method I am calling the above function to get the stream of todos

@override
  void onInit() {
    String uid = Get.find<AuthController>().user;
    todoList.bindStream(DatabaseService().todoStream(uid));
    super.onInit();
  }

Auth controller returns the user's id which is then fed to the todoStream function to get the todos

In the main page of the app inside the build method I am injecting the Todo controller like so

Get.put(TodoController());

In the body of the Scaffold widget I am displaying the todos using Obx like so

Obx(
            () {
              if (Get.find<TodoController>().todos.length > 0) {
                return Expanded(
                  child: ListView.builder(
                    itemCount: Get.find<TodoController>().todos.length,
                    itemBuilder: (_, index) {
                      TodoModel todo = Get.find<TodoController>().todos[index];
                      return Card(
                        margin:
                            EdgeInsets.symmetric(horizontal: 20, vertical: 5),
                        child: Padding(
                          padding: EdgeInsets.all(10.0),
                          child: Row(
                            children: [
                              Expanded(
                                child: Text(
                                  todo.content,
                                  style: TextStyle(
                                    fontSize: 15,
                                    fontWeight: FontWeight.bold,
                                  ),
                                ),
                              ),
                              Checkbox(
                                  value: todo.done,
                                  onChanged: (newValue) {
                                    print(newValue);
                                  }),
                            ],
                          ),
                        ),
                      );
                    },
                  ),
                );
              } else {
                return Text(
                  "Loading....",
                  style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),
                );
              }
            },
          )

The issue I am facing is when the app starts and the first user logs in, I can see that the todo controller is created and initialized and the list of his todos is displayed on the page. However when he logs out and another user logs in the page is able to display his name correctly which means the user is identified correctly, but still the list of todos of the first user are showing.

This tells me that when the second user logs in the onInit method of the todo controller is not called.

Anyone know how to fix this?

Below is my TodoController -

class TodoController extends GetxController {
  Rxn<List<TodoModel>> todoList = Rxn<List<TodoModel>>();

  List<TodoModel> get todos => todoList.value ?? [];

  void clear() => todoList = Rxn<List<TodoModel>>();

  @override
  void onInit() {
    String uid = Get.find<AuthController>().user;
    print("onInit called=====================" + uid);
    todoList.bindStream(DatabaseService().todoStream(uid));
    print("todo bind stream done=========================");
    super.onInit();
  }
}

1 Answers1

1

oninit function is calling actually issue was with bindstream todoList.bindStream(DatabaseService().todoStream(uid)); After calling this bindstream it take one second to get data from firebase after that this gives to model so you need to add loader on ui page once data is loaded todoList!=null then you need to False loader and show data

  • if you see the Obx I can actually check if the length of todos list is greater than 0, only then I am displaying the list else I am displaying 'Loading....' text – Pushkar Kastoori Jul 29 '21 at 05:02
  • If you check array.length it was giving error at length so you need to check like this list==null after that list is not null then you need to check greater than 0 or else you can do you need make list like List = []; for not give null error – yogesh bhanushali Jul 29 '21 at 05:22
  • I had updated the post and added the TodoController class, where I am having this - List get todos => todoList.value ?? []; – Pushkar Kastoori Jul 29 '21 at 06:38
  • if(controller. todos == null) { return Center(child: CircularProgressIndicator(),); }else{ if(controller. todos!=null){ You can show ui herer } } – yogesh bhanushali Jul 29 '21 at 07:23
  • I tried the above and the issue remains. I don't see the circular progress indicator, which could be because the data is loading quickly. Another interesting observation is that if I add todos for the second user I don't see the todoList getting updated in the UI, I can still the first users todos. If I restart the app and login as the second user, I am able to see the todo I added previously. This tells me the todoList stream is not getting updated after I login as second user. Can send the github link to my code if that helps, thanks for all your suggestions so far. – Pushkar Kastoori Jul 30 '21 at 08:22
  • I have also Faced this issue but when changed from ever to everAll function my issue fixed please check this – yogesh bhanushali Aug 17 '21 at 06:41