Flutter snackbar 替代方法或比将所有内容包装在脚手架中更简单的方法?

Flutter snackbar alternative or easier method than wrapping everything in Scaffold?

我正在开发我的第一个 Flutter 应用程序(在我的 Android phone 上调试)。我有一个包含行项目的列表。当您长按该行时,它会将内容复制到用户的剪贴板中。这很好用!

但我需要让用户知道内容已被复制。

我已经尝试按照许多教程尝试让行被构建方法包围或在脚手架内,但我无法进行任何工作。是否有其他方法可以(简单地)通知用户发生了类似 "Copied!" 的事情?

注意下面注释掉的 Scaffold.of(...。似乎必须有一种更简单的方法来通知用户,而不是将所有内容都包装在脚手架中。 (当我尝试时,它破坏了我的布局)。

import 'package:flutter/material.dart';
import 'package:my_app/Theme.dart' as MyTheme;
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/services.dart';

class RowRule extends StatelessWidget {
  final DocumentSnapshot ruleGroup;

  RowRule(this.ruleGroup);

  _buildChildren() {
    var builder = <Widget>[];
    if (!ruleGroup['label'].isEmpty) {
      builder.add(new Text(ruleGroup['label'],
          style: MyTheme.TextStyles.articleContentLabelTextStyle));
    }
    if (!ruleGroup['details'].isEmpty) {
      builder.add(new Text(ruleGroup['details'],
          style: MyTheme.TextStyles.articleContentTextStyle));
    }
    return builder;
  }


  @override
  Widget build(BuildContext context) {
    return new GestureDetector(
        onLongPress: () {
          Clipboard.setData(new ClipboardData(text: ruleGroup['label'] + " "  + ruleGroup['details']));
//          Scaffold.of(context).showSnackBar(SnackBar
//            (content: Text('text copied')));
        },
        child: Container(
          margin: const EdgeInsets.symmetric(vertical: 3.0),
          child: new FlatButton(
            color: Colors.white,
            padding: EdgeInsets.symmetric(horizontal: 0.0),
            child: new Stack(
              children: <Widget>[
                new Container(
                  margin: const EdgeInsets.symmetric(
                    vertical: MyTheme.Dimens.ruleGroupListRowMarginVertical),
                  child: new Container(
                      child: Padding(
                        padding: EdgeInsets.symmetric(horizontal: 32.0, vertical: 8.0),
                        child: new Column(
                          crossAxisAlignment: CrossAxisAlignment.stretch,
                          children: _buildChildren(),
                        ),
                    )),
              )
            ],
          ),
        ),
      ));
    }
  }

我的目标是拥有一个像这样的页面(见图),我有它,它可以工作和滚动......等等,但我无法让它与脚手架一起工作,因此,还没有能够使用小吃店。每个 "Row"(此文件的用途)都应在 longPress 上显示一个快餐栏。

您可以使用 GlobalKey 使其按照您想要的方式工作。

由于我无权访问您的数据库内容,这就是我给您的想法。将此代码复制并粘贴到您的 class 中并进行相应的更改。我也相信你的 RowRule class 有问题,你能复制我给你的完整代码和 运行 吗?

void main() => runApp(MaterialApp(home: HomePage()));

class HomePage extends StatelessWidget {
  final GlobalKey<ScaffoldState> _key = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Color(0xFFFFFFFF).withOpacity(0.9),
      key: _key,
      body: Column(
        children: <Widget>[
          Container(
            color: Color.fromRGBO(52, 56, 245, 1),
            height: 150,
            alignment: Alignment.center,
            child: Container(width: 56, padding: EdgeInsets.only(top: 12), decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.yellow)),
          ),
          Expanded(
            child: ListView.builder(
              padding: EdgeInsets.zero,
              itemCount: 120,
              itemBuilder: (context, index) {
                return Container(
                  color: Colors.white,
                  margin: const EdgeInsets.all(4),
                  child: ListTile(
                    title: Text("Row #$index"),
                    onLongPress: () => _key.currentState
                      ..removeCurrentSnackBar()
                      ..showSnackBar(SnackBar(content: Text("Copied \"Row #$index\""))),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

您需要做几件事,例如使用 onPressed 属性 的 FlatButton 必须允许点击,将您的 GestureDetector 包装在Scaffold。我进一步修改了代码,以便它使用 GlobalKey 让事情变得简单。

这是最终代码(你的方式

class RowRule extends StatelessWidget {
  final GlobalKey<ScaffoldState> globalKey = GlobalKey();
  final DocumentSnapshot ruleGroup;

  RowRule(this.ruleGroup);

  _buildChildren() {
    var builder = <Widget>[];
    if (!ruleGroup['label'].isEmpty) {
      builder.add(new Text(ruleGroup['label'], style: MyTheme.TextStyles.articleContentLabelTextStyle));
    }
    if (!ruleGroup['details'].isEmpty) {
      builder.add(new Text(ruleGroup['details'], style: MyTheme.TextStyles.articleContentTextStyle));
    }
    return builder;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: globalKey,
      body: GestureDetector(
        onLongPress: () {
          Clipboard.setData(new ClipboardData(text: ruleGroup['label'] + " " + ruleGroup['details']));
          globalKey.currentState
            ..removeCurrentSnackBar()
            ..showSnackBar(SnackBar(content: Text('text copied')));
        },
        child: Container(
          margin: const EdgeInsets.symmetric(vertical: 3.0),
          child: new FlatButton(
            onPressed: () => print("Handle button press here"),
            color: Colors.white,
            padding: EdgeInsets.symmetric(horizontal: 0.0),
            child: new Stack(
              children: <Widget>[
                new Container(
                  margin: const EdgeInsets.symmetric(vertical: MyTheme.Dimens.ruleGroupListRowMarginVertical),
                  child: new Container(
                    child: Padding(
                      padding: EdgeInsets.symmetric(horizontal: 32.0, vertical: 8.0),
                      child: new Column(
                        crossAxisAlignment: CrossAxisAlignment.stretch,
                        children: _buildChildren(),
                      ),
                    ),
                  ),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}

我不确定您的 build() 方法是否已完成,或者您尚未更改它,因为它包含许多多余的小部件。就像 Container 中不需要 ContainerPadding 以及 FlatButton 一样,这将使整个屏幕可点击。另外 Column 也不是一个好主意,因为如果您有更多数据,您的屏幕可能会溢出。请改用 ListView

因此,如果您接受我的建议,请使用这个简单的代码,它应该可以为您提供您真正需要的东西。 (参见 build() 方法只有 5 行。

class RowRule extends StatelessWidget {
  final GlobalKey<ScaffoldState> globalKey = GlobalKey();

  final DocumentSnapshot ruleGroup;

  RowRule(this.ruleGroup);

  _buildChildren() {
    var builder = <Widget>[];
    if (!ruleGroup['label'].isEmpty) {
      builder.add(
        ListTile(
          title: Text(ruleGroup['label'], style: MyTheme.TextStyles.articleContentLabelTextStyle),
          onLongPress: () {
            globalKey.currentState
              ..removeCurrentSnackBar()
              ..showSnackBar(SnackBar(content: Text("Clicked")));
          },
        ),
      );
    }
    if (!ruleGroup['details'].isEmpty) {
      builder.add(
        ListTile(
          title: Text(ruleGroup['details'], style: MyTheme.TextStyles.articleContentTextStyle),
          onLongPress: () {
            globalKey.currentState
              ..removeCurrentSnackBar()
              ..showSnackBar(SnackBar(content: Text("Clicked")));
          },
        ),
      );
    }
    return builder;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: globalKey,
      body: ListView(children: _buildChildren()),
    );
  }
}

我阅读了您对所有答案的评论,这是我的结论:

您需要 ScaffoldState 树中小部件上方的对象才能显示 Snackbar。您可以按照许多人的建议通过 GlobalKey 获得它。如果 Scaffold 在小部件的 build 内部创建,则相当简单,但如果它在小部件外部(在您的情况下),那么它会变得复杂。您需要通过子小部件的构造函数参数在任何需要的地方传递该密钥。

Scaffold.of(context) 是一种非常简洁的方法。就像 InheritedWidget, Scaffold.of(BuildContext context) 可以让您访问树上方最近的 ScaffoldState 对象。否则,如果您的树很深,则获取该实例(通过将其作为构造函数参数传递)可能是一场噩梦。

抱歉,让您失望了,但我认为没有比这更好或更干净的方法了,如果您想获得未在该小部件的内部构建的 ScaffoldState。您可以在任何以 Scaffold 作为父级的小部件中调用它。

这些是名为 "Flushbar" 的 Snackbar 的简单插件替代品。 您可以在此处获取插件 - https://pub.dartlang.org/packages/flushbar 您不必考虑将小部件包装到脚手架中,您还可以进行大量修改,例如背景渐变、添加表单等到 Snackbar 等等。 在 GestureDetectore 中的 onLongPressed 中,您可以执行此操作。

onLongPressed:(){
Clipboard.setData(new ClipboardData(text: ruleGroup['label'] + " "  + ruleGroup['details']));
Flushbar(
             message:  "Copied !!",
             duration:  Duration(seconds: 3),              
          )..show(context);
}

这将在您的应用程序中显示您希望看到它的位置,您还可以进行大量修改,以便您可以使其看起来与您的应用程序一致。

我在 pub 上制作了一个下拉横幅 package,让您可以轻松地通知用户错误或确认成功。这是一项正在进行的工作,因为我会继续添加视觉上丰富的功能。