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:
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(),
)
],
),
),
);
}
}
listen: false will not cause widgets to rebuild when the value changes. If your widgets still rebuild with listen: false , then it is likely that even without the call to Provider. of , your widget would still rebuild. This can happen if the parent of your widget rebuilds too.
Every Context object comes with a Provider React component that allows consuming components to subscribe to context changes. The Provider component accepts a value prop to be passed to consuming components that are descendants of this Provider. One Provider can be connected to many consumers.
That's because, while the widget that called Provider.of
with listen:false
did not want to rebuild, its parent forced it to.
In your example, when Counter
changes, MyHomePage
rebuilds and recreate the widget that specified listen:false
, which in turn force it to rebuild too.
MyHomePage
should specify listen: false
too here.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With