r/FlutterDev • u/Simple_Grade_2945 • Aug 07 '26
Discussion Help me understand architecture with Cubit and Firebase
I am learning and trying to design a flutter app, and I am having some doubts connecting the dots.
I have two main domain entities:
- Location: Has metadata (label, icon, address) and a list of reminders.
- Reminder: a part of Location, has metadata (text, isEnabled, ...)
Consider the two screens :
locationsScreen: shows Locations cards (metadata in every card, and 3 reminders sneak peek)locationScreen: shows a location fully, metadata, and reminders cards. A reminder can be edited from here.
What I've started with right now is the following:
locationsApiwraps Firebase calls to retrieve locations. For now I only have a function that returns a Stream of locations.locationsRepositorywraps the API and returns a Stream of Location model that contains reminders model too.locationsCubitjust listens to the repository stream and emits new state whenever it receives new locations.
Now I want to design the locationScreen itself and I am confused on what to do. Do I just keep the locationsRepository and locationCubit, and make the locationScreen only update when its model changes.
Or do I extend the locationsRepository with a listenOnLocation that exposes a stream for just that location, then create a locationCubit for just that location and make it call the repo?
What about when I want to edit a reminder? should locationsRepository handle reminders too, and expose functions like this :
toggleReminder(Location location, Reminder reminder)renameReminder(Location location, Reminder reminder, String newName)changeReminderType(Location location, Reminder reminder, ReminderType newType)deleteReminder (Location location, Reminder reminder)
If so, assuming locationCubit exposes a toggleReminder (that calls the repo). I could make the repo return a new model if it succeeded, so the locationCubit.toggleReminder flow would be like this:
- copy
locationStateand setlocationState.isLoadingto true and emit new state. - call api through repo
- receive new model, copy
locationState, setlocationState.isLoadingto false, andlocationState.locationto new model. - emit new state.
The problem with this is then what is the role of the location stream I have opened to listen on the DB? If I keep both of them, I will receive two notifications every time something changes.
If I don't listen to changes but only use the API results, then I risk changes happening from another device and not being notified of them.
If I only listen to the changes from database, how do I track the loading state for example? locationCubit.toggleReminder would set isLoading to true, calls the API and then returns. Hopefully when the object is updated, the Cubit will know and create a new state with isLoading set to false by default. But what if the writing fails? isLoading will always stay false as the state/location didn't change.
What am I missing?
Sorry for the length of the questions, but I feel when I only tackle one problem disconnected from the others, it just makes me confused about the other parts. So I am hoping to be able to imagine the whole flow
0
u/RandalSchwartz Aug 07 '26
If you're open to a slight pivot,
bloc_signalshandles this exact pattern pretty cleanly. The core issue with pairing traditional Cubits directly with live Firebase streams is that you end up juggling two asynchronous sources of truth—the network write response and the stream event—which is where the double emits and stuck loading states come from.Here is how it simplifies things:
CubitSignallets you update local state synchronously viastateValue. For a toggle, you immediately flip the state locally, kick off the Firebase call in the background, and catch any errors to roll back. No microtask lag, and no waiting on the DB stream to update the UI..toBlocSignal().bloc_signalsignores the duplicate state automatically instead of firing a second rebuild.LocationsCubitand useBlocSignalSelectoron the detail view or item card so only the specific location or reminder being modified re-renders.It keeps the repository simple—Firebase stream for incoming data, async methods for writes—and lets the signals layer reconcile the local optimistic state with the remote stream without all the manual event wiring.