-
-
Notifications
You must be signed in to change notification settings - Fork 0
ViewModels with Cubit
View Models are representations of the state of a feature, without it being connected to the actual UI code. I have used Cubit in this app, but you could also use Bloc, RiverPod, Change Notifiers, the list goes on and on.
Basically the View Models help keep UI code clean and only responsible for displaying views and accepting user interaction.
Some simple states can be managed with Statefuly widgets.
Cubit and Bloc are basically the same thing. If you see Bloc anywhere, just know that it Bloc widgets work with Cubits.
There is a HomeScreenCubit and Survey Cubit. They both manage the state for their respective feature.
First, define an abstract state for the Cubit, ("SurveyState" for example). Then add all of the states that the feature will be in as classes that extend the abstract state (LoadingHomeScreenState or FailureHomeScreenState). In the super constructor of the Cubit, put in the intial state of the Cubit (UninitializedHomeScreenState() for example).
Next, Create functions that represent user interaction in the Cubit (HomeScreenCubit only has one - loadHomeScreen(), SurveyCubit has many more). These are the functions that will be called from somewhere in the UI code (although you could reference another Cubit from a different cubit and call its functions, that gets pretty hard to keep track of).
When one of these functions is called, repository calls will fetch, set, update, and delete data as you would like. When the feature needs to be notified of a state change ('hey we waited for the data, now lets present it') you can emit() a state. Normally, you will emit a LoadingState first when you call these functions, process some repository calls, and then emit a Loaded state if no exceptions are thrown or a Failed State if there are errors.
The HomeScreenCubit has multiple "success" states (WelcomeFirstTimeHomeScreenState, WelcomeBackHomeScreenState, etc.)
You can use Enums, primitive data types or Classes to represent states. Classes work really well because you can add fields that hold any data you want to utilize for that state.
Note: The creators of bloc recommend you use Value typing like (https://pub.dev/packages/equatable) but I havent seemed to have needed it. I believe the concern is if you emit the same state twice, the widgets listening to these changes will not update because they hold referential equality. If you make them value types though, they wont be the same object in memory, so the widgets will rebuild. If you emit a Loading State at the beginning of each Cubit function call though, you should not run into this issue.
-
Bloc Provider - above the MaterialApp() class in main, use either a BlocProvider or a MultiBlocProvider to provide the Cubit (Or Bloc) View Model to the rest of the app.
-
call a Cubit function - context.bloc().loadHomeScreen() will tell the HomeScreen Cubit to call that function. Just doing this will not change the UI, but the View Model will begin processing and emitting states. (you can also use BlocProvider.of(context).loadHomeScreen() to do the same thing)
-
BlocBuilders are UI widgets that will update whenever a Cubit or Bloc's state changes. The builder function contains the build context and the state. Do if checks on the state to decide what you want to render. See home_page.dart for an example on how different Home states are rendered.
-
BlocListeners are UI widgets that will perform an action based on a state change. Navigation, animations, snackbars can be triggered from responses to state changes inside of the BlocListener. Make sure to wrap the body of a Scaffold, not the scaffold itself if you want to perform navigations for a scaffold (Keep the listeners below the scaffold level). See survey_question_page.dart to see how navigation within the Survey Feature behaves.
Libertas People • releases page • slavefreetrade website