I have an application where I display a list of cards that represent a model (Let's say a Person). These are stateless widgets as they cannot be changed directly. These instances are obtained from an API and, since the amount of items is large, I am using AutomaticKeepAliveClientMixin to keep them from reloading and triggering more API calls.
When clicked, these cards lead to the Person's page where there we can edit some information on them. Using a StatefullWidget here with setState takes care of all issues related to the page. However, when I pop the individual page the card that was clicked does not reflect the changes made.
I was thinking of writing a ChangeNotifier wrapper for each Person but from what I've read this is a bad idea. Using more powerful state management tools like BLoC or Redux seems like overkill too. How can I implement this properly?
I think you should try:
ontap: () async {
await Navigator.push ....);
ref
}
Related
Im building an app with a backend on firestore.
Architecture:
This is the relevant route of screens the user can take in my app:
Home-> myOrdersList(FutureBuilder)-> OrderInfo(FutureBuilder)-> HandleOrder.
to get the futures and handle my order I have this class:
class FirestoreService
functions:
Future List GetOrders;
Future Order getOrder;
Future void changeOrderInfo;
Order class:
class Order
parameters:
String info;
This is the problem:
When the calls FirestoreService().changeOrderInfo on HandleOrder-screen the screen pops to OrderInfo-screen. The SpecificOrder-screen automaticly rebuilds with setState so thats works fine and the changed info is up to date here. But when the user navigates one screen backwards in the stack to myOrdersList (where all current orders are listed) that screen still holds the old data that was fetched before the order was handled. So not before the user manually updates the screen you can se the current state and the changed info on that screen.
My question:
So I wonder if there is a simple stateManagement technique to solve this problem in a simple way. One way I have thought of is to use changenotifier in FirestoreService, and notifyallisteners when the function changeOrderInfo is called to then notify all relevant screens and get them up to date but I dont really know if thats correct. Since there are many orders and new ones are continuously created I cant simply listen on one document and provide those changes.
One solution could also be to use a streambuilder in myOrdersList but i dont like that solotion since it would mean that data would be fetched everytime any order was changed
if you are after real-time updates stream builder is the only way to go. unless you want to refresh your future builder again and again every time there is an update which will kill your wallet because that means you are refetching all the documents again and again. Unlike stream builder, you only fetch the collection once and if there is an update, it will only refetch the document changed/updated not the whole collection. check this out for more explanation https://firebase.google.com/docs/firestore/query-data/listen
My app requires to consume different attributes of a User object in different sections of the app i.e. different screens and dialogs.
Example of User attributes would be a list of followers and following.
I need this User object to be reactive, i.e. should append a new user (response from api call) to it's following list when the subsequent action is performed.
Currently I plan to use certain BLoC's as Reactive View Models (from MVVM) for each screen (so that the business logic is separated from the presentation layer) and have a global User BLoC listening to these View Models.
For example :- User BLoC can listen to local View Model BLoC for follow user page and when the view model emits a successful state the response can be added to the User BLoC's User state's following list.
How feasible and performant would an implementation like this be? Is this the standard way of doing things in BLoC. I looked around for examples like these but could only find simple implementations of BLoC architecture. Would this be too over kill for a mid sized app?
Much appreciated!
What's the best approach to make something like parent-children bloc architecture? Very often I face a problem when there is a need to establish communication between cubits or blocs.
Let's image a form where we would like to create a new user:
User name
User role (separate cubit, fetch all possible option from API)
Calendar (separate cubit, a lot of calculations made)
Apply button (main BLoC which is responsible to save new user). First
approach:
We need to get each cubit's state and emit new state when clicking on Apply button.
First approach:
var role = context.read<RolesCubit>().state as RoleSelected;
var date = context.read<CalendarCubit>().state as DateSelected;
context.read<CreateUserBloc>.add(CreateNewUserEvent((role: role, date: date));
Pros:
good granulation,
each small cubit is responsible for one thing eg getting a list of available roles and choose one
easier to maintain tests,
cubits can be reused in many places
Cons:
a need to remember to update in each place where main CreateNewUserEvent is added (imagine we would like to add 3rd sub cubit responsible for Access or Salary).
need to know each sub-cubit's state before we add "main" event. I should copy this logic to other places where this event is added. Maybe another example: I wanted to follow this approach with paginated list, on scroll refresh indicator action and one of sub page, therefore I had to check each sub-cubit state and after that I could add main event CreateNewUserEvent;
Second approach:
context.read<CreateUserBloc>.add(DateChanged((newDate));
context.read<CreateUserBloc>.add(RoleChanged((newRole));
context.read<CreateUserBloc>.add(CreateNewUserEvent(());
Create one big BLoC which can do all responsibilities.
Pros:
everything is kept in one place. When we select new role or new date,
"master" BLoC is immediately informed about change and decides what
to do next.
Cons:
big piece of code which is hard do maintain and test smaller parts
can not be reused in other places because it's monolith
Third approach
Stream subscription and make "master" bloc strictly dependent of sub-cubits. Master's cubit constructor requires a stream of sub-cubits. Explained here:
I hope I explained a problem in a simple way. Whats the best approach to maintain complex architectural design in flutter?
I was wondering , since I started using RiverPod , if I should not use setState at all and have almost everything in StateNotifierProvider
Yes you can. You need to understand there are 2 types of state:
Ephemeral(Local): This is contained to only a single widget and not used for passing information between different components. You should use setState and internal state variables for cases like these. Like #Ruchit said in his comment above, a good example is a checkbox, switch, dropdown. Or if you want to hide/show something based on some data.
Global: This is for any information that is passed between layers, components or widgets and should be accessible and synced across different screens. For these cases you should use state management solutions like Provider, Riverpod etc. Some examples are:
Adding items to a cart.
Updating a favourites list.
Sending data to network layer to make http calls etc.
I'm working with Flutter quite a long time and have a bunch of released products. I never really liked BLoC and preferred to use Provider or later Riverpod.
I just don't understand that event concept. Why do we still need it? And i'm confused because of it's actual popularity... BLoC has Cubit subclass that seems to be more simple to use, but everyone just keep telling: "Cubit is simpler, but not so functional". But what are limitations?
I even think that Cubits are MORE USEFUL and MORE SIMPLE at the same time:
With Cubit you just call it's method with params. You still can listen its state AND get the method return value if needed too.
You don't need extra coding implementing these Event Types.
You don't need extra extra coding implementing how bloc will handle every single event type. METHODS DO THAT JUST FINE.
example:
User taps some product's "Add to cart" button.
Cubit:
cartCubit.addProduct(productId);
BLoC:
cartBloc.addEvent(UserAddsProductEvent(productId));
inside them:
Cubit:
void addProduct(String productId) async {
//some validation...
if(...){...}
final result = await cartRepo.addProduct(id);
if(result == ...) {
state = someState;
//....
}
BloC:
void addEvent(CartEvent event) {
if (event is UserAddsProductEvent) {
_addProduct(event.productId)
} else if (event is....) {
//.....
}
}
void _addProduct(String productId) async {
//some validation...
if(...){...}
final result = await cartRepo.addProduct(id);
if(result == ...) {
state = someState;
//....
}
What is the point?
There's a good overview of Cubit vs Bloc in the official documentation.
In short, Cubit's advantage is simplicity, while Bloc provides better traceability and advanced ReactiveX operations.
In our projects, we use both Cubit for simpler cases, and Bloc if the logic is more complicated and some "limitations" actually become useful:
You can emit a new state only as a reaction to an event, so the implementation is more straightforward (but more verbose as well).
All the events are processed one by one. Again, it makes implementation more reliable and easier to maintain.
Also, that can be a matter of personal preference, but I like Bloc for its close mapping to the FSM pattern. In most cases, the application state can be nicely represented as a state machine. It's easier to discuss the implementation even with a whiteboard, as you can just show the scheme with a number of states and events changing that state.
If you're happy with Cubit, then you probably don't need Bloc. After all, the main goal is to make the architecture easy to understand and to maintain.
BLoC
The advantage of having events instead of direct method calls is that you can debounce/throttle, buffer the stream before doing your logic.
In other words, you can use special methods applicable for events logic.
Cubit
If you start with Cubit for a new project, the idea why it exists is that later you will have the ability to migrate from Cubit to BLoC.
This means that if at the beginning of a project you think that BLoC is too much overhead and you need simpler state management (without events, boilerplate, etc.) you can choose cubit and migrate to BLoC with fewer efforts, than if you selected a different state management solution like MobX or Riverpod.
So with Cubit, you first implement the state and functions. Later if you decide to switch to BLoC you add events and EventHandler.
More you can read here (official docs): Cubit vs BLoC
Cubits are easy to understand and implement, while still being quite powerful. I wrote a few small to middle sized apps using cubits and hooks, so it's a matter of preference whether one chooses Blocs or Cubits or both.
You only need to be careful with the things that you emit in state, many times I saw developers (me included ;) being surprised that UI doesn't update. It was because the object/list was equal to the previous one, either becase equalTo() wasn't correctly implemented or the list size remained the same.
I actually wrote an extensive Flutter tutorial showing how to write a clean Flutter app with cubits and hooks.