I'm currently reading the example code of the provider package:
// ignore_for_file: public_member_api_docs
import 'package:flutter/foundation.dart';
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 {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(builder: (_) => Counter()),
],
child: Consumer<Counter>(
builder: (context, counter, _) {
return MaterialApp(
supportedLocales: const [Locale('en')],
localizationsDelegates: [
DefaultMaterialLocalizations.delegate,
DefaultWidgetsLocalizations.delegate,
_ExampleLocalizationsDelegate(counter.count),
],
home: const MyHomePage(),
);
},
),
);
}
}
class ExampleLocalizations {
static ExampleLocalizations of(BuildContext context) =>
Localizations.of<ExampleLocalizations>(context, ExampleLocalizations);
const ExampleLocalizations(this._count);
final int _count;
String get title => 'Tapped $_count times';
}
class _ExampleLocalizationsDelegate
extends LocalizationsDelegate<ExampleLocalizations> {
const _ExampleLocalizationsDelegate(this.count);
final int count;
@override
bool isSupported(Locale locale) => locale.languageCode == 'en';
@override
Future<ExampleLocalizations> load(Locale locale) =>
SynchronousFuture(ExampleLocalizations(count));
@override
bool shouldReload(_ExampleLocalizationsDelegate old) => old.count != count;
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Title()),
body: const Center(child: CounterLabel()),
floatingActionButton: const IncrementCounterButton(),
);
}
}
class IncrementCounterButton extends StatelessWidget {
const IncrementCounterButton({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: Provider.of<Counter>(context).increment,
tooltip: 'Increment',
child: const Icon(Icons.add),
);
}
}
class CounterLabel extends StatelessWidget {
const CounterLabel({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
final counter = Provider.of<Counter>(context);
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'${counter.count}',
style: Theme.of(context).textTheme.display1,
),
],
);
}
}
class Title extends StatelessWidget {
const Title({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text(ExampleLocalizations.of(context).title);
}
}
At first I was confused to see the following code. It is a MultiProvider, immediately followed by a Consumer, at the top of the Widget tree:
return MultiProvider(
providers: [
ChangeNotifierProvider(builder: (_)=>Counter()),
],
child: Consumer<Counter>(
builder: (context, counter, _){
return MaterialApp(
home: const MyHomePage()
);
},
),
);
I was wondering: Isn't this really bad for performance?
Everytime the state of the consumer is updated, all the tree has to be rebuilt.
Then I realized the const
qualifiers everywhere. This seems like a very neat setup.
I decided to debug through it and see when and where widgets are rebuilt.
When the app is first started, flutter goes down the tree and builds the widgets one by one. This makes sense.
When the button is clicked and the Counter
is incremented, builder
is called on the Consumer at the very top of the tree. After that, build
is called on CounterLabel
and IncrementCounterButton
.
CounterLabel
makes sense. This is not const
and will actually change its content.
But IncrementCounterButton
is marked as const
. Why does it rebuild?
It is not clear to me why some const
widgets are rebuilt while others aren't. What is the system behind this?
const Center(child: new CounterLabel())
won't compile. Theconst
keyword is infered, and the example is just syntax sugar forconst Center(child: const CounterLabel())
– Rémi Rousselet