Skip to content

Commit

Permalink
WIP - Reland "Override MediaQuery for WidgetsApp (#81295)" (#85298)
Browse files Browse the repository at this point in the history
  • Loading branch information
atoka93 committed Jul 27, 2021
1 parent f8d2615 commit ea1182e
Show file tree
Hide file tree
Showing 8 changed files with 298 additions and 85 deletions.
7 changes: 7 additions & 0 deletions packages/flutter/lib/src/cupertino/app.dart
Expand Up @@ -170,6 +170,7 @@ class CupertinoApp extends StatefulWidget {
this.actions,
this.restorationScopeId,
this.scrollBehavior,
this.useInheritedMediaQuery = false,
}) : assert(routes != null),
assert(navigatorObservers != null),
assert(title != null),
Expand Down Expand Up @@ -210,6 +211,7 @@ class CupertinoApp extends StatefulWidget {
this.actions,
this.restorationScopeId,
this.scrollBehavior,
this.useInheritedMediaQuery = false,
}) : assert(title != null),
assert(showPerformanceOverlay != null),
assert(checkerboardRasterCacheImages != null),
Expand Down Expand Up @@ -407,6 +409,9 @@ class CupertinoApp extends StatefulWidget {
/// in a subtree.
final ScrollBehavior? scrollBehavior;

/// {@macro flutter.widgets.widgetsApp.useInheritedMediaQuery}
final bool useInheritedMediaQuery;

@override
State<CupertinoApp> createState() => _CupertinoAppState();

Expand Down Expand Up @@ -530,6 +535,7 @@ class _CupertinoAppState extends State<CupertinoApp> {
shortcuts: widget.shortcuts,
actions: widget.actions,
restorationScopeId: widget.restorationScopeId,
useInheritedMediaQuery: widget.useInheritedMediaQuery,
);
}
return WidgetsApp(
Expand Down Expand Up @@ -564,6 +570,7 @@ class _CupertinoAppState extends State<CupertinoApp> {
shortcuts: widget.shortcuts,
actions: widget.actions,
restorationScopeId: widget.restorationScopeId,
useInheritedMediaQuery: widget.useInheritedMediaQuery,
);
}

Expand Down
7 changes: 7 additions & 0 deletions packages/flutter/lib/src/material/app.dart
Expand Up @@ -200,6 +200,7 @@ class MaterialApp extends StatefulWidget {
this.actions,
this.restorationScopeId,
this.scrollBehavior,
this.useInheritedMediaQuery = false,
}) : assert(routes != null),
assert(navigatorObservers != null),
assert(title != null),
Expand Down Expand Up @@ -247,6 +248,7 @@ class MaterialApp extends StatefulWidget {
this.actions,
this.restorationScopeId,
this.scrollBehavior,
this.useInheritedMediaQuery = false,
}) : assert(routeInformationParser != null),
assert(routerDelegate != null),
assert(title != null),
Expand Down Expand Up @@ -665,6 +667,9 @@ class MaterialApp extends StatefulWidget {
/// * <https://material.io/design/layout/spacing-methods.html>
final bool debugShowMaterialGrid;

/// {@macro flutter.widgets.widgetsApp.useInheritedMediaQuery}
final bool useInheritedMediaQuery;

@override
State<MaterialApp> createState() => _MaterialAppState();

Expand Down Expand Up @@ -858,6 +863,7 @@ class _MaterialAppState extends State<MaterialApp> {
shortcuts: widget.shortcuts,
actions: widget.actions,
restorationScopeId: widget.restorationScopeId,
useInheritedMediaQuery: widget.useInheritedMediaQuery,
);
}

Expand Down Expand Up @@ -893,6 +899,7 @@ class _MaterialAppState extends State<MaterialApp> {
shortcuts: widget.shortcuts,
actions: widget.actions,
restorationScopeId: widget.restorationScopeId,
useInheritedMediaQuery: widget.useInheritedMediaQuery,
);
}

