在 AlertDialog 小部件顶部显示 SnackBar

Jos*_*utz 17 dart flutter flutter-layout

我有一个AlertDialog小部件,当您点击其文本时,它会显示 SnackBar。SnackBar 当前显示AlertDialog在屏障后面的背景中。我希望 Snackbar 显示在透明AlertDialog屏障的顶部。我正在寻求的行为可以在 Flutter 中实现吗?我创建了一个全新的 Flutter 应用程序,仅包含用于说明下面用例的相关代码以及屏幕截图。

Main.dart 要点

@override
Widget build(BuildContext context) {
  WidgetsBinding.instance!.addPostFrameCallback((_) async {
    showDialog(
      context: context,
      builder: (BuildContext dialogContext) => AlertDialog(
        content: GestureDetector(
          onTap: () {
            ScaffoldMessenger.of(dialogContext).showSnackBar(SnackBar(
              content: const Text('snack'),
              duration: const Duration(seconds: 1),
              action: SnackBarAction(
                label: 'ACTION',
                onPressed: () {},
              ),
            ));
          },
          child: Center(
            child: Text('Show SnackBar!'),
          ),
        ),
      ),
    );
  });
  // This method is rerun every time setState is called, for instance as done
  // by the _incrementCounter method above.
  //
  // The Flutter framework has been optimized to make rerunning build methods
  // fast, so that you can just rebuild anything that needs updating rather
  // than having to individually change instances of widgets.
  return Scaffold(
    appBar: AppBar(
      // Here we take the value from the MyHomePage object that was created by
      // the App.build method, and use it to set our appbar title.
      title: Text(widget.title),
    ),
    body: Center(
      // Center is a layout widget. It takes a single child and positions it
      // in the middle of the parent.
      child: Column(
        // Column is also a layout widget. It takes a list of children and
        // arranges them vertically. By default, it sizes itself to fit its
        // children horizontally, and tries to be as tall as its parent.
        //
        // Invoke "debug painting" (press "p" in the console, choose the
        // "Toggle Debug Paint" action from the Flutter Inspector in Android
        // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
        // to see the wireframe for each widget.
        //
        // Column has various properties to control how it sizes itself and
        // how it positions its children. Here we use mainAxisAlignment to
        // center the children vertically; the main axis here is the vertical
        // axis because Columns are vertical (the cross axis would be
        // horizontal).
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text(
            'You have pushed the button this many times:',
          ),
          Text(
            '$_counter',
            style: Theme.of(context).textTheme.headline4,
          ),
        ],
      ),
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: _incrementCounter,
      tooltip: 'Increment',
      child: Icon(Icons.add),
    ), // This trailing comma makes auto-formatting nicer for build methods.
  );
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

Ste*_*wie 31

更新

感谢艾米,我意识到点击障碍物并不会关闭对话框。此外,由于使用嵌套支架,代码导致显示多个 SnackBar。

查看解决所有问题的以下模型:

showDialog
 |
 |
ScaffoldMessenger => "Set a scope to show SnackBars only in the inner Scaffold"
   |
   --- Builder => "Add a Builder widget to access the Scaffold Messenger"
        |
        --- Scaffold => "The inner Scaffold that is needed to show SnackBars"
             |
             --- GestureDetector => "Dismiss the dialog when tapped outside"
                  |
                  --- GestureDetector => "Don't dismiss it when tapped inside"
                       |
                       --- AlertDialog => "Your dialog"
Run Code Online (Sandbox Code Playgroud)

这是实现:

showDialog
 |
 |
ScaffoldMessenger => "Set a scope to show SnackBars only in the inner Scaffold"
   |
   --- Builder => "Add a Builder widget to access the Scaffold Messenger"
        |
        --- Scaffold => "The inner Scaffold that is needed to show SnackBars"
             |
             --- GestureDetector => "Dismiss the dialog when tapped outside"
                  |
                  --- GestureDetector => "Don't dismiss it when tapped inside"
                       |
                       --- AlertDialog => "Your dialog"
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

旧答案

ScaffoldMessenger显示SnackBar在最近的后代中ScaffoldScaffold如果您在之前添加另一个AlertDialog,它将使用它而不是留在对话框后面的根。

