flutter 从根窗口小部件显示对话框

jaql4c8m  于 2023-02-25  发布在  Flutter
关注(0)|答案(8)|浏览(167)

我想从根控件(创建MaterialApp的控件)显示对话框。我有一个NavigatorState示例,但showDialog需要返回Navigator.of(context)的上下文。
看起来我需要从路由提供上下文,但是我不能这样做,因为根小部件没有它。
编辑:我找到了一个变通方案:我可以推假路由,这是只有在那里显示对话框,然后弹出该路由时,对话框完成。不漂亮,但工程。

ldxq2e6h

ldxq2e6h1#

我使用navigatorKey.currentState.overlay.context修复了这个问题。下面是一个例子:

class GlobalDialogApp extends StatefulWidget {
  @override
  _GlobalDialogAppState createState() => _GlobalDialogAppState();
}

class _GlobalDialogAppState extends State<GlobalDialogApp> {
  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 MaterialApp(
      navigatorKey: navigatorKey,
      home: Scaffold(
        body: Center(
          child: RaisedButton(
            child: Text('Show alert'),
            onPressed: show,
          ),
        ),
      ),
    );
  }
}
h6my8fg2

h6my8fg22#

**tl;dr:**如果你想从你的根部件调用showDialog,把你的代码挤出到另一个部件(例如StatelessWidget),并在那里调用showDialog

无论如何,在下面我将假设你遇到这个问题:

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只能在其祖先有MaterialAppBuildContext中调用,因此如果您有这样的结构,则不能直接调用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而不会引发错误。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(),
      home: Home()
    );
  }
}

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RaisedButton(
            child: Text('Show dialog!'),
            onPressed: () {
              showDialog(
                  context: context,
                  builder: (BuildContext context) {
                    return Dialog(
                      child: Text('Dialog.'),
                    );
                  });
            }),
      ),
    );
  }
}
xnifntxz

xnifntxz3#

他们改变了导航器覆盖的工作方式。这是我们的工作解决方案,因为公认的一个不再是。

// 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);
ibps3vxo

ibps3vxo4#

如果已经有了context对象,可以通过final rootContext = context.findRootAncestorStateOfType<NavigatorState>().context获取根材质应用的上下文
并将其传递给showDialogshowModalBottomSheet上下文自变量。

cgvd09ve

cgvd09ve5#

由于showDialog用于显示材质对话框,因此它只能用于显示MaterialApp小工具内部的对话框。它不能用于显示小工具外部的对话框。

s4chpxco

s4chpxco6#

如果这对其他人有帮助的话,将navigator键注入到这样的对话框小部件中。

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,
    );
  }
}
ffscu2ro

ffscu2ro7#

答案就是这么简单,当你提供MaterialApp给树时,它正在提供,但在最下面你是在提供MaterialApp给树之前获得的上下文。要解决这个问题,你需要创建一个新的上下文,它将具有MaterialApp属性。为了在home和vola上面 Package 一个Builder,它正在工作...!

@override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Builder(builder: (context) {
        return HomePage(
          child: Center(
            child: TextButton(
              onPressed: () async {
                await showDialog(
                  context: context,
                  builder: (context) => Dialog(
                    child: Container(
                      color: Colors.green,
                      height: 50,
                      width: 100,
                      child: Text("Hi, I am a dialog"),
                    ),
                  ),
                );
              },
              child: Text("Tap me"),
            ),
          ),
        );
      }),
    );
  }
64jmpszr

64jmpszr8#

对于那些希望了解如何在多个小部件/路由/文件场景中执行此操作的人,我将其与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);
}

相关问题