I'm working through understanding how the Provider package works with Flutter but confused on how listen:false works.
I wrote some basic code using the usual Counter example from a new Flutter project. I created three types of stateless widgets each using a Provider:
- Provider.of(context)
- Consumer
- Provider.of(context, listen: false)
The third example was to show how access the provider object and call methods on it without it rebuilding.
When I run the application all of the widget counts are changing - and I only expect it to change in the first two.
This is a simple example - what am I doing wrong?
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(builder: (_) => Counter()),
],
child: MaterialApp(
title: 'Provider Demo',
theme: ThemeData(
primarySwatch: Colors.amber,
),
home: MyHomePage(title: 'Provider Demo Home Page'),
),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
Counter counter = Provider.of<Counter>(context);
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ExampleProviderWidget(),
ExampleConsumerWidget(),
ExampleNoListenWidget()
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => counter.increment(),
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class ExampleProviderWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
Counter counter = Provider.of<Counter>(context);
return Container(
color: Colors.green,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Provider.of<Counter>(context):',
),
Text(
'${counter.count}',
style: Theme.of(context).textTheme.display1,
),
],
),
),
);
}
}
class ExampleConsumerWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<Counter>(
builder: (context, counter, _) {
return Container(
color: Colors.blue,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Consumer<Counter>(context):',
),
Text(
'${counter.count}',
style: Theme.of(context).textTheme.display1,
),
],
),
),
);
},
);
}
}
class ExampleNoListenWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
Counter counter = Provider.of<Counter>(context, listen: false);
return Container(
color: Colors.red,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Provider.of<Counter>(context, listen: false):',
),
Text(
'${counter.count}',
style: Theme.of(context).textTheme.display1,
),
RaisedButton(
child: Text("Increment"),
onPressed: () => counter.increment(),
)
],
),
),
);
}
}
What Remi suggested is a working approach (hack). But I believe the better way in this case will be to remove the call Counter counter = Provider.of(context); from the MyHomePageClass. That call is polluting the scope of your Counter object. As suggested by flutter.io , It is best practice to put your Consumer widgets as deep in the tree as possible. As you did in the ExampleProviderWidget(), ExampleConsumerWidget(),ExampleNoListenWidget(). So make the floatingActionButton a separate widget class and have its own Provider.of(context). And your MyHomePageClass will not have to call Provider.of.
That's because, while the widget that called
Provider.of
withlisten:false
did not rebuild want to rebuild, its parent forced it to.In your example, when
Counter
changes,MyHomePage
rebuilds and recreate the widget that specifiedlisten:false
, which in turn force it to rebuild too.MyHomePage
should specifylisten: false
too here.