mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Provide a navigatorKey property on MaterialApp and WidgetsApp (#13591)
This lets people poke at navigators without having to get their BuildContext from a build function or State first.
This commit is contained in:
parent
2c0c9ba9e5
commit
e65c882e97
@ -79,6 +79,7 @@ class MaterialApp extends StatefulWidget {
|
||||
/// The boolean arguments, [routes], and [navigatorObservers], must not be null.
|
||||
MaterialApp({ // can't be const because the asserts use methods on Map :-(
|
||||
Key key,
|
||||
this.navigatorKey,
|
||||
this.title: '',
|
||||
this.onGenerateTitle,
|
||||
this.color,
|
||||
@ -128,6 +129,19 @@ class MaterialApp extends StatefulWidget {
|
||||
),
|
||||
super(key: key);
|
||||
|
||||
/// A key to use when building the [Navigator].
|
||||
///
|
||||
/// If a [navigatorKey] is specified, the [Navigator] can be directly
|
||||
/// manipulated without first obtaining it from a [BuildContext] via
|
||||
/// [Navigator.of]: from the [navigatorKey], use the [GlobalKey.currentState]
|
||||
/// getter.
|
||||
///
|
||||
/// If this is changed, a new [Navigator] will be created, losing all the
|
||||
/// application state in the process; in that case, the [navigatorObservers]
|
||||
/// must also be changed, since the previous observers will be attached to the
|
||||
/// previous navigator.
|
||||
final GlobalKey<NavigatorState> navigatorKey;
|
||||
|
||||
/// A one-line description used by the device to identify the app for the user.
|
||||
///
|
||||
/// On Android the titles appear above the task manager's app snapshots which are
|
||||
@ -403,6 +417,9 @@ class MaterialApp extends StatefulWidget {
|
||||
final bool debugShowCheckedModeBanner;
|
||||
|
||||
/// The list of observers for the [Navigator] created for this app.
|
||||
///
|
||||
/// This list must be replaced by a list of newly-created observers if the
|
||||
/// [navigatorKey] is changed.
|
||||
final List<NavigatorObserver> navigatorObservers;
|
||||
|
||||
/// Turns on a [GridPaper] overlay that paints a baseline grid
|
||||
@ -453,6 +470,18 @@ class _MaterialAppState extends State<MaterialApp> {
|
||||
_heroController = new HeroController(createRectTween: _createRectTween);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(MaterialApp oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.navigatorKey != oldWidget.navigatorKey) {
|
||||
// If the Navigator changes, we have to create a new observer, because the
|
||||
// old Navigator won't be disposed (and thus won't unregister with its
|
||||
// observers) until after the new one has been created (because the
|
||||
// Navigator has a GlobalKey).
|
||||
_heroController = new HeroController(createRectTween: _createRectTween);
|
||||
}
|
||||
}
|
||||
|
||||
// Combine the Localizations for Material with the ones contributed
|
||||
// by the localizationsDelegates parameter, if any. Only the first delegate
|
||||
// of a particular LocalizationsDelegate.type is loaded so the
|
||||
@ -526,6 +555,7 @@ class _MaterialAppState extends State<MaterialApp> {
|
||||
isMaterialAppTheme: true,
|
||||
child: new WidgetsApp(
|
||||
key: new GlobalObjectKey(this),
|
||||
navigatorKey: widget.navigatorKey,
|
||||
title: widget.title,
|
||||
onGenerateTitle: widget.onGenerateTitle,
|
||||
textStyle: _errorTextStyle,
|
||||
|
||||
@ -67,6 +67,7 @@ class WidgetsApp extends StatefulWidget {
|
||||
/// By default supportedLocales is `[const Locale('en', 'US')]`.
|
||||
WidgetsApp({ // can't be const because the asserts use methods on Iterable :-(
|
||||
Key key,
|
||||
this.navigatorKey,
|
||||
@required this.onGenerateRoute,
|
||||
this.onUnknownRoute,
|
||||
this.title: '',
|
||||
@ -99,6 +100,19 @@ class WidgetsApp extends StatefulWidget {
|
||||
assert(debugShowWidgetInspector != null),
|
||||
super(key: key);
|
||||
|
||||
/// A key to use when building the [Navigator].
|
||||
///
|
||||
/// If a [navigatorKey] is specified, the [Navigator] can be directly
|
||||
/// manipulated without first obtaining it from a [BuildContext] via
|
||||
/// [Navigator.of]: from the [navigatorKey], use the [GlobalKey.currentState]
|
||||
/// getter.
|
||||
///
|
||||
/// If this is changed, a new [Navigator] will be created, losing all the
|
||||
/// application state in the process; in that case, the [navigatorObservers]
|
||||
/// must also be changed, since the previous observers will be attached to the
|
||||
/// previous navigator.
|
||||
final GlobalKey<NavigatorState> navigatorKey;
|
||||
|
||||
/// A one-line description used by the device to identify the app for the user.
|
||||
///
|
||||
/// On Android the titles appear above the task manager's app snapshots which are
|
||||
@ -285,6 +299,9 @@ class WidgetsApp extends StatefulWidget {
|
||||
final bool debugShowCheckedModeBanner;
|
||||
|
||||
/// The list of observers for the [Navigator] created for this app.
|
||||
///
|
||||
/// This list must be replaced by a list of newly-created observers if the
|
||||
/// [navigatorKey] is changed.
|
||||
final List<NavigatorObserver> navigatorObservers;
|
||||
|
||||
/// If true, forces the performance overlay to be visible in all instances.
|
||||
@ -315,7 +332,7 @@ class WidgetsApp extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserver {
|
||||
GlobalObjectKey<NavigatorState> _navigator;
|
||||
GlobalKey<NavigatorState> _navigator;
|
||||
Locale _locale;
|
||||
|
||||
Locale _resolveLocale(Locale newLocale, Iterable<Locale> supportedLocales) {
|
||||
@ -338,11 +355,22 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_navigator = new GlobalObjectKey<NavigatorState>(this);
|
||||
_updateNavigator();
|
||||
_locale = _resolveLocale(ui.window.locale, widget.supportedLocales);
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(WidgetsApp oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.navigatorKey != oldWidget.navigatorKey)
|
||||
_updateNavigator();
|
||||
}
|
||||
|
||||
void _updateNavigator() {
|
||||
_navigator = widget.navigatorKey ?? new GlobalObjectKey<NavigatorState>(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
|
||||
@ -317,4 +317,25 @@ void main() {
|
||||
expect(textScaleFactor, isNotNull);
|
||||
expect(textScaleFactor, equals(1.0));
|
||||
});
|
||||
|
||||
testWidgets('MaterialApp.navigatorKey', (WidgetTester tester) async {
|
||||
final GlobalKey<NavigatorState> key = new GlobalKey<NavigatorState>();
|
||||
await tester.pumpWidget(new MaterialApp(
|
||||
navigatorKey: key,
|
||||
color: const Color(0xFF112233),
|
||||
home: const Placeholder(),
|
||||
));
|
||||
expect(key.currentState, const isInstanceOf<NavigatorState>());
|
||||
await tester.pumpWidget(new MaterialApp(
|
||||
color: const Color(0xFF112233),
|
||||
home: const Placeholder(),
|
||||
));
|
||||
expect(key.currentState, isNull);
|
||||
await tester.pumpWidget(new MaterialApp(
|
||||
navigatorKey: key,
|
||||
color: const Color(0xFF112233),
|
||||
home: const Placeholder(),
|
||||
));
|
||||
expect(key.currentState, const isInstanceOf<NavigatorState>());
|
||||
});
|
||||
}
|
||||
|
||||
36
packages/flutter/test/widgets/app_navigator_key_test.dart
Normal file
36
packages/flutter/test/widgets/app_navigator_key_test.dart
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
final RouteFactory generateRoute = (RouteSettings settings) => new PageRouteBuilder<Null>(
|
||||
settings: settings,
|
||||
pageBuilder: (BuildContext context, Animation<double> animation1, Animation<double> animation2) {
|
||||
return const Placeholder();
|
||||
},
|
||||
);
|
||||
|
||||
void main() {
|
||||
testWidgets('WidgetsApp.navigatorKey', (WidgetTester tester) async {
|
||||
final GlobalKey<NavigatorState> key = new GlobalKey<NavigatorState>();
|
||||
await tester.pumpWidget(new WidgetsApp(
|
||||
navigatorKey: key,
|
||||
color: const Color(0xFF112233),
|
||||
onGenerateRoute: generateRoute,
|
||||
));
|
||||
expect(key.currentState, const isInstanceOf<NavigatorState>());
|
||||
await tester.pumpWidget(new WidgetsApp(
|
||||
color: const Color(0xFF112233),
|
||||
onGenerateRoute: generateRoute,
|
||||
));
|
||||
expect(key.currentState, isNull);
|
||||
await tester.pumpWidget(new WidgetsApp(
|
||||
navigatorKey: key,
|
||||
color: const Color(0xFF112233),
|
||||
onGenerateRoute: generateRoute,
|
||||
));
|
||||
expect(key.currentState, const isInstanceOf<NavigatorState>());
|
||||
});
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user