Search code examples
flutterreactive-programmingbloc

Flutter Bloc Todo example - Listen to a single todo from the repository


I am learning Flutter and the BLoC pattern from the Bloc library. I think I just about understand the Todo example, but I'm trying to build upon it to do something just a tad more complex, and I'm hitting a wall.

So, my issue is this. Let's consider the Todo example https://bloclibrary.dev/#/fluttertodostutorial

Contrary to what the (probably outdated) GIF animation on that page is representing, when you click on a Todo, you don't get to a "Todo details" page from which you can then go to an "Edit todo" page. You get directly to the edit page in a single step:

Todo list -> Edit todo

What I'm trying to do is to have the "two-step" flow that can be seen in the animation:

Todo list -> Todo details -> Edit todo

So, in the code, the actual "one-step" flow works like that: when you click on a todo, you are taken to the EditTodoPage, which has its own EditTodoBloc. This Bloc is initialized with the todo you clicked on, but in a "plain" way, just by passing the model of the todo (initialTodo).

  // in EditTodoPage
  static Route<void> route({Todo? initialTodo}) {
    return MaterialPageRoute(
      fullscreenDialog: true,
      builder: (context) => BlocProvider(
        create: (context) => EditTodoBloc(
          todosRepository: context.read<TodosRepository>(),
          initialTodo: initialTodo,
        ),
        child: const EditTodoPage(),
      ),
    );
  }

This means that EditTodoBloc is not "reactive" to changes to the todo. This works because the EditTodoPage is the only place where the todo gets updated, so it can just have its own state. Then when the todo is saved, it is saved in the repository, and you are taken back to the list, which is reactive via the repository and stream and everything.

But how would you adapt that to work with the "two-step" flow? The naive solution may be to use the same Bloc for both pages, but for the sake of argument (and because it seems to be a good practice to have one Bloc per page), let's say we want a TodoDetailsBloc for the details page and an EditTodoBloc for the edit page. The edits made on the edit page must now be reflected on the details page so we can't just pass the todo model to the blocs naively.

Should the two Blocs listen to a new stream from the repository? A stream that would only return that particular Todo? I'm not sure how and when that stream would get created and what its lifecycle would be; and I'm not sure it's the concern of the repository to do that. Or maybe should the two Blocs communicate to each other directly via events? But to me this seems to disrupt the nice top-down repository-bloc-widget data flow.

So, how would that look like in practice?

Or alternatively, if you have a good example of a (slightly) more complex app that I could study, I would gladly take that too.

Thanks :)


Solution

  • These kind of opinionated questions are part of design choices and are heavily dependent on what you are trying to achieve. The following is just my opinion:

    1. It seems like the Todo example bloated up the whole software architecture (for a very simple app) just to demonstrate how you can apply the bloc pattern everywhere in your code base.
    2. But how would you adapt that to work with the "two-step" flow? I would not even create multiple blocs for Todo. Just one TodoBloc with multiple TodoEvents (AddTodoEvent, EditTodoEvent, DeleteTodoEvent, ...) is sufficient. Having more blocs and events only leads to more boilerplate.
    3. (and because it seems to be a good practice to have one Bloc per page). Not really. A bloc should contain everything which is related to one business logic (hence the name BLoC = Business Logic Component). It's totally up to you which business logic you want to unite as one bloc and which you rather want to split up. But these kind of decisions should be dependent on the business logic itself, neither UI nor other layers of your application should influence them. For this example I would consider Todo as one bloc.
    4. The edits made on the edit page must now be reflected on the details page so we can't just pass the todo model to the blocs naively. There are several ways to solve this problem. The TodoDetailsPage could always ask for the newest version of the Todo. Another way would be to use TodoDetailsBloc along with EditTodoBloc inside the edit page. Having separate blocs does not mean you are not allowed to talk to multiple blocs on one page.
    5. Should the two Blocs listen to a new stream from the repository? A stream that would only return that particular Todo? Not really as it should not be the job of the repository to return the newly edited Todo. The repository is only there to save, update and delete transactions.
    6. Or maybe should the two Blocs communicate to each other directly via events? Not at all. Bloc's should always work independently. If you really think that two or more should communicate to each other, then you should consider merging them into one bloc.

    I hope I could help you with that. Please keep in mind that these answers are more based on opinions. There are no right and wrong answers.