Expand Down
114 changes: 29 additions & 85 deletions packages/flutter/lib/src/widgets/app.dart
Expand Up @@ -236,6 +236,9 @@ typedef InitialRouteListFactory = List<Route<dynamic>> Function(String initialRo
/// It is used by both [MaterialApp] and [CupertinoApp] to implement base
/// functionality for an app.
///
/// Builds a [MediaQuery] using [MediaQuery.fromWindow]. To use an inherited
/// [MediaQuery] instead, set [useInheritedMediaQuery] to true.
///
/// Find references to many of the widgets that [WidgetsApp] wraps in the "See
/// also" section.
///
Expand All @@ -247,6 +250,8 @@ typedef InitialRouteListFactory = List<Route<dynamic>> Function(String initialRo
/// without an explicit style.
/// * [MediaQuery], which establishes a subtree in which media queries resolve
/// to a [MediaQueryData].
/// * [MediaQuery.fromWindow], which builds a [MediaQuery] with data derived
/// from [WidgetsBinding.window].
/// * [Localizations], which defines the [Locale] for its `child`.
/// * [Title], a widget that describes this app in the operating system.
/// * [Navigator], a widget that manages a set of child widgets with a stack
Expand Down Expand Up @@ -327,6 +332,7 @@ class WidgetsApp extends StatefulWidget {
this.shortcuts,
this.actions,
this.restorationScopeId,
this.useInheritedMediaQuery = false,
}) : assert(navigatorObservers != null),
assert(routes != null),
assert(
Expand Down Expand Up @@ -423,6 +429,7 @@ class WidgetsApp extends StatefulWidget {
this.shortcuts,
this.actions,
this.restorationScopeId,
this.useInheritedMediaQuery = false,
}) : assert(
routeInformationParser != null &&
routerDelegate != null,
Expand Down Expand Up @@ -1111,6 +1118,14 @@ class WidgetsApp extends StatefulWidget {
/// {@endtemplate}
final String? restorationScopeId;

/// {@template flutter.widgets.widgetsApp.useInheritedMediaQuery}
/// If true, an inherited MediaQuery will be used. If one is not available,
/// or this is false, one will be built from the window.
///
/// Cannot be null, defaults to false.
/// {@endtemplate}
final bool useInheritedMediaQuery;

/// If true, forces the performance overlay to be visible in all instances.
///
/// Used by the `showPerformanceOverlay` observatory extension.
Expand Down Expand Up @@ -1635,6 +1650,19 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {

assert(_debugCheckLocalizations(appLocale));

Widget child = Localizations(
locale: appLocale,
delegates: _localizationsDelegates.toList(),
child: title,
);

final MediaQueryData? data = MediaQuery.maybeOf(context);
if (!widget.useInheritedMediaQuery || data == null) {
child = MediaQuery.fromWindow(
child: child,
);
}

return RootRestorationScope(
restorationId: widget.restorationScopeId,
child: Shortcuts(
Expand All @@ -1648,13 +1676,7 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
child: DefaultTextEditingActions(
child: FocusTraversalGroup(
policy: ReadingOrderTraversalPolicy(),
child: _MediaQueryFromWindow(
child: Localizations(
locale: appLocale,
delegates: _localizationsDelegates.toList(),
child: title,
),
),
child: child,
),
),
),
Expand All @@ -1663,81 +1685,3 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
);
}
}

/// Builds [MediaQuery] from `window` by listening to [WidgetsBinding].
///
/// It is performed in a standalone widget to rebuild **only** [MediaQuery] and
/// its dependents when `window` changes, instead of rebuilding the entire widget tree.
class _MediaQueryFromWindow extends StatefulWidget {
const _MediaQueryFromWindow({Key? key, required this.child}) : super(key: key);

final Widget child;

@override
_MediaQueryFromWindowsState createState() => _MediaQueryFromWindowsState();
}

class _MediaQueryFromWindowsState extends State<_MediaQueryFromWindow> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance!.addObserver(this);
}

// ACCESSIBILITY

@override
void didChangeAccessibilityFeatures() {
setState(() {
// The properties of window have changed. We use them in our build
// function, so we need setState(), but we don't cache anything locally.
});
}

// METRICS

@override
void didChangeMetrics() {
setState(() {
// The properties of window have changed. We use them in our build
// function, so we need setState(), but we don't cache anything locally.
});
}

@override
void didChangeTextScaleFactor() {
setState(() {
// The textScaleFactor property of window has changed. We reference
// window in our build function, so we need to call setState(), but
// we don't need to cache anything locally.
});
}

// RENDERING
@override
void didChangePlatformBrightness() {
setState(() {
// The platformBrightness property of window has changed. We reference
// window in our build function, so we need to call setState(), but
// we don't need to cache anything locally.
});
}

@override
Widget build(BuildContext context) {
MediaQueryData data = MediaQueryData.fromWindow(WidgetsBinding.instance!.window);
if (!kReleaseMode) {
data = data.copyWith(platformBrightness: debugBrightnessOverride);
}
return MediaQuery(
data: data,
child: widget.child,
);
}

@override
void dispose() {
WidgetsBinding.instance!.removeObserver(this);
super.dispose();
}
}
120 changes: 120 additions & 0 deletions packages/flutter/lib/src/widgets/media_query.dart
Expand Up @@ -9,6 +9,7 @@ import 'dart:ui' show Brightness;
import 'package:flutter/foundation.dart';

