First, the performance of the Flutter itself is not in question. The key is how the Flutter is used.
We need to avoid these errors when we write Flutter to be efficient.
1. Don’t pull widgets out of the method
When we have a huge layout page, it is common practice for many people to break up multiple methods into widgets.
The following example contains header, Center, and footer widgets
Class MyHomePage extends StatelessWidget {widget-_buildheaderWidget () {final size = 40.0; Return Padding(Padding: const EdgeInsets. All (8.0), child: CircleAvatar(backgroundColor: color.grey [700], child: CircleAvatar(backgroundColor: color.grey [700]) FlutterLogo( size: size, ), radius: size, ), ); } Widget _buildMainWidget(BuildContext context) { return Expanded( child: Container( color: Colors.grey[700], child: Center( child: Text( 'Hello Flutter', style: Theme.of(context).textTheme.display1, ), ), ), ); } widge_buildfooterWidget () {return Padding(Padding: const EdgeInsets. All (8.0), child: Text('This is the footer '), ); } @override Widget build(BuildContext context) { return Scaffold( body: Padding( padding: Const EdgeInsets. All (15.0), child: Column(mainAxisSize: mainAxisSize. Min, children: [ _buildHeaderWidget(), _buildMainWidget(context), _buildFooterWidget(), ], ), ), ); }}Copy the code
The fact that we refresh a part of the widget will refresh the entire widget is no doubt a waste of CPU.
The correct way to do this is to make these Stateless widgets as follows
class MyHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Padding(Padding: const EdgeInsets. All (15.0), child: Column(mainAxisSize: mainAxisSize. Min, children: [ HeaderWidget(), MainWidget(), FooterWidget(), ], ), ), ); }} Class HeaderWidget extends StatelessWidget {@override Widget Build (BuildContext Context) {final size = 40.0; Return Padding(Padding: const EdgeInsets. All (8.0), child: CircleAvatar(backgroundColor: color.grey [700], child: CircleAvatar(backgroundColor: color.grey [700]) FlutterLogo( size: size, ), radius: size, ), ); } } class MainWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Expanded( child: Container( color: Colors.grey[700], child: Center( child: Text( 'Hello Flutter', style: Theme.of(context).textTheme.display1, ), ), ), ); } } class FooterWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( padding: Const EdgeInsets. All (8.0), child: Text('This is the footer '),); }}Copy the code
Stateful/Stateless widgets have special caching mechanisms (based on keys, widget type, and attributes) that are only refreshed when necessary. In addition to this, it also helps us encapsulate and refactor our widgets. (Divide and rule)
It’s also a good idea to add the const keyword to our widgets, more on that later.
2. Avoid repeating the REBUILDING all wigets
A typical mistake is to use setState when we need to rebuild our StatefulWidget.
The following example is a widget that has a square in the center, a FAV button that changes color every time it is clicked, and a background image.
We also added some code for print to see how it works.
class _MyHomePageState extends State<MyHomePage> { Color _currentColor = Colors.grey; Random _random = new Random(); void _onPressed() { int randomNumber = _random.nextInt(30); setState(() { _currentColor = Colors.primaries[randomNumber % Colors.primaries.length]; }); } @override Widget build(BuildContext context) { print('building `MyHomePage`'); return Scaffold( floatingActionButton: FloatingActionButton( onPressed: _onPressed, child: Icon(Icons.colorize), ), body: Stack( children: [ Positioned.fill( child: BackgroundWidget(), ), Center( child: Container( height: 150, width: 150, color: _currentColor, ), ), ], ), ); } } class BackgroundWidget extends StatelessWidget { @override Widget build(BuildContext context) { print('building `BackgroundWidget`'); return Image.network( 'https://cdn.pixabay.com/photo/2017/08/30/01/05/milky-way-2695569_960_720.jpg', fit: BoxFit.cover, ); }}Copy the code
You will see two logs
flutter: building `MyHomePage`
flutter: building `BackgroundWidget`
Copy the code
Every time we click the button, we refresh the entire screen, scaffolding, background image widgets.
It is not a good idea to rebuild the entire widget; we only need to update the parts that really need to be updated.
It’s well known that you can solve this problem with things like Flutter_bloc, mobx, provider, etc., but few people know that you don’t actually need any extension packages. The Flutter framework itself already provides this.
We refactor the above code using ValueNotifier.
class _MyHomePageState extends State<MyHomePage> { final _colorNotifier = ValueNotifier<Color>(Colors.grey); Random _random = new Random(); void _onPressed() { int randomNumber = _random.nextInt(30); _colorNotifier.value = Colors.primaries[randomNumber % Colors.primaries.length]; } @override void dispose() { _colorNotifier.dispose(); super.dispose(); } @override Widget build(BuildContext context) { print('building `MyHomePage`'); return Scaffold( floatingActionButton: FloatingActionButton( onPressed: _onPressed, child: Icon(Icons.colorize), ), body: Stack( children: [ Positioned.fill( child: BackgroundWidget(), ), Center( child: ValueListenableBuilder( valueListenable: _colorNotifier, builder: (_, value, __) => Container( height: 150, width: 150, color: value, ), ), ), ], ), ); }}Copy the code
Now you don’t see those print things anymore.
When I do not want to separate the business logic from the view, we can also use the notifier thing to do so.
The following example uses ChangeNotifier
//------ ChangeNotifier class ----// class MyColorNotifier extends ChangeNotifier { Color myColor = Colors.grey; Random _random = new Random(); void changeColor() { int randomNumber = _random.nextInt(30); myColor = Colors.primaries[randomNumber % Colors.primaries.length]; notifyListeners(); } } //------ State class ----// class _MyHomePageState extends State<MyHomePage> { final _colorNotifier = MyColorNotifier(); void _onPressed() { _colorNotifier.changeColor(); } @override void dispose() { _colorNotifier.dispose(); super.dispose(); } @override Widget build(BuildContext context) { print('building `MyHomePage`'); return Scaffold( floatingActionButton: FloatingActionButton( onPressed: _onPressed, child: Icon(Icons.colorize), ), body: Stack( children: [ Positioned.fill( child: BackgroundWidget(), ), Center( child: AnimatedBuilder( animation: _colorNotifier, builder: (_, __) => Container( height: 150, width: 150, color: _colorNotifier.myColor, ), ), ), ], ), ); }}Copy the code
This avoids unnecessary refreshes of the last two widgets.
3. Use as const keywords as possible
4. Use the itemExtent attribute in the long list ListView
5. Avoid rebuilding in the AnimatedBuilder
6. Avoid using transparency in animations
3-6 I will make a supplement next time when I have time
Original text: Link