写点什么

重识 Flutter — 探索 Slivers 的奇妙世界(综合实例)

  • 2023-06-30
    浙江
  • 本文字数:3065 字

    阅读完需:约 10 分钟

重识Flutter — 探索Slivers的奇妙世界(综合实例)

前言

在前三篇文章中,从为什么要使用Sliver,再根据使用频率逐个解析Slivers系列的组件。相信您已经入门了Sliver的世界。为了更好的将Slivers相关的组件结合起来使用,本文将通过一个综合的案例来帮助你理解。


源代码:https://www.aliyundrive.com/s/mPCDFwRv4Rm

效果图

话不多说,先上效果图,有图有真相!


页面框架搭建

顶部

SliverAppBar(  //指定状态栏(status bar)的亮度为暗色  systemOverlayStyle:  const SystemUiOverlayStyle(statusBarBrightness: Brightness.dark),  expandedHeight: 275.0,  backgroundColor: Colors.white,  elevation: 0.0,  pinned: true,  stretch: true,  flexibleSpace: FlexibleSpaceBar(    background: Image.asset(      'assets/images/back_image.png',      fit: BoxFit.cover,    ),    stretchModes: const [      StretchMode.blurBackground,      StretchMode.zoomBackground,    ],  ),  leadingWidth: 80.0,  //裁剪为圆角矩形  leading: ClipRRect(    borderRadius: BorderRadius.circular(56.0),    //模糊滤镜    child: BackdropFilter(      filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),      child: Container(        height: 56.0,        width: 56.0,        alignment: Alignment.center,        decoration: BoxDecoration(          shape: BoxShape.circle,          color: Colors.white.withOpacity(0.20),        ),        child: SvgPicture.asset('assets/images/icon/arrow-ios-back-outline.svg'),      ),    ),  ),);
复制代码

底部装饰


bottom: PreferredSize(  preferredSize: const Size.fromHeight(0.0),  child: Container(    height: 32.0,    alignment: Alignment.center,    decoration: const BoxDecoration(      color: Colors.white,      borderRadius: BorderRadius.only(        topLeft: Radius.circular(32.0),        topRight: Radius.circular(32.0),      ),    ),    child: Container(      width: 40.0,      height: 5.0,      decoration: BoxDecoration(        color: kOutlineColor,        borderRadius: BorderRadius.circular(100.0),      ),    ),  ),),
复制代码

使用SliverToBoxAdapter来使用基于Box协议的组件


SliverToBoxAdapter(  child: Padding(    padding: EdgeInsets.symmetric(horizontal: 20),    child: Column(      crossAxisAlignment: CrossAxisAlignment.start,      children: [        Text(          '意大利面拌42号混凝土',          style: Theme.of(context).textTheme.titleMedium,        ),        ...      ],    ),  ),),
复制代码

通过SliverPersistentHeader制作菜品展示区域


class Menu extends SliverPersistentHeaderDelegate {    ...@override  Widget build(      BuildContext context, double shrinkOffset, bool overlapsContent) {    ...    return Container(      child: Column(        crossAxisAlignment: CrossAxisAlignment.start,        children: [          ...          Text(               '菜品展示',               style: Theme.of(context).textTheme.titleMedium,          ),          Expanded(            child: Stack(              children: [                //控制层叠关系                if (percent > uploadlimit) ...[                  card,                  bottomsliverbar                ] else ...[                  bottomsliverbar,                  card                ]              ],            ),          ),        ],      ),    );  }  @override  double get maxExtent => maxExtended;
@override double get minExtent => minExtended;
@override bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) => false;}
复制代码

反转叠加动画通过Stack结合Transform实现

@overrideWidget build(    BuildContext context, double shrinkOffset, bool overlapsContent) {  //shrinkOffset为SliverPersistentHeader滚动偏移量,用于对应图片的偏移程度  final percent = shrinkOffset / 180;  //限制图片偏移的触发范围  final uploadlimit = 13 / 120;  //使用clamp限制范围  final valueback = (1 - percent - 0.77).clamp(0, uploadlimit);  //将percent的值取平方,用于菜品展示图片下方背景块的位置偏移  final fixrotation = pow(percent, 1.5);  //背景  final bottomsliverbar = _CustomBottomSliverBar(      size: size, fixrotation: fixrotation, percent: percent);  //菜品图片  final card = _CoverCard(      valueback: valueback,      size: size,      percent: percent,      uploadlimit: uploadlimit);  return Container(    ...  );}
复制代码

图片变换的布局

使用 Matrix4.identity()..rotateZ(...)实现绕 Z 轴的旋转变换。


Positioned(    top: size.height * 0.005,    left: size.width / 24,    child: Transform(      alignment: Alignment.topRight,      transform: Matrix4.identity()        ..rotateZ(percent > uploadlimit            ? (valueback * angleForCard)            : percent * angleForCard),      child: CoverPhoto(size: size), ))
//CoverPhotoContainer( ... decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), image: DecorationImage( ... fit: BoxFit.cover, )),)
复制代码

图片不规则背景+修复动画

背景块通过CustomPainter进行绘制


@overridevoid paint(Canvas canvas, Size size) {  final paint = Paint();  paint.color = backgroundcolor;  paint.style = PaintingStyle.fill;  paint.strokeWidth = 10;  final path = Path();  path.moveTo(0, size.height);  path.lineTo(size.width, size.height);  path.lineTo(size.width, 0);  path.lineTo(size.width * 0.27, 0);  canvas.drawPath(path, paint);}
复制代码


修复动画通过Positionedleft做出视觉上的一个视差


Positioned(    right: 0,    bottom: 0,    left: -size.width * fixrotation.clamp(0, 0.35),    child: Container(      height: size.height * 0.12,      child: Stack(        fit: StackFit.expand,        children: [          CustomPaint(            painter: CutRectangle(),          )        ],      ),  ))
复制代码

剩余部分


剩余部分都是通过SliverToBoxAdapter来进行实现,具体布局的内容不是本文的重点,就不过多阐述了,详见源代码。

总结

至此,三篇组件分解文章+一篇综合实战文章,我们学习了Sliver的使用和特性,相信您已经进入了Sliver的世界。我所写的也只是它魅力的冰山一角Sliver系列组件是用于创建灵活的滚动界面和复杂布局的关键,那么请继续探索 Sliver 的世界,利用其强大的特性和灵活的组合方式,创建出更加有趣和具有交互性的滚动界面吧~(后续还会有更多的使用教程、源码分解...)

关于我

Hello,我是 Taxze,如果您觉得文章对您有价值,希望您能给我的文章点个❤️,有问题需要联系我的话:我在这里 。如果您觉得文章还差了那么点东西,也请通过关注督促我写出更好的文章~万一哪天我进步了呢?😝

发布于: 刚刚阅读数: 5
用户头像

他日若遂凌云志 敢笑黄巢不丈夫 2020-10-15 加入

曾许少年凌云志,誓做人间第一流. 一起加入Flutter技术交流群532403442 有好多好多滴学习资料喔~ 小T目前主攻Android与Flutter, 通常会搞搞人工智能、SpringBoot 、Mybatiys等.

评论

发布
暂无评论
重识Flutter — 探索Slivers的奇妙世界(综合实例)_flutter_编程的平行世界_InfoQ写作社区