-
-
Save minikin/1f90f4ed313d4003dade39ce72219368 to your computer and use it in GitHub Desktop.
import 'package:flutter/material.dart'; | |
void main() => runApp(MyApp()); | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter Demo', | |
theme: ThemeData( | |
primarySwatch: Colors.blue, | |
), | |
home: 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> { | |
int _counter = 0; | |
void _incrementCounter() { | |
setState(() { | |
_counter++; | |
}); | |
} | |
@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:', | |
), | |
Text( | |
'$_counter', | |
style: Theme.of(context).textTheme.subtitle1, | |
), | |
const MyWidgetClass(key: const Key('const')), | |
MyWidgetClass(key: Key('non-const')), | |
_buildSomeWidgets(), | |
], | |
), | |
), | |
floatingActionButton: FloatingActionButton( | |
onPressed: _incrementCounter, | |
tooltip: 'Increment', | |
child: Icon(Icons.add), | |
), // This trailing comma makes auto-formatting nicer for build methods. | |
); | |
} | |
Widget _buildSomeWidgets() { | |
print('${DateTime.now()} Rebuild _buildSomeWidgets'); | |
return Container(); | |
} | |
} | |
class MyWidgetClass extends StatelessWidget { | |
const MyWidgetClass({Key key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
print('${DateTime.now()} Rebuild MyWidgetClass $key'); | |
return Container(); | |
} | |
} |
Flutter cannot optimize widgets returned from methods.
Although this point hasn't been widely publicized as a best practice,
large build methods should be considered splitting into separate widgets instead of methods to ensure optimal performance.
In short, methods can be called every time a state change of the parent widget occurs, thus forcing rebuilds, while the build method of a widget is only called when its state is changed. Also, by using StatelessWidgets instead of functions, these widgets can have keys, their context and can be inspected in the debugger.
By adopting a strategy of breaking larger widgets into sub-widgets, the app's performance will remain optimized, while the sub-widgets themselves will be easier to test and debug.
I've made an example you can run here:
https://dartpad.dev/1f90f4ed313d4003dade39ce72219368
You will see in your console:
- Const version's build method is run the first time, then when you increment the counter the
build
method is never called again - method version is run on every build
- non-const version's build method is run on every build
Classes can update independently from each other. Functions cannot. So it is possible for classes to partially update the widget tree.
I hope this helps clarify the difference between classes and methods and the actual impact it has on rebuilds.
https://dartpad.dev/1f90f4ed313d4003dade39ce72219368