How Does it Work
Stacked creates a simple View to ViewModel relationship that allows us to perform state management without a lot of boilerplate code. The idea is to completely separate our state from our UI, allowing us to easily test and extend logic without affecting the UI. Let's see how that works.
Let's Try it Out
In the app that you created during the Get Started guide we'll create a new View called counter
, I know, very original, but this is just to show you the basics of Stacked. To create a new View with Stacked run the following command:
stacked create view counter
This command will create three files for us:
- counter_view.dart: This is where you build your UI using Flutter widgets
- counter_viewmodel.dart: Store state and perform actions for users as they interact
- counter_viewmodel_test.dart: Contains all the unit tests for the
CounterViewModel
Let's dissect the View first.
Counter View
The CounterView
contains our UI that will be shown on the device. By looking at the View structure, you can see we don't extend from StatelessWidget
or StatefulWidget
. Instead, we extend from a StackedView
:
class CounterView extends StackedView<CounterViewModel> {
// A builder function that gives us a ViewModel
Widget builder(
BuildContext context,
CounterViewModel viewModel,
Widget? child,
) {
return Scaffold(
...
);
}
CounterViewModel viewModelBuilder(BuildContext context) => CounterViewModel();
}
In addition, you can also see a required override called viewModelBuilder
. This function constructs our ViewModel that will store our state. But before we jump into that, let me show you how this View / ViewModel thing works. This is the foundation of Stacked's State Management. The goal of the StackedView
is to "bind our ViewModel to our UI". This allows us to completely separate state and logic code from our UI. The mechanism is quite simple.
Build the UI from the ViewModel, update the ViewModel and then rebuild the UI from that ViewModel. Here's a little diagram that visually depicts the explanation below:
- The
viewModelBuilder
creates ourViewModel
- Stacked passes that
ViewModel
to ourbuilder
function - The
builder
function creates our UI - The user interacts with that UI
- The interaction goes to the
ViewModel
, updates theViewModel
, then requests torebuildUi
- The
rebuildUi
function triggers thebuilder
function with the updatedViewModel
to rebuild the UI
That's how simple the process is. With this process you can manage 100% of all state scenarios without ever having to write state related code in your View file. Clean separation of your state, which is the best starting point for a maintainable application.
Counter ViewModel: Flutter State Management
The ViewModel
generated is probably the most basic: it's a normal class that extends from BaseViewModel
:
class CounterViewModel extends BaseViewModel {}
And the state management is just as simple. For our counter example we want to store an integer that counts up, so we'll create a private integer value along with a function to increment it. When we've changed the value we call rebuildUi
which will call our builder
function in the View:
class CounterViewModel extends BaseViewModel {
int _counter = 0;
int get counter => _counter;
void incrementCounter() {
_counter++;
rebuildUi();
}
}
To wrap up the example, let's display the counter on screen and call the incrementCounter
function when the FAB is tapped. In the counter_view.dart
file, update the builder function to return the following:
Widget builder(BuildContext context, CounterViewModel viewModel, Widget? child) {
return Scaffold(
floatingActionButton:
FloatingActionButton(onPressed: viewModel.incrementCounter),
body: Center(
child: Text(
viewModel.counter.toString(),
style: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
),
);
}
And the last thing to do is to open up startup_viewmodel.dart
and change:
_navigationService.replaceWithHomeView();
to
_navigationService.replaceWithCounterView();
Finally, run your app by running flutter run
or starting a debug session in VS Code. On the screen, you should now see a basic counter that increases as you tap on the FloatingActionButton
.
We'll talk about the StartupView
and Navigation
next.
We're ready for the Web 🚀
Master Flutter on the web with the official Flutter Web Course