0
votes

I don't really understand the difference between Provider.of() and Consumer. I have read here that Consumer is like Provider.of with listen: true.

However, in the bellow example, I don't get an error when I use Consumer, but I get one while using Provider.of. I am forced to use listen: false. The below example is the default flutter app with ChangeNotifierProvider implemented.

I will just change the code in floatingActionButton in main.dart to see diffences between Consumer, Provider.of listen: true and Provider.of listen: false

Code of counter.dart

import 'package:flutter/material.dart';

class Counter extends ChangeNotifier {
  int value = 0;

  void increment() {
    value++;
    notifyListeners();
  }

  void decrement() {
    value--;
    notifyListeners();
  }
}

Full code of main.dart, with floatingActionButton using Consumer. It is working

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

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: ChangeNotifierProvider<Counter>(
        create: (context) => Counter(),
        child: MyHomePage(title: 'Flutter Demo Home Page'),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Consumer<Counter>(builder: (context, counter, child) {
              return Text(
                counter.value.toString(),
                style: Theme.of(context).textTheme.headline4,
              );
            }),
          ],
        ),
      ),
      floatingActionButton:
          Consumer<Counter>(builder: (context, counter, child) {
        return FloatingActionButton(
          onPressed: () {
            counter.increment();
          },
          tooltip: 'Increment',
          child: Icon(Icons.add),
        );
      }),
    );
  }
}

Code of floatingActionButton using Provider.of, listen: true, not working

floatingActionButton: FloatingActionButton(
        onPressed: () {
          Provider.of<Counter>(context, listen: true).increment();
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),

Error : Tried to listen to a value exposed with provider, from outside of the widget tree...

Code of floatingActionButton using Provider.of, listen: false, working

floatingActionButton: FloatingActionButton(
    onPressed: () {
      Provider.of<Counter>(context, listen: false).increment();
    },
    tooltip: 'Increment',
    child: Icon(Icons.add),
  ),

I don't get it. I put listen: false, but it is still listening and rebuilding the widget

Thank you for your help

1

1 Answers

2
votes

listen:true needs to put inside a build widget tree. It purpose is to rebuild the whole widget if called (notifyListener).

@override
  Widget build(BuildContext context) {
    Counter counter = Provider.of<Counter>(context, listen: true);
...
    counter.increment(); // Will rebuild entire widget.
...

Consumer is a widget that call listen:true inside it but will only rebuild it's child widget.

Consumer<Counter>(builder: (_, counter, __) {
  return Column(
    children:[
      TextButton(
        onPressed: () => counter.increment(), // Will only rebuild this Column
        child: Icon(Icons.add),),
      Text(counter.value.toString());
}),

listen:false will access Provider without rebuild widget if called.

TextButton(
  onPressed: () { 
    Counter counter = Provider.of<Counter>(context, listen: false);
    counter.increment(); 
    // Will change Provider value, but won't rebuild. It will make widget that has 
    // Consumer as parent to rebuild (or with listen:true to rebuild). 
  }
  child: Icon(Icons.add),
),