Commit 79cbe25b authored by Remi Rousselet's avatar Remi Rousselet Committed by Michael Goderbauer

No longer rebuild Routes when MediaQuery updates (#41763)

* No longer rebuild Routes when MediaQuery updates

fixes #37878

* Fix tests

* PR feedback

* Add missing endline
parent e3195c26
......@@ -706,8 +706,7 @@ class WidgetsApp extends StatefulWidget {
_WidgetsAppState createState() => _WidgetsAppState();
}
class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserver {
class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver {
// STATE LIFECYCLE
@override
......@@ -731,13 +730,6 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) { }
@override
void didHaveMemoryPressure() { }
// NAVIGATOR
GlobalKey<NavigatorState> _navigator;
......@@ -995,46 +987,6 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
yield DefaultWidgetsLocalizations.delegate;
}
// 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.
});
}
// BUILDER
bool _debugCheckLocalizations(Locale appLocale) {
......@@ -1201,8 +1153,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
},
child: DefaultFocusTraversal(
policy: ReadingOrderTraversalPolicy(),
child: MediaQuery(
data: MediaQueryData.fromWindow(WidgetsBinding.instance.window),
child: _MediaQueryFromWindow(
child: Localizations(
locale: appLocale,
delegates: _localizationsDelegates.toList(),
......@@ -1213,3 +1164,77 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
);
}
}
/// 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, 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) {
return MediaQuery(
data: MediaQueryData.fromWindow(WidgetsBinding.instance.window),
child: widget.child,
);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
}
......@@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/semantics.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mockito/mockito.dart';
class StateMarker extends StatefulWidget {
const StateMarker({ Key key, this.child }) : super(key: key);
......@@ -392,6 +394,67 @@ void main() {
);
});
testWidgets("WidgetsApp don't rebuild routes when MediaQuery updates", (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/37878
int routeBuildCount = 0;
int dependentBuildCount = 0;
await tester.pumpWidget(WidgetsApp(
color: const Color.fromARGB(255, 255, 255, 255),
onGenerateRoute: (_) {
return PageRouteBuilder<void>(pageBuilder: (_, __, ___) {
routeBuildCount++;
return Builder(
builder: (BuildContext context) {
dependentBuildCount++;
MediaQuery.of(context);
return Container();
},
);
});
},
));
expect(routeBuildCount, equals(1));
expect(dependentBuildCount, equals(1));
// didChangeMetrics
tester.binding.window.physicalSizeTestValue = const Size(42, 42);
addTearDown(tester.binding.window.clearPhysicalSizeTestValue);
await tester.pump();
expect(routeBuildCount, equals(1));
expect(dependentBuildCount, equals(2));
// didChangeTextScaleFactor
tester.binding.window.textScaleFactorTestValue = 42;
addTearDown(tester.binding.window.clearTextScaleFactorTestValue);
await tester.pump();
expect(routeBuildCount, equals(1));
expect(dependentBuildCount, equals(3));
// didChangePlatformBrightness
tester.binding.window.platformBrightnessTestValue = Brightness.dark;
addTearDown(tester.binding.window.clearPlatformBrightnessTestValue);
await tester.pump();
expect(routeBuildCount, equals(1));
expect(dependentBuildCount, equals(4));
// didChangeAccessibilityFeatures
tester.binding.window.accessibilityFeaturesTestValue = MockAccessibilityFeature();
addTearDown(tester.binding.window.clearAccessibilityFeaturesTestValue);
await tester.pump();
expect(routeBuildCount, equals(1));
expect(dependentBuildCount, equals(5));
});
testWidgets('Can get text scale from media query', (WidgetTester tester) async {
double textScaleFactor;
await tester.pumpWidget(MaterialApp(
......@@ -726,3 +789,5 @@ void main() {
expect(themeAfterBrightnessChange.brightness, Brightness.dark);
});
}
class MockAccessibilityFeature extends Mock implements AccessibilityFeatures {}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment