How to avoid unnecessary rebuilds while using provider in flutter?

Muhammad picture Muhammad · Jun 8, 2019 · Viewed 8.6k times · Source

I am using provider in my app, but I faced with unnecessary building.

Example

class AllWidget extends StatelessWidget{

  @override
  Widget build(BuildContext context){
    print('state build called');
    return ChangeNotifierProvider(
            builder: (_) => MyCounter(),
            child: Column(children: <Widget>[
                  MyCounterText(),
                  MyIncreaseButton(),
                  MyDecreaseButton(),
            ],
          ),
    );
  }
}

class MyCounterText extends StatelessWidget{

  @override
  Widget build(BuildContext context) {
    final myCounter = Provider.of<MyCounter>(context, listen: false);
    print('MyCounterText');
    return Text(myCounter.num.toString());

  }
}

class MyIncreaseButton extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    final myCounter = Provider.of<MyCounter>(context, listen: false);
    print('MyIncreaseButton');
    return RaisedButton(
      child: Text('Increase +'),
      onPressed: ()=> myCounter.increament(),
    );

  }
}


class MyDecreaseButton extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    final myCounter = Provider.of<MyCounter>(context, listen: false);
    print('MyDecreaseButton');
    return RaisedButton(
      child: Text('Decrease -'),
      onPressed: ()=> myCounter.decreament(),
    );

  }
}

Now if I click on MyIncreaseButton widget, to inscrease the value, the MyDecreaseButton widget builds too, even when I dont click on it.

And vice versa, if I click on MyDecreaseButton widget, to descrease the value, the MyIncreaseButton widget builds too, even when I dont click on it.

My Expectation is:

When clicking MyIncreaseButton widget, MyDecreaseButton widget should not build.

Answer

R&#233;mi Rousselet picture Rémi Rousselet · Aug 24, 2019

There are multiple solutions:

  • use context.read or pass listen: false to Provider.of:
RaisedButton(
  onPressed: () {
    context.read<MyModel>().increment();
    // alternatively, do:
    Provider.of<MyModel>(context, listen: false).increment();
  },
  child: Child(),
);
  • use context.select:
Widget build(context) {
  final increment = context.select((MyModel m) => m.increment);

  return RaisedButton(
    onPressed: increment,
    child: Child(),
  ); 
}
  • use Selector:
Widget build(context) {
  return Selector<MyModel, VoidCallback>(
    selector: (_, model) => model.increment,
    builder: (_, increment) {
      return RaisedButton(
        onPressed: increment,
        child: Child(),
      );
    },
  );
}