- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我想实现 Material 设计卡片的点击行为。当我点击它时,它应该会展开全屏并显示其他内容/新页面。如何实现?
https://material.io/design/components/cards.html#behavior
我尝试使用 Navigator.of(context).push() 来显示新页面并使用 Hero 动画将卡片背景移动到新的 Scaffold,但是这似乎不是可行的方法,因为新页面没有显示从卡本身,否则我无法做到。我正在尝试实现与我在上面介绍的 material.io 中相同的行为。请您以某种方式指导我吗?
谢谢
最佳答案
不久前,我尝试复制那个确切的页面/过渡,虽然我没有让它看起来完全像它,但我确实非常接近。请记住,这是快速组合起来的,并没有真正遵循最佳实践或其他任何东西。
重要的部分是英雄小部件,尤其是伴随它们的标签 - 如果它们不匹配,它就不会这样做。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepPurple,
),
body: ListView.builder(
itemBuilder: (context, index) {
return TileItem(num: index);
},
),
),
);
}
}
class TileItem extends StatelessWidget {
final int num;
const TileItem({Key key, this.num}) : super(key: key);
@override
Widget build(BuildContext context) {
return Hero(
tag: "card$num",
child: Card(
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.all(
Radius.circular(8.0),
),
),
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Stack(
children: <Widget>[
Column(
children: <Widget>[
AspectRatio(
aspectRatio: 485.0 / 384.0,
child: Image.network("https://picsum.photos/485/384?image=$num"),
),
Material(
child: ListTile(
title: Text("Item $num"),
subtitle: Text("This is item #$num"),
),
)
],
),
Positioned(
left: 0.0,
top: 0.0,
bottom: 0.0,
right: 0.0,
child: Material(
type: MaterialType.transparency,
child: InkWell(
onTap: () async {
await Future.delayed(Duration(milliseconds: 200));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return new PageItem(num: num);
},
fullscreenDialog: true,
),
);
},
),
),
),
],
),
),
);
}
}
class PageItem extends StatelessWidget {
final int num;
const PageItem({Key key, this.num}) : super(key: key);
@override
Widget build(BuildContext context) {
AppBar appBar = new AppBar(
primary: false,
leading: IconTheme(data: IconThemeData(color: Colors.white), child: CloseButton()),
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black.withOpacity(0.4),
Colors.black.withOpacity(0.1),
],
),
),
),
backgroundColor: Colors.transparent,
);
final MediaQueryData mediaQuery = MediaQuery.of(context);
return Stack(children: <Widget>[
Hero(
tag: "card$num",
child: Material(
child: Column(
children: <Widget>[
AspectRatio(
aspectRatio: 485.0 / 384.0,
child: Image.network("https://picsum.photos/485/384?image=$num"),
),
Material(
child: ListTile(
title: Text("Item $num"),
subtitle: Text("This is item #$num"),
),
),
Expanded(
child: Center(child: Text("Some more content goes here!")),
)
],
),
),
),
Column(
children: <Widget>[
Container(
height: mediaQuery.padding.top,
),
ConstrainedBox(
constraints: BoxConstraints(maxHeight: appBar.preferredSize.height),
child: appBar,
)
],
),
]);
}
}
编辑:作为对评论的回应,我将解释 Hero 的工作原理(或者至少我认为它是如何工作的 =D)。
基本上,当页面之间的转换开始时,执行转换的底层机制(或多或少是导航器的一部分)会在当前页面和新页面中查找任何“英雄”小部件。如果找到英雄,则会为每个页面计算其大小和位置。
随着页面之间的过渡,新页面中的英雄被移动到与旧英雄相同位置的叠加层,然后其大小和位置被动画化为在新页面中的最终大小和位置. (请注意,如果您愿意,可以通过一些工作进行更改 - 参见 this blog 了解更多信息)。
这是 OP 试图实现的目标:
When you tap on a Card, its background color expands and becomes a background color of a Scaffold with an Appbar.
最简单的方法是简单地将脚手架本身放入英雄中。任何其他的东西都会在过渡期间遮挡 AppBar,因为它在进行 hero 过渡时它是在一个叠加层中。请参阅下面的代码。请注意,我在一个类中添加了一个使转换发生得更慢的类,以便您可以看到发生了什么,因此要以正常速度查看它,请更改将 SlowMaterialPageRoute 推回 MaterialPageRoute 的部分。
看起来像这样:
import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepPurple,
),
body: ListView.builder(
itemBuilder: (context, index) {
return TileItem(num: index);
},
),
),
);
}
}
Color colorFromNum(int num) {
var random = Random(num);
var r = random.nextInt(256);
var g = random.nextInt(256);
var b = random.nextInt(256);
return Color.fromARGB(255, r, g, b);
}
class TileItem extends StatelessWidget {
final int num;
const TileItem({Key key, this.num}) : super(key: key);
@override
Widget build(BuildContext context) {
return Hero(
tag: "card$num",
child: Card(
color: colorFromNum(num),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(8.0),
),
),
clipBehavior: Clip.antiAliasWithSaveLayer,
child: Stack(
children: <Widget>[
Column(
children: <Widget>[
AspectRatio(
aspectRatio: 485.0 / 384.0,
child: Image.network("https://picsum.photos/485/384?image=$num"),
),
Material(
type: MaterialType.transparency,
child: ListTile(
title: Text("Item $num"),
subtitle: Text("This is item #$num"),
),
)
],
),
Positioned(
left: 0.0,
top: 0.0,
bottom: 0.0,
right: 0.0,
child: Material(
type: MaterialType.transparency,
child: InkWell(
onTap: () async {
await Future.delayed(Duration(milliseconds: 200));
Navigator.push(
context,
SlowMaterialPageRoute(
builder: (context) {
return new PageItem(num: num);
},
fullscreenDialog: true,
),
);
},
),
),
),
],
),
),
);
}
}
class PageItem extends StatelessWidget {
final int num;
const PageItem({Key key, this.num}) : super(key: key);
@override
Widget build(BuildContext context) {
return Hero(
tag: "card$num",
child: Scaffold(
backgroundColor: colorFromNum(num),
appBar: AppBar(
backgroundColor: Colors.white.withOpacity(0.2),
),
),
);
}
}
class SlowMaterialPageRoute<T> extends MaterialPageRoute<T> {
SlowMaterialPageRoute({
WidgetBuilder builder,
RouteSettings settings,
bool maintainState = true,
bool fullscreenDialog = false,
}) : super(builder: builder, settings: settings, fullscreenDialog: fullscreenDialog);
@override
Duration get transitionDuration => const Duration(seconds: 3);
}
但是,在某些情况下,让整个脚手架进行过渡可能不是最佳选择 - 可能它有很多数据,或者被设计为适合特定数量的空间。在这种情况下,可以选择制作一个你想做的任何英雄转换的版本,它本质上是一个“假”——即有一个有两层的堆栈,一层是英雄,有背景颜色、脚手架等等否则你想在过渡期间显示,顶部的另一层完全遮盖了底层(即具有 100% 不透明度的背景),它还有一个应用栏和你想要的任何其他东西。
可能有比这更好的方法 - 例如,您可以使用 the blog I linked to 中提到的方法单独指定英雄。 .
关于android - 如何在 flutter 中扩展卡片?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53307474/
我需要将文本放在 中在一个 Div 中,在另一个 Div 中,在另一个 Div 中。所以这是它的样子: #document Change PIN
奇怪的事情发生了。 我有一个基本的 html 代码。 html,头部, body 。(因为我收到了一些反对票,这里是完整的代码) 这是我的CSS: html { backgroun
我正在尝试将 Assets 中的一组图像加载到 UICollectionview 中存在的 ImageView 中,但每当我运行应用程序时它都会显示错误。而且也没有显示图像。 我在ViewDidLoa
我需要根据带参数的 perl 脚本的输出更改一些环境变量。在 tcsh 中,我可以使用别名命令来评估 perl 脚本的输出。 tcsh: alias setsdk 'eval `/localhome/
我使用 Windows 身份验证创建了一个新的 Blazor(服务器端)应用程序,并使用 IIS Express 运行它。它将显示一条消息“Hello Domain\User!”来自右上方的以下 Ra
这是我的方法 void login(Event event);我想知道 Kotlin 中应该如何 最佳答案 在 Kotlin 中通配符运算符是 * 。它指示编译器它是未知的,但一旦知道,就不会有其他类
看下面的代码 for story in book if story.title.length < 140 - var story
我正在尝试用 C 语言学习字符串处理。我写了一个程序,它存储了一些音乐轨道,并帮助用户检查他/她想到的歌曲是否存在于存储的轨道中。这是通过要求用户输入一串字符来完成的。然后程序使用 strstr()
我正在学习 sscanf 并遇到如下格式字符串: sscanf("%[^:]:%[^*=]%*[*=]%n",a,b,&c); 我理解 %[^:] 部分意味着扫描直到遇到 ':' 并将其分配给 a。:
def char_check(x,y): if (str(x) in y or x.find(y) > -1) or (str(y) in x or y.find(x) > -1):
我有一种情况,我想将文本文件中的现有行包含到一个新 block 中。 line 1 line 2 line in block line 3 line 4 应该变成 line 1 line 2 line
我有一个新项目,我正在尝试设置 Django 调试工具栏。首先,我尝试了快速设置,它只涉及将 'debug_toolbar' 添加到我的已安装应用程序列表中。有了这个,当我转到我的根 URL 时,调试
在 Matlab 中,如果我有一个函数 f,例如签名是 f(a,b,c),我可以创建一个只有一个变量 b 的函数,它将使用固定的 a=a1 和 c=c1 调用 f: g = @(b) f(a1, b,
我不明白为什么 ForEach 中的元素之间有多余的垂直间距在 VStack 里面在 ScrollView 里面使用 GeometryReader 时渲染自定义水平分隔线。 Scrol
我想知道,是否有关于何时使用 session 和 cookie 的指南或最佳实践? 什么应该和什么不应该存储在其中?谢谢! 最佳答案 这些文档很好地了解了 session cookie 的安全问题以及
我在 scipy/numpy 中有一个 Nx3 矩阵,我想用它制作一个 3 维条形图,其中 X 轴和 Y 轴由矩阵的第一列和第二列的值、高度确定每个条形的 是矩阵中的第三列,条形的数量由 N 确定。
假设我用两种不同的方式初始化信号量 sem_init(&randomsem,0,1) sem_init(&randomsem,0,0) 现在, sem_wait(&randomsem) 在这两种情况下
我怀疑该值如何存储在“WORD”中,因为 PStr 包含实际输出。? 既然Pstr中存储的是小写到大写的字母,那么在printf中如何将其给出为“WORD”。有人可以吗?解释一下? #include
我有一个 3x3 数组: var my_array = [[0,1,2], [3,4,5], [6,7,8]]; 并想获得它的第一个 2
我意识到您可以使用如下方式轻松检查焦点: var hasFocus = true; $(window).blur(function(){ hasFocus = false; }); $(win
我是一名优秀的程序员,十分优秀!