Quick Dart recap
Before diving into the details, we want to briefly review how the Dart
language treats constant objects. For example, consider this extremely simple piece of code:
class Example {
const Example();
}
void main() {
print(Example() == Example()); // false
print(const Example() == const Example()); // true
}
class Example {
const Example();
}
void main() {
const a = Example();
const b = Example();
print(identical(a, b)); // true
}
// 'child' is the old widget that needs to be removed.
// 'newWidget' is the new widget to be inserted in the tree
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
if (newWidget == null) { // 1
return null;
}
final Element newChild;
if (child != null) {
if (child.widget == newWidget) { // 2
newChild = child;
} else if (Widget.canUpdate(child.widget, newWidget)) { // 3
child.update(newWidget);
newChild = child;
} else {
deactivateChild(child);
newChild = inflateWidget(newWidget, newSlot); // 4
}
}
return newChild;
}
The function allows you to write less code but in Dart you cannot use a constant constructor in front of a function! As such, cannot do this...
@override
Widget build(BuildContext context) {
return const buildContainer(); // Compiler error!!!
}
... but can do this, which is much better:
@override
Widget build(BuildContext context) {
return const Example();
}
Manually caching widgets
Sometimes, you may wish to create a constant widget but you cannot because it has some external dependencies that forbids the const
usage. For example, look at this Container
:
class Example extends StatelessWidget {
final Color color;
const Example({
super.key,
required this.color,
});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Here we have the list:'),
const SizedBox(height: 20),
Container(
height: 30,
width: 30,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: SomeOtherWidget(
color: color,
),
),
],
);
}
}
Both Container
and its child depend on color
, but it may not always change. It may not change at all but we still cannot use const
. In this case, we can manually cache the widget:
class Example extends StatefulWidget {
final Color color;
const Example({
super.key,
required this.color,
});
@override
State<Example> createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
late var child = _ExampleChild(widget.color);
@override
void didUpdateWidget(covariant Example oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.color != oldWidget.color) {
child = _ExampleChild(widget.color);
}
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Here we have the list:'),
const SizedBox(height: 20),
child,
],
);
}
}
class _ExampleChild extends StatelessWidget {
final Color color;
const _ExampleChild(this.color);
@override
Widget build(BuildContext context) {
return Container(
height: 30,
width: 30,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: SomeOtherWidget(
color: color,
),
);
}
}
There is some more code to write but the result is the same as using a const
constructor! Since the widget is now located in the state...
class _ExampleState extends State<Example> {
late var child = _ExampleChild(widget.color);