相亲源码搭建,实现应用页面和详情页面的切换

前言

在相亲源码中,列表的元素和详情的内容是一致的,这个时候利用 Hero 动画切换到详情会感觉无缝过渡,用户体验会更好。

思路

上面的效果是列表和详情共用了头像和头像的背景色。二者的组合是一个 Stack 组件,因此可以使用 Hero 组件完成。然后是 Hero 组件的移动,我们先做了水平移动,再做垂直方向移动,这样过渡体验会更好,这种可以用我们自定义的 RectTween 完成。下面是我们的各个部分的实现过程。

列表元素

列表元素我们定义一个 HeroListItem 类,整个列表元素需要点击进入详情,使用 GestureDetector 包裹。然后使用 Row 组件完成横向布局,而头像部分使用的是 Stack 组件。HeroListItembuild 方法如下:

Widget build(BuildContext context) {
  return GestureDetector(
    child: Container(
      padding: EdgeInsets.fromLTRB(0, 10.0, 10.0, 10.0),
      child: Row(
        children: [
          Hero(
            tag: heroTag,
            createRectTween: (begin, end) {
              return ListToDetailRectTween(
                begin: begin!,
                end: end!,
              );
            },
            child: ListImage(
              assetImageName: assetImageName,
              imageBgColor: imageBgColor,
            ),
          ),
          SizedBox(
            width: 10.0,
          ),
          Expanded(
            child: Text(
              content,
              style: TextStyle(
                color: Colors.black87,
                fontSize: 18.0,
              ),
              maxLines: 2,
            ),
          ),
        ],
      ),
    ),
    onTap: () {
      Navigator.of(context).push(
        MaterialPageRoute(
          fullscreenDialog: true,
          builder: (context) => ListDetail(
            heroTag: heroTag,
            imageBgColor: imageBgColor,
            assetImageName: assetImageName,
          ),
        ),
      );
    },
  );
}

头像这块因为涉及到背景的边框圆弧处理,单独抽出来一个 ListImage 组件。整个头像是一个 Stack 组件,然后底部的背景 Container 右边的圆弧是通过 BoxDecoration 完成的。后面的 OvalImage 是一个圆形图片包装类,其实就是拿 ClipOval 包裹住 Image 组件就可以了,这里就不贴代码了。

Widget build(BuildContext context) {
  return Stack(
    children: [
      Container(
        height: 90.0,
        width: 110,
        decoration: BoxDecoration(
          color: imageBgColor,
          borderRadius: BorderRadius.only(
            topRight: Radius.circular(45.0),
            bottomRight: Radius.circular(45.0),
          ),
        ),
      ),
      Positioned(
        child: OvalImage(
          assetImageName: assetImageName,
          imageSize: 90.0,
        ),
        left: 20.0,
        top: 0.0,
      )
    ],
  );
}

列表这里的关键其实就是使用 Hero 将头像区域包裹,然后使用了 createRectTween 定义了 Hero 飞行路径。

详情页面

详情页面因为跳转过来是个全屏弹窗的方式,因此需要自己完成返回按钮的操作。实际上详情页顶部就是一个 Stack 组件,组件的背景色和图片和列表保持一样,再在 Stack 组件加上关闭按钮和详情标题即可。这里为了保持页面顶部覆盖状态栏,使用的是 CustomScrollView 实现 。顶部的界面我们定义了一个 ListDetailHeader 组件,代码如下所示。

class ListDetailHeader extends StatelessWidget {
  final heroTag;
  final imageBgColor;
  final assetImageName;
  const ListDetailHeader({
    Key? key,
    required this.heroTag,
    required this.imageBgColor,
    required this.assetImageName,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Hero(
      child: Stack(
        children: [
          Container(
            height: 160.0,
            width: double.infinity,
            decoration: BoxDecoration(
              color: imageBgColor,
            ),
          ),
          Positioned(
            child: Material(
              child: IconButton(
                icon: Icon(
                  Icons.close,
                  color: Colors.white,
                ),
                onPressed: () {
                  Navigator.of(context).pop();
                },
              ),
              color: Colors.transparent,
            ),
            left: 10.0,
            top: 50.0,
            height: 40.0,
          ),
          Positioned(
            child: OvalImage(
              assetImageName: assetImageName,
              imageSize: 90.0,
            ),
            right: 20.0,
            top: 50.0,
          ),
          Positioned(
            child: Material(
              color: Colors.transparent,
              child: Text(
                '这是详情',
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 18.0,
                ),
                maxLines: 1,
              ),
            ),
            left: 20,
            top: 100,
          ),
        ],
      ),
      tag: heroTag,
      createRectTween: (begin, end) {
        return ListToDetailRectTween(
          begin: begin!,
          end: end!,
        );
      },
    );
  }
}

这里没什么特别的,但是一开始遇到了一个问题就是发现文本下面会有两条下划线,而且样式也不对。后来百度了一下,发现是因为使用 fullscreenDialog 的时候,实际上是使用的苹果风格的页面,要保持 Material 风格的话,需要使用 Scaffold 或者使用 Material 组件包裹。因此,在 Text 组件上一层加了一个 Material 组件。Material 组件本身的背景色是白色的,为了不影响组件的底色,需要设置它的背景色为transparent

不被 Material 包裹文本

这里的 Hero 组件的 tagcreateRectTween 和列表保持一致即可,实际的 tag 可以使用列表元素的 id 来设置。

总结

本篇介绍了相亲源码列表到详情之间利用 Hero 过渡切换。实际上,如果考虑更好的动画效果,还可以结合 AnimatedWidgetAnimatedBuilder 等配合完成。
声明:本文由云豹科技转发自岛上码农博客,如有侵权请联系作者删除

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值