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
中不需要 Container
和 Padding
以及 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,让您可以轻松地通知用户错误或确认成功。这是一项正在进行的工作,因为我会继续添加视觉上丰富的功能。
我正在开发我的第一个 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
中不需要 Container
和 Padding
以及 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,让您可以轻松地通知用户错误或确认成功。这是一项正在进行的工作,因为我会继续添加视觉上丰富的功能。