根窗口小部件的showDialog

时间:2018-11-14 06:49:37

标签: flutter

我想显示一个来自根窗口小部件(创建MaterialApp的窗口)的对话框,我有一个NavigatorState实例,但是showDialog需要返回Navigator.of(context)的上下文。

似乎我需要从路由提供上下文,但是我不能这样做,因为根小部件没有它。

编辑:我找到了一种解决方法:我可以将仅存在于其中的伪路由发送到showDialog,然后在对话框完成时弹出该路由。不漂亮,但是可以。

7 个答案:

答案 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只能在其祖先具有BuildContextMaterialApp中被调用。因此,如果您具有这样的结构,则不能直接调用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)

对于那些想要了解如何在多小部件/路由/文件场景中执行此操作的人,我将其与 InheritedWidgetBuildContext 上的扩展一起使用。

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

并将其传递给 showDialogshowModalBottomSheet 上下文参数。