State management in Flutter
Instructions on how to manage state with ChangeNotifiers.
Learn to create a ViewModel with ChangeNotifier and manage loading, success, and error states.
What you'll accomplish
Steps
1
Introduction
Introduction
When developers talk about state-management in Flutter, they're essentially referring to the pattern by which your app updates the data it needs to render correctly and then tells Flutter to re-render the UI with that new data.
In MVVM, this responsibility falls to the ViewModel layer,
which sits between and connects your UI to your Model layer.
In Flutter, ViewModels use Flutter's ChangeNotifier class to
notify the UI when data changes.
To use ChangeNotifier, extend it in your state management class to
gain access to the notifyListeners() method,
which triggers UI rebuilds when called.
2
Create the basic view model structure
Create the basic view model structure
Create the ArticleViewModel class with its
basic structure and state properties:
class ArticleViewModel extends ChangeNotifier {
final ArticleModel model;
Summary? summary;
String? errorMessage;
bool loading = false;
ArticleViewModel(this.model);
}
The ArticleViewModel holds three pieces of state:
summary: The current Wikipedia article data.errorMessage: Any error that occurred during data fetching.loading: A flag to show progress indicators.
3
Add constructor initialization
Add constructor initialization
Update the constructor to automatically fetch content when the
ArticleViewModel is created:
class ArticleViewModel extends ChangeNotifier {
final ArticleModel model;
Summary? summary;
String? errorMessage;
bool loading = false;
ArticleViewModel(this.model) {
getRandomArticleSummary();
}
// Methods will be added next.
}
This constructor initialization provides immediate content when
a ArticleViewModel object is created.
Because constructors can't be asynchronous,
it delegates initial content fetching to a separate method.
4
Set up the getRandomArticleSummary method
Set up the getRandomArticleSummary method
Add the getRandomArticleSummary that fetches data and manages state updates:
class ArticleViewModel extends ChangeNotifier {
final ArticleModel model;
Summary? summary;
String? errorMessage;
bool loading = false;
ArticleViewModel(this.model) {
getRandomArticleSummary();
}
Future<void> getRandomArticleSummary() async {
loading = true;
notifyListeners();
// TODO: Add data fetching logic
loading = false;
notifyListeners();
}
}
The ViewModel updates the loading property and
calls notifyListeners() to inform the UI of the update.
When the operation completes, it toggles the property back.
When you build the UI, you'll use this loading property to
show a loading indicator while fetching a new article.
5
Retrieve an article from the ArticleModel
Retrieve an article from the ArticleModel
Complete the getRandomArticleSummary method to fetch an article summary.
Use a try-catch block
to gracefully handle network errors and
store error messages that the UI can display to users.
The method clears previous errors on success and
clears the previous article summary on error to maintain a consistent state.
class ArticleViewModel extends ChangeNotifier {
final ArticleModel model;
Summary? summary;
String? errorMessage;
bool loading = false;
ArticleViewModel(this.model) {
getRandomArticleSummary();
}
Future<void> getRandomArticleSummary() async {
loading = true;
notifyListeners();
try {
summary = await model.getRandomArticleSummary();
errorMessage = null; // Clear any previous errors.
} on HttpException catch (error) {
errorMessage = error.message;
summary = null;
}
loading = false;
notifyListeners();
}
}
6
Test the ViewModel
Test the ViewModel
Before building the full UI, test that your HTTP requests work by
printing results to the console.
First, update the getRandomArticleSummary method to
print the results:
Future<void> getRandomArticleSummary() async {
loading = true;
notifyListeners();
try {
summary = await model.getRandomArticleSummary();
print('Article loaded: ${summary!.titles.normalized}'); // Temporary
errorMessage = null; // Clear any previous errors.
} on HttpException catch (error) {
print('Error loading article: ${error.message}'); // Temporary
errorMessage = error.message;
summary = null;
}
loading = false;
notifyListeners();
}
Then, update the MainApp widget to create the ArticleViewModel,
which calls the getRandomArticleSummary method on creation:
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
// Instantiate your `ArticleViewModel` to test its HTTP requests.
final viewModel = ArticleViewModel(ArticleModel());
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Wikipedia Flutter'),
),
body: const Center(
child: Text('Check console for article data'),
),
),
);
}
}
Hot reload your app and check your console output. You should see either an article title or an error message, which confirms that your Model and ViewModel are wired up correctly.
7
Review
Review
What you accomplished
Here's a summary of what you built and learned in this lesson.Created the ArticleViewModel with ChangeNotifier
The ViewModel sits between your UI and Model, managing state and connecting the two layers. By extending
ChangeNotifier, your ViewModel gains the ability to notify listeners when data changes.
Managed loading, success, and error states
Your ViewModel tracks three pieces of state: loading, summary, and errorMessage. Using
try and catch, you handle network errors gracefully and maintain consistent state for each possible outcome.
Used notifyListeners to signal UI updates
Calling notifyListeners() tells any listening widgets to rebuild. You call it after setting
loading = true and again after the operation completes. This is how you can implement reactive UI updates in Flutter.
8
Test yourself
Test yourself
State Management Quiz
1 / 2-
A widget that displays notifications to the user.
Not quite
ChangeNotifier is not a widget; it's a class for managing state.
-
A class that can notify listeners when its data changes, enabling reactive UI updates.
That's right!
ChangeNotifier provides the notifyListeners method to signal widgets to rebuild when state changes.
-
A built-in Dart class for sending push notifications.
Not quite
ChangeNotifier is for in-app state management, not push notifications.
-
A type of animation controller in Flutter.
Not quite
Animation controllers are separate; ChangeNotifier is for state management.
-
Saves the current state to local storage.
Not quite
`notifyListeners()` signals UI updates; persistence requires separate implementation.
-
Tells any listening widgets to rebuild and reflect the new state.
That's right!
Calling `notifyListeners()` triggers a rebuild of all widgets listening to this ChangeNotifier.
-
Logs the state change to the console for debugging.
Not quite
It doesn't log anything; it signals listeners to rebuild.
-
Resets all state properties to their default values.
Not quite
`notifyListeners()` doesn't modify state; it just signals that state has changed.
Unless stated otherwise, the documentation on this site reflects Flutter 3.38.6. Page last updated on 2026-1-13. View source or report an issue.