import 'basic.dart';
import 'binding.dart';
import 'debug.dart';
import 'framework.dart';

Expand Down Expand Up @@ -787,6 +788,28 @@ class MediaQuery extends InheritedWidget {
);
}

/// Provides a [MediaQuery] which is built and updated using the latest
/// [WidgetsBinding.window] values.
///
/// The [MediaQuery] is wrapped in a separate widget to ensure that only it
/// and its dependents are updated when `window` changes, instead of
/// rebuilding the whole widget tree.
///
/// This should be inserted into the widget tree when the [MediaQuery] view
/// padding is consumed by a widget in such a way that the view padding is no
/// longer exposed to the widget's descendants or siblings.
///
/// The [child] argument is required and must not be null.
static Widget fromWindow({
Key? key,
required Widget child,
}) {
return _MediaQueryFromWindow(
key: key,
child: child,
);
}

/// Contains information about the current media.
///
/// For example, the [MediaQueryData.size] property contains the width and
Expand Down Expand Up @@ -922,3 +945,100 @@ enum NavigationMode {
/// focus (although they remain disabled) when traversed.
directional,
}

/// Provides a [MediaQuery] which is built and updated using the latest
/// [WidgetsBinding.window] values.
///
/// Receives `window` updates by listening to [WidgetsBinding].
///
/// The standalone widget ensures that it rebuilds **only** [MediaQuery] and
/// its dependents when `window` changes, instead of rebuilding the entire
/// widget tree.
///
/// It is used by [WidgetsApp] if no other [MediaQuery] is available above it.
///
/// See also:
///
/// * [MediaQuery], which establishes a subtree in which media queries resolve
/// to a [MediaQueryData].
class _MediaQueryFromWindow extends StatefulWidget {
/// Creates a [_MediaQueryFromWindow] that provides a [MediaQuery] to its
/// descendants using the `window` to keep [MediaQueryData] up to date.
///
/// The [child] must not be null.
const _MediaQueryFromWindow({
Key? key,
required this.child,
}) : super(key: key);

/// {@macro flutter.widgets.ProxyWidget.child}
final Widget child;

@override
State<_MediaQueryFromWindow> createState() => _MediaQueryFromWindowState();
}

class _MediaQueryFromWindowState extends State<_MediaQueryFromWindow> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance!.addObserver(this);
}

// ACCESSIBILITY

@override
void didChangeAccessibilityFeatures() {
setState(() {
// The properties of window have changed. We use them in our build
// function, so we need setState(), but we don't cache anything locally.
});
}

// METRICS

@override
void didChangeMetrics() {
setState(() {
// The properties of window have changed. We use them in our build
// function, so we need setState(), but we don't cache anything locally.
});
}

@override
void didChangeTextScaleFactor() {
setState(() {
// The textScaleFactor property of window has changed. We reference
// window in our build function, so we need to call setState(), but
// we don't need to cache anything locally.
});
}

// RENDERING
@override
void didChangePlatformBrightness() {
setState(() {
// The platformBrightness property of window has changed. We reference
// window in our build function, so we need to call setState(), but
// we don't need to cache anything locally.
});
}

@override
Widget build(BuildContext context) {
MediaQueryData data = MediaQueryData.fromWindow(WidgetsBinding.instance!.window);
if (!kReleaseMode) {
data = data.copyWith(platformBrightness: debugBrightnessOverride);
}
return MediaQuery(
data: data,
child: widget.child,
);
}

@override
void dispose() {
WidgetsBinding.instance!.removeObserver(this);
super.dispose();
}
}

0 comments on commit ea1182e

Please sign in to comment.