Skip to content

Instantly share code, notes, and snippets.

@minikin
Created September 29, 2020 09:18
Show Gist options
  • Save minikin/1f90f4ed313d4003dade39ce72219368 to your computer and use it in GitHub Desktop.
Save minikin/1f90f4ed313d4003dade39ce72219368 to your computer and use it in GitHub Desktop.
Flutter Widgets: Classes vs Methods
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();
}
}
@minikin
Copy link
Author

minikin commented Sep 29, 2020

@minikin
Copy link
Author

minikin commented Sep 29, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment