我想显示一个来自根窗口小部件(创建MaterialApp的窗口)的对话框,我有一个NavigatorState
实例,但是showDialog需要返回Navigator.of(context)
的上下文。
似乎我需要从路由提供上下文,但是我不能这样做,因为根小部件没有它。
编辑:我找到了一种解决方法:我可以将仅存在于其中的伪路由发送到showDialog,然后在对话框完成时弹出该路由。不漂亮,但是可以。
答案 0 :(得分:8)
我通过使用navigator.overlay.context
解决了该问题。这是示例:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
final navigatorKey = GlobalKey<NavigatorState>();
void show() {
final context = navigatorKey.currentState.overlay.context;
final dialog = AlertDialog(
content: Text('Test'),
);
showDialog(context: context, builder: (x) => dialog);
}
@override
Widget build(BuildContext context) {
return new MaterialApp(
navigatorKey: navigatorKey,
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(),
body: FlatButton(
child: Text('Show alert'),
onPressed: show,
),
),
);
}
}
答案 1 :(得分:3)
tl; dr:如果要从根窗口小部件调用showDialog
,请将代码扩展到另一个窗口小部件(例如StatelessWidget
)中,然后调用{{1 }}。
无论如何,下面我将假设您遇到了这个问题:
showDialog
如前所述,showDialog只能在其祖先具有BuildContext的MaterialApp中被调用。因此,如果您具有这样的结构,则不能直接调用flutter: No MaterialLocalizations found.
flutter: MyApp widgets require MaterialLocalizations to be provided by a Localizations widget ancestor.
flutter: Localizations are used to generate many different messages, labels,and abbreviations which are used by the material library.
:
showDialog
在一个代码示例中,这将导致这样的代码,抛出上面给出的错误:
- MaterialApp
- Scaffold
- Button // call show Dialog here
要解决此错误,您可以创建一个新的import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(),
home: Scaffold(
body: Center(
child: RaisedButton(
child: Text('Show dialog!'),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
child: Text('Dialog.'),
);
});
}),
),
),
);
}
}
,它有自己的Widget
。修改后的结构如下所示:
BuildContext
将代码示例修改为上面给出的结构,将导致下面的代码片段。 - MaterialApp
- Home
- Home // your own (Stateless)Widget
- Button // call show Dialog here
可以称为没有引发错误。
showDialog
答案 2 :(得分:1)
由于showDialog
用于显示材质对话框,因此只能用于显示MaterialApp
小部件内的对话框。它不能用于在其外部显示对话框。
答案 3 :(得分:1)
他们更改了导航器叠加的工作方式。 这对我们来说是可行的解决方案,因为不再被接受。
// If you want to use the context for anything.
final context = navigatorKey.currentState.overlay.context;
// How to insert the dialog into the display queue.
navigatorKey.currentState.overlay.insert(anyDialog);
答案 4 :(得分:0)
如果对其他人有帮助,则将导航器键注入对话框小部件中。
class MyApp extends StatelessWidget {
MyApp({Key key});
final navigatorKey = GlobalKey<NavigatorState>();
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorKey,
onGenerateRoute: Router.generateRoute,
// ...
builder: (context, routeChild) {
return Material(
child: InviteRequestModal(
navigatorKey: navigatorKey,
child: routeChild,
),
);
},
);
}
然后在需要模式的窗口小部件中,您可以如上所述使用它。
class InviteRequestModal extends StatelessWidget {
final Widget child;
final GlobalKey<NavigatorState> navigatorKey;
InviteRequestModal({
Key key,
this.child,
this.navigatorKey,
}) : super(key: key);
void _showInviteRequest(InviteRequest invite) {
final context = navigatorKey.currentState.overlay.context;
showDialog(
context: context,
builder: (_) {
// Your dialog content
return Container();
}
);
}
@override
Widget build(BuildContext context) {
return BlocListener<InviteContactsBloc, InviteContactsState>(
listenWhen: (previous, current) => current is InviteRequestLoaded,
listener: (_, state) {
if (state is InviteRequestLoaded) {
_showInviteRequest(state.invite);
}
},
child: child,
);
}
}
答案 5 :(得分:0)
对于那些想要了解如何在多小部件/路由/文件场景中执行此操作的人,我将其与 InheritedWidget
和 BuildContext
上的扩展一起使用。
main.dart
import 'package:flutter/material.dart';
import 'package:myapp/home_screen.dart';
import 'package:myapp/app_navkey.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final navigatorKey = GlobalKey<NavigatorState>();
@override
Widget build(BuildContext context) {
return AppNavKey(
navigatorKey: navigatorKey,
child: MaterialApp(
navigatorKey: navigatorKey,
theme: ThemeData(),
home: Scaffold(
body: HomeScreen(),
),
),
);
}
}
home_screen.dart
import 'package:myapp/extensions.dart';
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final overlayContext = context.navigationKey().currentState.overlay.context;
return Center(
child: TextButton(
child: Text('Show dialog!'),
onPressed: () {
showDialog(
context: overlayContext, // use app level navigation context overlay
builder: (BuildContext context) {
return Dialog(
child: Text('Dialog.'),
);
});
},
),
);
}
}
app_navkey.dart
import 'package:flutter/widgets.dart';
class AppNavKey extends InheritedWidget {
final Widget child;
final GlobalKey<NavigatorState> navigatorKey;
AppNavKey({
Key key,
@required this.child,
@required this.navigatorKey,
}) : super(key: key, child: child);
static GlobalKey<NavigatorState> of(BuildContext context) {
final ctx = context.dependOnInheritedWidgetOfExactType<AppNavKey>();
if (ctx == null) throw Exception('Could not find ancestor of type AppNavProvider');
return ctx.navigatorKey;
}
@override
bool updateShouldNotify(covariant InheritedWidget oldWidget) => false;
}
extensions.dart
import 'package:flutter/widgets.dart';
import 'package:myapp/app_navkey.dart';
extension SwitchTabContext on BuildContext {
/// Get app level NavigatorState key.
/// ```dart
/// context.navigationKey();
/// ```
GlobalKey<NavigatorState> navigationKey() => AppNavKey.of(this);
}
答案 6 :(得分:0)
如果你已经有一个 context
对象,你可以通过
final rootContext = context.findRootAncestorStateOfType<NavigatorState>().context
并将其传递给 showDialog
或 showModalBottomSheet
上下文参数。