Skip to content
Boring Doc
Official documentation (snapshot)doc

State management in Flutter

Instructions on how to manage state with ChangeNotifiers.

Source last updated Oct 21, 2026 · last synchronized 3 weeks ago · commit b3045439b7c0 · read on docs.flutter.dev

This is a rendered snapshot of the official page as stored by Boring Doc for provenance and search. Site-specific components (embedded demos, DartPad, images with relative paths) may not render; use the official page for the full experience. Content © The Flutter Authors, CC BY 3.0.

Learn to create a ViewModel with ChangeNotifier and manage loading, success, and error states.


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.

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;
  Exception? error;
  bool isLoading = false;

  ArticleViewModel(this.model);
}

The ArticleViewModel holds three pieces of state:

  • summary: The current Wikipedia article data.
  • error: Any error that occurred during data fetching.
  • isLoading: A flag to show progress indicators.

Add constructor initialization

Update the constructor to automatically fetch content when the ArticleViewModel is created:

class ArticleViewModel extends ChangeNotifier {
  final ArticleModel model;
  Summary? summary;
  Exception? error;
  bool isLoading = false;

  ArticleViewModel(this.model) {
    fetchArticle();
  }

  // Methods will be added next.
  Future<void> fetchArticle() async {}
}

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.

Set up the fetchArticle method

Add the fetchArticle method that fetches data and manages state updates:

class ArticleViewModel extends ChangeNotifier {
  final ArticleModel model;
  Summary? summary;
  Exception? error;
  bool isLoading = false;

  ArticleViewModel(this.model) {
    fetchArticle();
  }

  Future<void> fetchArticle() async {
    isLoading = true;
    notifyListeners();

    // TODO: Add data fetching logic

    isLoading = false;
    notifyListeners();
  }
}

The ViewModel updates the isLoading 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 isLoading property to show a loading indicator while fetching a new article.

Retrieve an article from the ArticleModel

Complete the fetchArticle 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;
  Exception? error;
  bool isLoading = false;

  ArticleViewModel(this.model) {
    fetchArticle();
  }

  Future<void> fetchArticle() async {
    isLoading = true;
    notifyListeners();
    try {
      summary = await model.getRandomArticleSummary();
      error = null; // Clear any previous errors.
    } on HttpException catch (e) {
      error = e;
      summary = null;
    }
    isLoading = false;
    notifyListeners();
  }
}

Test the ViewModel

Before building the full UI, test that your HTTP requests work by printing results to the console. First, update the fetchArticle method to print the results:

Future<void> fetchArticle() async {
  isLoading = true;
  notifyListeners();
  try {
    summary = await model.getRandomArticleSummary();
    print('Article loaded: ${summary!.titles.normalized}'); // Temporary
    error = null; // Clear any previous errors.
  } on HttpException catch (e) {
    print('Error loading article: ${e.message}'); // Temporary
    error = e;
    summary = null;
  }
  isLoading = false;
  notifyListeners();
}

Then, update the MainApp widget to create the ArticleViewModel, which calls the fetchArticle 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.

Review

Test yourself

Was this useful?

Content from flutter/website, © The Flutter Authors, used under the Creative Commons Attribution 3.0 License; code samples under the BSD License.