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

简介: 本文将通过一个炫酷的综合案例来帮助你理解Slivers,和我一起继续探索Sliver的世界,利用其强大的特性和灵活的组合方式,创建出更加有趣和具有交互性的滚动界面吧!

前言

在前三篇文章中,从为什么要使用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实现

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

         )

       ],

     ),

 ))

剩余部分

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

总结

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

相关文章
|
存储 Dart 数据库
重识Flutter状态管理 — 探索Flutter中的状态
我遇到过很多没有了解过响应式编程框架的,或者从事后端开发,自己想用Flutter写个app玩玩的朋友,一上来,不管在哪里都用`setState`,我问为啥不用状态管理,大部分都回了一句:啥是状态管理?
|
iOS开发 容器
重识Flutter 在不同的滑动列表场景,请选择合适的Slivers - part2
在Flutter中,碰到复杂的、不同的滑动业务场景,若是选择了一个合适的Slivers组件,那么我认为问题会变得简单!
重识Flutter 在不同的滑动列表场景,请选择合适的Slivers - part2
重识Flutter 用于解决复杂滑动视窗问题的Slivers - part1
在日常的开发工作中,仅仅使用ListView、ListView.builder等这样的滑动组件就能满足大部分的业务需求,但在碰到较为复杂的滑动页面时,我认为Slivers可以帮你更简单的实现。
重识Flutter  用于解决复杂滑动视窗问题的Slivers - part1
|
iOS开发 容器
重识Flutter 非常用Slivers组件速览 - part3
通过前面两篇文章,从为什么要使用Sliver,再根据使用频率逐个解析Slivers系列的组件。本文就来讲讲那些不常用的组件,看完这三篇,相信你会入门Sliver的世界。
重识Flutter 非常用Slivers组件速览 - part3
Flutter Button 实例
Flutter Button 实例
160 0
Flutter Button 实例
|
算法 容器
flutter 约束(constrains )实例研究(下)
flutter 约束(constrains )实例研究(下)
164 0
flutter 约束(constrains )实例研究(下)
|
Web App开发 前端开发 Go
flutter 约束(constrains )实例研究(上)
flutter 约束(constrains )实例研究
129 0
flutter 约束(constrains )实例研究(上)
|
Dart
[Flutter]足够入门的Dart语言系列之面向对象:类的定义详解、成员和实例使用
类表示的是分类,一类问题或事物,它是对具体或现实世界的抽象。比如动物类、犬科动物类、猫科动物类、房子类、数学类,类是具体事物的描述,它不是指具体的某个动物、某栋房子、某个数学题,而是对它们的概括...
339 0
[Flutter]足够入门的Dart语言系列之面向对象:类的定义详解、成员和实例使用
|
Dart 测试技术 API
【Flutter】大型项目里Flutter测试应用实例以及集成测试的深度使用
【Flutter】大型项目里Flutter测试应用实例以及集成测试的深度使用