我正在参加「启航计划」

前语

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

源代码:www.aliyundrive.com/s/mPCDFwRv4…

效果图

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

页面框架建立

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

顶部

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'),
      ),
    ),
  ),
);

底部装修

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

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协议的组件

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

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

经过SliverPersistentHeader制造菜品展示区域

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

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完成

@override
Widget 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),
 ))
//CoverPhoto
Container(
	...
  decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(12),
      image: DecorationImage(
        ...
        fit: BoxFit.cover,
      )),
)

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

布景块经过CustomPainter进行制作

@override
void 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(),
          )
        ],
      ),
  ))

剩下部分

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

剩下部分都是经过SliverToBoxAdapter来进行完成,详细布局的内容不是本文的要点,就不过多论述了,详见源代码。

总结

至此,三篇组件分化文章+一篇归纳实战文章,我们学习了Sliver的运用和特性,信任您现已进入了Sliver的世界。我所写的也只是它魅力的冰山一角Sliver系列组件是用于创建灵敏的翻滚界面和杂乱布局的要害,那么请继续探索Sliver的世界,利用其强壮的特性和灵敏的组合方式,创建出更加有趣和具有交互性的翻滚界面吧~(后续还会有更多的运用教程、源码分化…)

关于我

Hello,我是Taxze,如果您觉得文章对您有价值,期望您能给我的文章点个❤️,有问题需求联系我的话:我在这里,也可以经过的新的私信功能联系到我。如果您觉得文章还差了那么点东西,也请经过关注督促我写出更好的文章~如果哪天我前进了呢?