Flutter 小吃店替代方案还是比将所有内容包装在 Scaffold 中更简单的方法?
Posted
技术标签:
【中文标题】Flutter 小吃店替代方案还是比将所有内容包装在 Scaffold 中更简单的方法?【英文标题】:Flutter snackbar alternative or easier method than wrapping everything in Scaffold? 【发布时间】:2019-08-28 12:07:06 【问题描述】:我正在开发我的第一个 Flutter 应用(在我的 android 手机上进行调试)。我有一个包含行项目的列表。当您长按该行时,它会将内容复制到用户的剪贴板中。这很好用!
但我需要让用户知道内容已被复制。
我尝试按照许多教程来尝试将行包围在构建方法中或在 Scaffold 中,但我无法工作。是否有另一种方法来通知用户(简单地)类似“已复制!”发生了吗?
请注意下面注释掉的Scaffold.of(...
。似乎必须有一种更简单的方法来通知用户,而不是将所有内容包装在 Scaffold 中。 (当我尝试时,它会破坏我的布局)。
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(),
),
)),
)
],
),
),
));
目标是有一个像这样的页面(见图),我有它,它可以工作和滚动......等等,但我不能让它与脚手架一起工作,因此,一直没能使用小吃店。每个“行”(此文件用于)都应在 longPress 上显示一个快餐栏。
【问题讨论】:
【参考方案1】:您可以使用GlobalKey
使其按您想要的方式工作。
由于我无权访问您的数据库内容,这就是我给您的想法的方式。将此代码复制并粘贴到您的课程中,并进行相应的更改。我也认为您的RowRule
课程有问题,您可以复制我给您的完整代码并运行吗?
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\""))),
),
);
,
),
),
],
),
);
【讨论】:
我在这个文件/布局中没有脚手架。我认为这是一种建议的方法,但是如何让 GlobalKey 在这里可以访问?我见过的所有例子都是这样的,其中 Scaffold 与 longPress 在同一个文件中。 @Dave 您可以轻松地将您的小部件包装在Scaffold
中,在您的示例中将GestureDetector
设为Scaffold
的子级。
当我用 Scaffold 包裹 GestureDetector 并将 GestureDetector 设为“body”时,我得到:Another exception was thrown: RenderCustomMultiChildLayoutBox object was given an infinite size during layout.
“_RenderInkFeatures”、“RenderPhysicalModel”、“RenderRepaintBoundary”和“同样的错误”渲染索引语义”。【参考方案2】:
这是一个名为“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);
这将在您的应用程序中显示您希望看到的小吃栏,您还可以获得很多可用的修改,以便您可以根据您的应用程序使其看起来。
【讨论】:
剪贴板有什么用?这甚至与您的答案有关吗? 仅供参考:Flushbar 已停产。 pub.dartlang.org/packages/flushbar【参考方案3】:您需要做几件事,例如使用FlatButton
的onPressed
属性必须允许点击,将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(),
),
),
),
)
],
),
),
),
),
);
【讨论】:
这会引发以下错误:RenderCustomMultiChildLayoutBox object was given an infinite size during layout.
【参考方案4】:
我在 pub 上制作了一个下拉横幅 package,让您可以轻松地通知用户错误或确认成功。随着我继续添加视觉上丰富的功能,这项工作正在进行中。
【讨论】:
【参考方案5】:我不确定您的 build()
方法是否已完成或者您尚未更改它,因为它包含许多多余的小部件。就像没有必要在Container
和Padding
中添加Container
以及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()),
);
【讨论】:
我试过这个,我得到了很多错误,其中一些与我尝试使用脚手架时遇到的相同:“抛出另一个异常:_RenderInkFeatures 对象在布局期间被赋予了无限大小。”和其他一些人。 你能分享一下你使用RowRule
的类吗?【参考方案6】:
我阅读了您的所有答案,这是我的结论:
您需要位于树中小部件上方的ScaffoldState
对象才能显示Snackbar
。您可以按照许多人的建议通过GlobalKey
获得它。如果Scaffold
是在小部件的build
内创建的,则相当简单,但如果它在小部件之外(在您的情况下),那么它就会变得复杂。您需要通过子小部件的构造函数参数传递该密钥,无论您需要它。
Scaffold.of(context)
是一种非常巧妙的方法。就像InheritedWidget 一样,Scaffold.of(BuildContext context)
让您可以访问树上方最近的ScaffoldState
对象。否则,如果您的树非常深,那么获取该实例(通过将其作为构造函数参数传递)可能是一场噩梦。
抱歉,让您失望了,但我认为没有比这更好或更清洁的方法了,如果您想获得不是在该小部件的构建内部构建的ScaffoldState
。您可以在任何以Scaffold
为父级的小部件中调用它。
【讨论】:
Scaffold.of(context)
与 InheritedWidgets 无关
@RémiRousselet 我做了一些搜索,你说得对,Scafoold 不是 IW,但它确实提供了类似的 .of()
静态方法来获取最近的 State 对象。我只是假设它会是IW。无论如何纠正了我的答案。谢谢你启发我!!以上是关于Flutter 小吃店替代方案还是比将所有内容包装在 Scaffold 中更简单的方法?的主要内容,如果未能解决你的问题,请参考以下文章