Skip to content

View Metrics Overrides - #190375

Closed
victorsanni wants to merge 19 commits into
flutter:masterfrom
victorsanni:view-metrics-overrides
Closed

victorsanni wants to merge 19 commits into
flutter:masterfrom
victorsanni:view-metrics-overrides

Conversation

@victorsanni

@victorsanni victorsanni commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

tracking issue: flutter/devtools#9893
Implements Option 4 in https://flutter.dev/go/view-metrics-overrides:

  • physicalSize and devicePixelRatio overrides on the render level (ViewConfiguration) and MediaQuery level
  • Created data class ViewMetricsOverrides to encapsulate view metrics
  • Added debugViewMetricsOverrides map for per-View overrides
  • Keeping debugBrightnessOverride for now, added TODO to refactor and use debugViewMetricsOverrides in subsequent PR
  • Exposed displayCornerRadii in MediaQueryData.copyWith since it depends on overridable devicePixelRatio
  • Changed 2 callsites which read from FlutterView metrics directly to apply overrides:
    • Accessibility evaluations (e.g MinimumTapTargetEvaluation) read from FlutterView directly which will bypass overrides, this PR makes it read from RenderView which already has overrides applied via ViewConfiguration.
    • Exposed GestureBinding._devicePixelRatioForView so that RendererBinding can override it to apply view metrics overrides. This makes it so gestures aren't using the wrong DPR.
    • For other sites, see Limitations section in ViewMetricsOverrides API docs.

@github-actions github-actions Bot added the framework flutter/packages/flutter repository. See also f: labels. label Jul 31, 2026
@victorsanni victorsanni added the CICD Run CI/CD label Jul 31, 2026
@victorsanni victorsanni added CICD Run CI/CD and removed CICD Run CI/CD labels Aug 3, 2026
@victorsanni
victorsanni marked this pull request as ready for review August 3, 2026 19:23
@github-actions github-actions Bot added a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) f: gestures flutter/packages/flutter/gestures repository. labels Aug 3, 2026
@victorsanni victorsanni added CICD Run CI/CD and removed CICD Run CI/CD labels Aug 3, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a debug-only view metrics override mechanism (ViewMetricsOverride) to allow developer tooling to simulate different device configurations (such as device pixel ratio, physical size, and accessibility settings) per view. It integrates these overrides into RendererBinding, MediaQuery, pointer event conversion, and accessibility evaluations, and registers the ext.flutter.viewMetricsOverride service extension. A syntax error was identified in the newly added test file where .new() is used incorrectly to initialize a map.

return _instance ??= ViewMetricsOverrideTestBinding();
}

final Map<String, ServiceExtensionCallback> extensions = .new();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The syntax .new() is invalid in Dart without a receiver class or constructor prefix. It should be initialized as an empty map literal <String, ServiceExtensionCallback>{} or using Map<String, ServiceExtensionCallback>.new().

Suggested change
final Map<String, ServiceExtensionCallback> extensions = .new();
final Map<String, ServiceExtensionCallback> extensions = <String, ServiceExtensionCallback>{};

@victorsanni victorsanni added CICD Run CI/CD and removed CICD Run CI/CD labels Aug 3, 2026
@victorsanni victorsanni added CICD Run CI/CD and removed CICD Run CI/CD labels Aug 3, 2026
@protected
ViewConfiguration createViewConfigurationFor(RenderView renderView) {
return ViewConfiguration.fromView(renderView.flutterView);
final ui.FlutterView view = renderView.flutterView;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

feels like a weird place to inject the override, if we want to override the viewConfig, should we do it in ViewConfiguration.fromView?

that being said, should we do it in mediaquery level, so we can keep all override in the same place.

dpr -> MediaQueryData.devicePixelRatio

physical size -> MediaQueryData.size

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

we can keep all override in the same place.

There are two things I am worried about if the view is not aware of size and dpr overrides:

  1. Some widgets layout with MediaQuery size, some don't (so get their constraints from view constraints). If a developer overrides the size in devtools, some parts of their layout will change but some won't, leading to a layout state that never really happens in real life. On a real device of a specific size, the view is that size so the layout changes in a predictable way.

  2. AccessibilityEvaluations e.g MinimumTapTargetEvaluation is using view.devicePixelRatio not MediaQueryData.devicePixelRatio. Say a developer overrides dpr in devtools and reruns the evaluation, the new evaluation run will ignore the dpr change since the view's dpr is not changing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

yeah you are correct, it seems it is safer to do override view level directly.

@victorsanni
victorsanni requested a review from chunhtai August 18, 2026 18:48
/// and the overridden value.
@protected
double? devicePixelRatioForView(int viewId) {
return platformDispatcher.view(id: viewId)?.devicePixelRatio;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If we want to do it outside of mediaquery, should we go all the way back to dart:ui? that way, we don't need to have different override across different place like this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agree here, it feels like fighting multiple fires trying to inject the override in MediaQueryData, ViewConfiguration, a11y evaluations etc. With this option overrides won't propagate to direct ui.FlutterView consumers, each will have to apply the overrides itself.

Like you said, it will probably be better to wrap the FlutterView so every consumer gets it automatically (option 2 in the doc). Since we can't override all metrics cleanly only in MediaQueryData, might as well just do it in dart:ui. The doc says it is significant development effort though, I am not sure since there is already precedent of implementing FlutterView in the framework (TestFlutterView in flutter_test) where it didn't look too hard to do.

I will convert this PR to a draft while exploring a dart:ui solution; when that is done I will close this PR as superseded by that one.

..onPlatformBrightnessChanged = handlePlatformBrightnessChanged;
addPersistentFrameCallback(_handlePersistentFrameCallback);
assert(() {
debugViewMetricsOverridesNotifier.addListener(_debugHandleViewMetricsOverridesChanged);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

as part of the pr, we should also attempt to wire up the vm services, just to make sure everything can be hooked up as expected.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

agreed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) CICD Run CI/CD f: gestures flutter/packages/flutter/gestures repository. framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants