Menu
CoddyTech

Provider: Share a Model

Part of the State Management section of Coddy's Flutter journey. Lesson 9 of 27.

Lifting state up works while the widgets are close. When a value is needed all over the app, on two screens, in the top bar, deep inside a list, you would pass it through every widget in between. Provider skips that. It is a package: a set of code you import with import 'package:provider/provider.dart';.

It has three parts. First, a model: a class that holds the data and extends ChangeNotifier. Its methods change the data and then call notifyListeners():

class CupCounter extends ChangeNotifier {
  int _cups = 0;
  int get cups => _cups;

  void addCup() {
    _cups++;
    notifyListeners();
  }
}

Second, a ChangeNotifierProvider high in the tree creates the model once and hands it to every widget below it:

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CupCounter(),
      child: const MyApp(),
    ),
  );
}

Third, any widget below asks for it: context.watch<CupCounter>() to show it, context.read<CupCounter>() to call a method. The title and the button below get the same counter, and neither one is passed anything.

challenge icon

Challenge

Medium

Open the app: it shows an error, because the title asks for a CupCounter and nothing above it provides one.

  • In main, wrap MyApp in a ChangeNotifierProvider that creates a CupCounter.

Try it yourself

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class CupCounter extends ChangeNotifier {
  int _cups = 0;

  int get cups => _cups;

  void addCup() {
    _cups++;
    notifyListeners();
  }
}

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const CupsTitle()),
      body: const Center(child: AddCupButton()),
    );
  }
}

class CupsTitle extends StatelessWidget {
  const CupsTitle({super.key});

  @override
  Widget build(BuildContext context) {
    final counter = context.watch<CupCounter>();
    return Text('Cups: ${counter.cups}');
  }
}

class AddCupButton extends StatelessWidget {
  const AddCupButton({super.key});

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () {
        context.read<CupCounter>().addCup();
      },
      child: const Text('Another cup'),
    );
  }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in State Management

Practice on your own: Online Flutter compiler