Skip to content

Conversation

@moffatman
Copy link
Contributor

@moffatman moffatman commented Feb 7, 2022

When some aspect of the flutter MediaQuery changes, every widget which depends on MediaQuery.of(context) is rebuilt. Most of these rebuilds are unnecessary, since most changes to MediaQuery do not change more than 1 or 2 fields at a time (window resizing, system brightness changing, system font scale changing).

MediaQuery does not change often on mobile, but resizing desktop windows happens often and flutter should be able to handle being resized without dropping frames. There is one place that MediaQuery is rapidly updated on mobile: on iOS, as the keyboard opens, MediaQuery is updated every frame with the only change being the keyboard insets.

Here is a demo program, resizing the window causes MyHomePage to rebuild needlessly. The issue here could be alleviated by better Widget encapsulation, but many framework widgets are rebuilt on every change in MediaQuery when they depend on rarely-changing properties such as platformBrightness or textScaleFactor.

import 'package:flutter/cupertino.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const CupertinoApp(
      title: 'Flutter Demo',
      home: MyHomePage()
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: CupertinoDynamicColor.resolve(CupertinoColors.systemFill, context),
      child: ListView(
        children: <Widget>[
          // Some expensive build function
          for (int i = 0; i < 100000; i++) Text('Item $i', textAlign: TextAlign.center)
        ]
      )
    );
  }
}

This change makes MediaQuery an InheritedModel rather than an InheritedWidget, so any widget which knows it only depends on a specific property of MediaQuery the ability to declare that when reading the MediaQuery from the context.

For example,

MediaQuery.of(context).platformBrightness

could become

MediaQuery.of(context, MediaQueryAspect.platformBrightness).platformBrightness

as a performance optimization.

MediaQuery.of(context) will continue to work as it always has.

Fixes #102060

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • All existing and new tests are passing.

@flutter-dashboard flutter-dashboard bot added a: text input Entering text in a text field or keyboard related problems f: cupertino flutter/packages/flutter/cupertino repository f: material design flutter/packages/flutter/material repository. f: routes Navigator, Router, and related APIs. f: scrolling Viewports, list views, slivers, etc. framework flutter/packages/flutter repository. See also f: labels. labels Feb 7, 2022
@moffatman moffatman force-pushed the mediaquery branch 2 times, most recently from dadf6d6 to 1a2f307 Compare February 12, 2022 02:19
@skia-gold
Copy link

Gold has detected about 1 new digest(s) on patchset 2.
View them at https://flutter-gold.skia.org/cl/github/97928

@b3nni97
Copy link

b3nni97 commented Apr 18, 2022

Is this still being worked on?

@moffatman
Copy link
Contributor Author

I think I need to make an issue so that it will be reviewed. Will do that.

@nikitadol
Copy link
Contributor

nikitadol commented Apr 20, 2022

I propose a more universal solution:
Add a function select (which will be caught by InheritedWidget or InheritedModel), as was done in the package provider

https://github.com/rrousselGit/provider/blob/master/lib/src/inherited_provider.dart#L246

Since it will be useful not only for MediaQuery

@b3nni97
Copy link

b3nni97 commented Apr 28, 2022

Can anyone here review the PR? This improves the performance of many apps.

@FelixMittermeier
Copy link

This looks very promising 👍🏻 Thanks for your work.
These kind of performance improvements are always great

@aurangzaibsiddiqui
Copy link

aurangzaibsiddiqui commented Jun 11, 2022

Hi,

This can be very useful for my application as I am facing a lot of jank on soft keyboard pop-up. However, I am relatively new to Flutter programming. Can someone please guide me how I can use @moffatman's solution?

As in how do I pull his github into my Flutter code?

@b3nni97
Copy link

b3nni97 commented Jun 21, 2022

Can anyone here review this? This is not a breaking change and for all who modify their code a lot of unnecessary rebuilds will be avoided... @Piinks

@marctan
Copy link

