No longer rebuild Routes when MediaQuery updates (#41763)
* No longer rebuild Routes when MediaQuery updates fixes #37878 * Fix tests * PR feedback * Add missing endline
This commit is contained in:
committed by
Michael Goderbauer
parent
e3195c2629
commit
79cbe25b95
@@ -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 {}
|
||||
|
||||
Reference in New Issue
Block a user