Provider in Flutter


In this blog, we add provider to our counter app. To use provider in flutter first add the dependency in the pubspec.yaml file.

provider: ^6.0.2

Create a class named Counter and add the count variable:

import 'package:flutter/material.dart';

class Counter {
  var _count = 0;
}

To convert it into a provider class, extend ChangeNotifier from the material.dart package. This provides us with the notifyListeners() method, and will notify all the listeners whenever we change a value.

Now add a method to increment the counter:

import 'package:flutter/material.dart';

class Counter extends ChangeNotifier {
  var _count = 0;
  void incrementCounter() {
    _count += 1;
  }
}

At the end of this method we’ll call notifyListeners(). This will trigger a change all over the app to whichever widget is listening to it.

That's the beauty of the provider pattern in Flutter – you don’t have to care about manually dispatching to streams.

Finally, create a getter to return the counter value. We’ll use this to display the latest value:

import 'package:flutter/material.dart';

class Counter extends ChangeNotifier {
  var _count = 0;
  int get getCounter {
    return _count;
  }

  void incrementCounter() {
    _count += 1;
    notifyListeners();
  }
}

Listening to button clicks

Now that we have the provider set up, we can go ahead and use it in our main widget.

First, let's convert MyHomePage to a stateless widget instead of a stateful one. We'll have to remove the setState() call since that's only available in a StatefulWidget:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatelessWidget {
  int _counter = 0;
  final String title;
  MyHomePage({this.title});
  void _incrementCounter() {}
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}



With this done, we can now use the provider pattern in Flutter to set and get the counter value. On each button click we need to increment the counter value by 1.

So, in the _incrementCounter method (which is called when the button is pressed) add this line:

Provider.of<Counter>(context, listen: false).incrementCounter();

What’s happening here is that you’ve asked Flutter to go up in the widget tree and find the first place where Counter is provided. (I’ll tell you how to provide it in the next section.) This is what Provider.of() does.

The generics (values inside <> brackets) tell Flutter what type of provider to look for. Then Flutter goes up through the widget tree until it finds the provided value. If the value isn’t provided anywhere then an exception is thrown.

Finally, once you’ve got the provider, you can call any method on it. Here we call our incrementCounter method.

But we also need a context, so we accept the context as an argument and alter the onPressed method to pass the context as well:

void _incrementCounter(BuildContext context) {
  Provider.of<Counter>(context, listen: false).incrementCounter();
}

Note: We’ve set listen to false because we don’t need to listen to any values here. We’re just dispatching an action to be performed.

We'll get MultiProvider to wrap the MaterialApp widget:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider.value(
          value: Counter(),
        ),
      ],
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: MyHomePage(title: "AndroidVille Provider Pattern"),
      ),
    );
  }
}

With this, we’ve provided the provider to our widget tree and can use it anywhere below this level in the tree.

There’s just one more thing left: we need to update the value that's displayed.

Updating the text

To update the text, get the provider in the build function of your MyHomePage widget. We’ll use the getter we created to get the latest value.

Then just add this value to the text widget below.

And we’re done! This is how your final main.dart file should look:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider.value(
          value: Counter(),
        ),
      ],
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: MyHomePage(title: "AndroidVille Provider Pattern"),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final String title;
  MyHomePage({this.title});
  void _incrementCounter(BuildContext context) {
    Provider.of<Counter>(context, listen: false).incrementCounter();
  }

  @override
  Widget build(BuildContext context) {
    var counter = Provider.of<Counter>(context).getCounter;
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _incrementCounter(context),
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

Note: We haven’t set listen:false in this case because we want to listen to any updates in the count value.

Post a Comment

Previous Post Next Post

Subscribe Us


Get tutorials, Flutter news and other exclusive content delivered to your inbox. Join 1000+ growth-oriented Flutter developers subscribed to the newsletter

100% value, 0% spam. Unsubscribe anytime