Skip to content

Optimize unneeded rebuild when pushing overlay/navigator opaque content #45797

Closed

Description

@rrousselGit

Internal: b/146212303

Related to: #11655

Currently, there's an optimization performed by Navigator/Overlay such that when a full-screen opaque content is placed above another content, the content behind will stop painting.

It is useful, but at the same time there's an irony:
The act of flagging the previously visible widget tree as no longer needing to be painted forces the entire associated widget tree to rebuild.

From my understanding, this is caused by a change in the depth of the associated widget tree during the transition. This, therefore, cause the widget tree to be deactivated and reactivated for a frame, which calls build/didChangeDependencies on everything.

It is suboptimal, especially considering the widget tree is actually no longer visible.

My theory is that is should be possible to fix this inconvenience by a change on this line

by changing it from:

_Theatre(
  onstage: Stack(
    fit: StackFit.expand,
    children: onstageChildren.reversed.toList(growable: false),
  ),
  offstage: offstageChildren,
);

to:

_Theatre(
  // somehow make _RenderTheatre fill the gap of the missing Stack
  onstage: onstageChildren.reversed.toList(growable: false),
  offstage: offstageChildren,
);

This should remove the reparenting, which should no longer unnecessarily rebuild the no longer visible widget tree.

Activity

  1. added
    engineflutter/engine related. See also e: labels.
    f: routesNavigator, Router, and related APIs.
    c: proposalA detailed proposal for a change to Flutter
    c: performanceRelates to speed or footprint issues (see "perf:" labels)
    frameworkflutter/packages/flutter repository. See also f: labels.
    on Nov 29, 2019
  2. SBDavid commented on Dec 9, 2019

    @SBDavid

    This is working as intended. In general, you should assume that all widgets can rebuild at any time, that they don't is mostly just an optimisation.

    In particular, routes will rebuild because their navigator state has changed so they might need to update how they draw back buttons and the like.

    I fix the "rebuild" problem by adding a key to Overlay build function.

    First I add a GlobalKey to OverlayEntry class. For example _key2. This key will be used in TickerMode, the purpose is to give a Key to overlay offstage elements.

    class OverlayEntry {
    
      OverlayState _overlay;
      final GlobalKey<_OverlayEntryState> _key = GlobalKey<_OverlayEntryState>();
    
      final GlobalKey _key2 = GlobalKey();
    }

    And the next step is add _key2 to build function.

    class OverlayState extends State<Overlay> with TickerProviderStateMixin {
    @override
      Widget build(BuildContext context) {
        final List<Widget> onstageChildren = <Widget>[];
        final List<Widget> offstageChildren = <Widget>[];
        bool onstage = true;
        print("overlay build");
        for (int i = _entries.length - 1; i >= 0; i -= 1) {
    
          final OverlayEntry entry = _entries[i];
          if (onstage) {
            print("onstage ${entry._key}");
            onstageChildren.add(_OverlayEntry(entry));
            if (entry.opaque)
              onstage = false;
          } else if (entry.maintainState) {
            offstageChildren.add(TickerMode(key: entry._key2, enabled: false, child: _OverlayEntry(entry)));
            print("offstage ${entry._key}");
          } else {
            print("!maintainState ${entry._key}");
          }
        }
        return _Theatre(
          onstage: Stack(
            fit: StackFit.expand,
            children: onstageChildren.reversed.toList(growable: false),
          ),
          offstage: offstageChildren,
        );
      }
    
    }

    And then Flutter diff algorithm will know how to update offstage element properly, other than just inflateWidget. The inflateWidget method will reload images which were used in offstage elements, thus cause serious performance issue.

  3. jkurtw commented on Dec 13, 2019

    @jkurtw

    b/146212303

  4. liyuqian commented on Dec 16, 2019

    @liyuqian
    Contributor

    Is the code in #11655 (comment) also a reproduction for the issue here? If this issue has a significant performance impact, maybe it's nice to write a Flutter driver test (https://flutter.dev/docs/cookbook/testing/integration/profiling) so we can have some metrics to track.

  5. added this to the milestone on Dec 18, 2019
  6. modified the milestones: , April 2020 on Jan 8, 2020
  7. removed
    engineflutter/engine related. See also e: labels.
    on Jan 13, 2020
  8. goderbauer commented on Jan 17, 2020

    @goderbauer
    Member

    There's a fix out for review: #48900.

  9. goderbauer commented on Jan 22, 2020

    @goderbauer
    Member

    As documented in the PR, this change had a positive affect on some of our transition benchmarks: #48900 (comment).

    Thanks for the suggestion!

  10. added
    perf: speedPerformance issues related to (mostly rendering) speed
    on May 1, 2020
  11. github-actions commented on Aug 21, 2021

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  12. locked as resolved and limited conversation to collaborators on Aug 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

c: performanceRelates to speed or footprint issues (see "perf:" labels)c: proposalA detailed proposal for a change to Flutterf: routesNavigator, Router, and related APIs.frameworkflutter/packages/flutter repository. See also f: labels.perf: speedPerformance issues related to (mostly rendering) speed

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions