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
viewModelBuildercreates ourViewModel - Stacked passes that
ViewModelto ourbuilderfunction - The
builderfunction creates our UI - The user interacts with that UI
- The interaction goes to the
ViewModel, updates theViewModel, then requests torebuildUi - The
rebuildUifunction triggers thebuilderfunction with the updatedViewModelto 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