website
website copied to clipboard
Add two more debug mode limitations to 'Flutter's build modes' page
Update the following pages to reflect more of the differences between debug and release mode.
- https://docs.flutter.dev/testing/build-modes
- https://github.com/flutter/flutter/wiki/Flutter's-modes
Two missing limitations
-
Members that begin with the word debug such as
debugDoingLayoutonly return valid values in debug mode. In release mode they tend to return null. In the future we may provide lints if these methods are called outside of assert blocks. -
const cannonicalization of widgets is slightly different in debug mode to support debugging tools such as the inspector and to support better error reporting. You only need to care about this if you are writing code that will break if widgets are not fully cannonicalized. In debug mode widgets are still cannonicalized enough to get the main performance benefit from widget cannonicalization. Non-widget objects are canonicalized the same in debug and release mode.
In debug mode Flutter tracks exactly what source code line each widget was created on. This is great for debugging but means that how const objects are canonicalized is slightly different in debug and release mode. In release mode, absolutely all const objects with identical parameters are normalized to the same instance. In debug mode, only const objects. In debug mode, all const widgets created on the same line # will have the same object but the identical const widget created at a different line will not.
In practice, this still enables the main performance benefit const widgets provide for Flutter which is caching instances of the same widget created at the same line number and column.
How this impacts tests
This has a bigger impact on test code than application code as in tests it is reasonable to call
pumpWidget multiple times in a test with very similar widget trees. For this case, the const widgets will be different objects and so the test will not reproduce bugs that only occur in the presence of const canonicalization. In general, if you write code that only relies on const canonicalization for performance and not correctness, you never need to care.
See: https://github.com/dart-lang/linter/issues/1842https://github.com/dart-lang/linter/issues/1842 For examples of how to rewrite code if you need to reproduce the same const object at multiple locations in a test.
@Sfshaza
Thank you, that was really helpful!
It still confuses people with the different behavior between debug and release mode.
like below code, I expect the ConstWidget with key will never rebuild, and it works in release mode, but in debug mode it rebuilds every time.
class BadPage extends StatefulWidget {
const BadPage({super.key});
@override
State<BadPage> createState() => _BadPageState();
}
class _BadPageState extends State<BadPage> {
bool _showTop = true;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () => setState(() => _showTop = !_showTop),
child: const Text('toggle', style: TextStyle(fontSize: 30))),
if (_showTop) const ConstWidget(key: ValueKey('key1')),
const Divider(),
if (!_showTop) const ConstWidget(key: ValueKey('key1')),
],
),
),
);
}
}
class ConstWidget extends StatelessWidget {
const ConstWidget({super.key});
@override
Widget build(BuildContext context) {
print('key: $key, element: ${context.hashCode}, hashCode: $hashCode');
return Text(
'key: $key, element: ${context.hashCode}, hashCode: $hashCode',
style: const TextStyle(fontSize: 25),
);
}
}