Flutter provides (heh)  us with an amazingly easy way to create mobile applications. Once again, a fundamental thing you have to master is the management of state

There are numerous patterns that can be used to manage state within Flutter, such as the BLoC pattern, Redux, setState, MobX, Provider, and much more. Each method has a different way to manage the same concept. How do we pass data around our application in an efficient, truthful manner?

Installing Provider


Prior to answering this question with an example, we'll need to install the Provider library into our project. Head over to your pubspec.yaml and add the following to the dependencies block:

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.2
  provider: ^2.0.1

As always, it's important that you keep the tab spacing the same as the above.

Using ChangeNotifier

Now we'll need to create a class at lib/theme.dart which uses with ChangeNotifier as this gives us access to the notifyListeners method. Inside of the class, we'll make a simple getTheme and setTheme method:

import 'package:flutter/material.dart';

class ThemeChanger with ChangeNotifier {
  ThemeData _themeData;

  ThemeChanger(this._themeData);

  getTheme() => _themeData;
  setTheme(ThemeData theme) {
    _themeData = theme;

    notifyListeners();
  }
}

Once we're setting the new theme, we're calling notifyListeners to push the changes to any client listeners. We'll see this in action next.

Interacting with ChangeNotifier

We can use the ChangeNotifierProvider to give us the ability to listen to the value of our current Theme. You can see this in the below example:

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

import './theme/theme.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<ThemeChanger>(
      builder: (_) => ThemeChanger(ThemeData.dark()),
      child: new MaterialAppWithTheme(),
    );
  }
}

The key areas to consider is this part:

return ChangeNotifierProvider<ThemeChanger>(
  builder: (_) => ThemeChanger(ThemeData.dark()),
  child: new MaterialAppWithTheme(),
);

Using the builder method, we can return a ThemeChanger that uses the ThemeData.dark() factory to generate a dark theme by default. We can listen to this inside of our MaterialAppWithTheme by using Provider.of():

class MaterialAppWithTheme extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final theme = Provider.of<ThemeChanger>(context);

    return MaterialApp(
      home: HomePage(),
      theme: theme.getTheme(),
    );
  }
}

In order to see this, we'll need to create and import the HomePage widget at pages/home.dart:

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

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    ThemeChanger _themeChanger = Provider.of<ThemeChanger>(context);

    return new Scaffold(
      appBar: AppBar(
        title: Text('Home'),
      ),
      body: Container(
        child: Column(
          children: <Widget>[
            FlatButton(
                child: Text('Dark Theme'),
                onPressed: () => _themeChanger.setTheme(ThemeData.dark())),
            FlatButton(
                child: Text('Light Theme'),
                onPressed: () => _themeChanger.setTheme(ThemeData.light())),
          ],
        ),
      ),
    );
  }
}

Whenever we click either of our buttons, it calls _themeChanger.setTheme(value) which is Provided to our MaterialAppWithTheme widget, and thus, our theme updates.  In order to see this, you'll also need to ensure that home.dart has been imported in main.dart:

import './pages/home.dart';

This is how it looks: