首页 > 解决方案 > 在 AlertDialog 小部件顶部显示 SnackBar

问题描述

我有一个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.
  );
}

在此处输入图像描述

标签: flutterdartflutter-layout

解决方案


更新

多亏了艾米,我才意识到点击屏障并不会关闭对话框。此外,由于使用了嵌套的 Scaffold,代码导致显示多个 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"

这是实现:

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!'),
                ),
              ),
            ),
          ),
        ),
      ),
    ),
  ),
);

在此处输入图像描述

旧答案

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

showDialog(
  context: context,
  builder: (BuildContext dialogContext) => Scaffold(
    backgroundColor: Colors.transparent, // Make Scaffold's background transparent
    body: 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!'),
        ),
      ),
    ),
  ),
);

推荐阅读