marctan commented Aug 18, 2022

any update on this? still not in stable?

@moffatman
Copy link
Contributor Author

I thought of some default values here. But the more I think about it, I would rather not add new default values, and just keep the minimum compatibility the old behaviour. I don't see the value of hiding this likely accidental state where there is no MediaQuery ancestor.

Property Possible default value Notes
size WidgetsBinding.instance.window.physicalSize
orientation Could be imputed from size
devicePixelRatio 1.0
textScaleFactor 1.0 🟢 Existing behaviour
platformBrightness Brightness.light 🟢 Existing behaviour
padding EdgeInsets.zero
viewInsets EdgeInsets.zero
systemGestureInsets EdgeInsets.zero
viewPadding EdgeInsets.zero
alwaysUse24HourFormat false
accessibleNavigation false
invertColors false
highContrast false 🟢 Existing behaviour
disableAnimations false
boldTextOverride false 🟠 There is already MediaQuery.boldTextOverride (notice no of in the name), which has a default return value of false.

Would like to deprecate that one and rename to match as MediaQuery.boldTextOverrideOf.

Not sure if it's accepted to deprecated and rename just to match other names?
navigationMode NavigationMode.traditional
gestureSettings const DeviceGestureSettings()
displayFeatures const []

@goderbauer
Copy link
Member

I would rather not add new default values, and just keep the minimum compatibility the old behaviour.

I agree with this.

Copy link
Member

@goderbauer goderbauer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@gspencergoog FYI since you're currently working on unifying the ofs and maybeOfs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it makes sense for size to be something where you can depend on just the width or just the height changing? I think most properties it doesn't make sense to go down another level like that, but size is something that can have huge redraw implications, and if we can avoid rebuilding a widget when the height changes and the widget only cares about width (like in a horizontal widget during keyboard animation), that could be good.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a discussion about this above and we decided against exposing sub-properties for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could possible have like 18 more methods including topPaddingOf, leftViewInsetsOf... I see the value but let's hold on that for now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, OK. I missed that discussion.

@flutter-dashboard
Copy link

Golden file changes have been found for this pull request. Click here to view and triage (e.g. because this is an intentional change).

If you are still iterating on this change and are not ready to resolve the images on the Flutter Gold dashboard, consider marking this PR as a draft pull request above. You will still be able to view image results on the dashboard, commenting will be silenced, and the check will not try to resolve itself until marked ready for review.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

Changes reported for pull request #97928 at sha 455de13

@flutter-dashboard flutter-dashboard bot added the will affect goldens Changes to golden files label Nov 1, 2022
@goderbauer
Copy link
Member

@moffatman Sorry for the hassle, but could you close this PR and open a new one with the same change? It looks like since this PR is so old our golden testing infra got confused by it and is in a corrupted state. Opening this as a new PR should resolve the issue.

@moffatman moffatman changed the title MediaQuery as InheritedModel [defunct] MediaQuery as InheritedModel Nov 1, 2022
@flutter-dashboard flutter-dashboard bot added f: cupertino flutter/packages/flutter/cupertino repository f: material design flutter/packages/flutter/material repository. c: contributor-productivity Team-specific productivity, code health, technical debt. c: tech-debt Technical debt, code quality, testing, etc. labels Nov 1, 2022
@moffatman moffatman mentioned this pull request Nov 1, 2022
8 tasks
@moffatman moffatman closed this Nov 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: text input Entering text in a text field or keyboard related problems c: contributor-productivity Team-specific productivity, code health, technical debt. c: tech-debt Technical debt, code quality, testing, etc. f: cupertino flutter/packages/flutter/cupertino repository f: material design flutter/packages/flutter/material repository. f: routes Navigator, Router, and related APIs. f: scrolling Viewports, list views, slivers, etc. framework flutter/packages/flutter repository. See also f: labels. will affect goldens Changes to golden files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Should be able to subscribe to changes on properties of MediaQuery instead of all changes