website icon indicating copy to clipboard operation
website copied to clipboard

Add two more debug mode limitations to 'Flutter's build modes' page

Open jacob314 opened this issue 5 years ago • 2 comments

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 debugDoingLayout only 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

jacob314 avatar Nov 15 '19 16:11 jacob314

Thank you, that was really helpful!

Amal4m41 avatar Dec 21 '21 09:12 Amal4m41

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),
    );
  }
}

ebwood avatar Jan 09 '24 15:01 ebwood