showDialog(
  context: context,
  builder: (context) => ScaffoldMessenger(
    child: Builder(
      builder: (context) => Scaffold(
        backgroundColor: Colors.transparent,
        body: GestureDetector(
          behavior: HitTestBehavior.opaque,
          onTap: () => Navigator.of(context).pop(),
          child: GestureDetector(
            onTap: () {},
            child: AlertDialog(
              content: GestureDetector(
                onTap: () {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(
                      content: const Text('snack'),
                      duration: const Duration(seconds: 1),
                      action: SnackBarAction(
                        label: 'ACTION',
                        onPressed: () {},
                      ),
                    ),
                  );
                },
                child: Center(
                  child: Text('Show SnackBar!'),
                ),
              ),
            ),
          ),
        ),
      ),
    ),
  ),
);
Run Code Online (Sandbox Code Playgroud)

  • 然后你就无法点击对话框来关闭它 (2认同)

Chr*_*ore 8

这里的问题是showDialog使用MaterialApp. 因此,当您显示对话框时,它会完全推到您的脚手架上。为了解决这个问题,您需要一个导航器,该导航器曾经是显示小吃栏的脚手架的子级。所以下面的代码添加了这个导航器,设置useRootNavigatorfalse使用这个导航器,重要的是在新创建的导航器BuildContext 下使用 a :

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Navigator(      //New navigator added here
        initialRoute: '/',
        onGenerateRoute: (setting) {
          return MaterialPageRoute(
            builder: (context) => Center(
              child: Builder(builder: (context) {
                WidgetsBinding.instance!
                  .addPostFrameCallback((_) async {
                    showDialog(
                      context: context,
                      useRootNavigator: false,//Dialog must not use root navigator
                      builder: (BuildContext dialogContext) =>
                      AlertDialog(
                        content: GestureDetector(
                          onTap: () {
                            ScaffoldMessenger.of(dialogContext)
                              .showSnackBar(SnackBar(
                                content: const Text('snack'),
                                duration: const Duration(seconds: 1),
                                action: SnackBarAction(
                                  label: 'ACTION',
                                  onPressed: () {},
                                ),
                              ));
                          },
                          child: Center(
                            child: Text('Show SnackBar!'),
                          ),
                        ),
                      ),
                    );
                  });
                return Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Text(
                      'You have pushed the button this many times:',
                    ),
                    Text(
                      '$_counter',
                      style: Theme.of(context).textTheme.headline4,
                    ),
                  ]);
              }),
            ));
        }),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
Run Code Online (Sandbox Code Playgroud)

结果: 在此输入图像描述


请注意,此解决方案确实限制了对话框的大小,并且应用栏和浮动操作按钮位于内容上方,这可能是不可取的。只需在新创建的导航器下方添加另一个支架并根据需要向下移动这些 appbar/FAB 属性即可解决此问题。AppBar模态下面的示例:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Navigator(
          initialRoute: '/',
          onGenerateRoute: (setting) {
            return MaterialPageRoute(
                builder: (context) => Scaffold(
                    appBar: AppBar(
                      title: Text(widget.title),
                    ),
                    body: Center(
                      child: Builder(builder: (context) {
                        WidgetsBinding.instance!
                            .addPostFrameCallback((_) async {
                          showDialog(
                            context: context,
                            useRootNavigator: false,
                            builder: (BuildContext dialogContext) =>
                                AlertDialog(
                              content: GestureDetector(
                                onTap: () {
                                  ScaffoldMessenger.of(dialogContext)
                                      .showSnackBar(SnackBar(
                                    content: const Text('snack'),
                                    duration: const Duration(seconds: 1),
                                    action: SnackBarAction(
                                      label: 'ACTION',
                                      onPressed: () {},
                                    ),
                                  ));
                                },
                                child: Center(
                                  child: Text('Show SnackBar!'),
                                ),
                              ),
                            ),
                          );
                        });
                        return Column(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: <Widget>[
                              Text(
                                'You have pushed the button this many times:',
                              ),
                              Text(
                                '$_counter',
                                style: Theme.of(context).textTheme.headline4,
                              ),
                            ]);
                      }),
                    )));
          }),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
Run Code Online (Sandbox Code Playgroud)

结果: 在此输入图像描述


MRa*_*iaz 5

使用another_flushbar代替 SnackBar ,它将出现在 AlertDialog 上方。

Flushbar(
    backgroundColor: Colors.red,
    message: S.of(context).choose_date,
    duration: Duration(seconds: Constants.TOAST_DURATION),
  ).show(context);
Run Code Online (Sandbox Code Playgroud)

结果:

冲水杆演示