Flutter for OpenHarmony 骨架屏萌系实战指南:给 App 装上软乎乎的 “加载小面包”✨
Flutter for OpenHarmony 骨架屏萌系实战指南:给 App 装上软乎乎的 “加载小面包”✨
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
一、开篇:给鸿蒙 App 加上会呼吸的加载小面包🍞
哈喽~这次我给 Flutter 鸿蒙 App 装上了一个软乎乎的骨架屏!就像给 App 加了块会呼吸的加载小面包,数据加载的时候,页面会显示和真实内容一模一样的占位,用户再也不会盯着空白屏幕发呆啦~
这次的小项目里,我搞定了三件大事:
设计了和页面内容完美匹配的骨架屏,像给 App 拍了张 “素颜照”
实现了骨架屏的显示和隐藏逻辑,加载时乖乖出现,加载完悄悄退场
加了软乎乎的呼吸动画,加载过程看起来像面包在轻轻膨胀,超治愈~
接下来就和我一起看看,怎么给鸿蒙 App 装上这个会呼吸的加载小面包吧~
二、第一步:给页面拍张 “素颜照”,设计匹配的骨架屏🎨
骨架屏的关键就是和真实页面长得像!我给列表页和详情页分别设计了骨架屏,每个占位元素的大小、位置都和真实组件一模一样,用户一眼就能知道页面大概长什么样~
踩过的小坑:
一开始设计的时候,骨架屏和真实页面的间距、大小对不上,加载完成切换的时候会有 “跳一下” 的感觉,像面包突然变形了一样~后来我把真实组件的尺寸直接复制到骨架屏里,连边距、圆角都一模一样,切换的时候就丝滑多啦!
通用骨架屏组件代码
dartimport'package:flutter/material.dart';// 软乎乎的骨架小方块组件classSkeletonBoxextendsStatelessWidget{finaldouble width;finaldouble height;finaldouble radius;constSkeletonBox({super.key,requiredthis.width,requiredthis.height,this.radius=8,});@overrideWidgetbuild(BuildContextcontext){returnContainer(width:width,height:height,decoration:BoxDecoration(color:Colors.grey[200],borderRadius:BorderRadius.circular(radius),),);}}// 列表页骨架屏(和真实列表项长得一模一样)WidgetbuildListSkeleton(int count){returnListView.builder(itemCount:count,itemBuilder:(context,index){returnPadding(padding:constEdgeInsets.symmetric(horizontal:16,vertical:10),child:Row(children:[constSkeletonBox(width:48,height:48,radius:24),// 头像占位constSizedBox(width:12),Expanded(child:Column(crossAxisAlignment:CrossAxisAlignment.start,children:[constSkeletonBox(width:double.infinity,height:16),// 标题constSizedBox(height:8),constSkeletonBox(width:120,height:12),// 副标题],),),],),);},);}三、第二步:让骨架屏 “懂礼貌”,显示和隐藏逻辑💡
为了让骨架屏更贴心,我给它加了显示和隐藏逻辑~数据加载的时候,它会乖乖出现;加载完成后,会悄悄退场,不会抢内容的风头~
加载状态控制代码
dartclassCuteListPageextendsStatefulWidget{constCuteListPage({super.key});@overrideState<CuteListPage>createState()=>_CuteListPageState();}class_CuteListPageStateextendsState<CuteListPage>{bool _isLoading=true;List<String>_items=[];@overridevoidinitState(){super.initState();// 模拟网络请求,2秒后加载完成Future.delayed(constDuration(seconds:2),(){setState((){_items=List.generate(10,(i)=>'软乎乎的小面包$i');_isLoading=false;});});}@overrideWidgetbuild(BuildContextcontext){returnScaffold(appBar:AppBar(title:constText('软乎乎的列表')),// 用AnimatedSwitcher实现平滑切换body:AnimatedSwitcher(duration:constDuration(milliseconds:300),child:_isLoading?buildListSkeleton(10)// 加载中显示骨架屏:ListView.builder(itemCount:_items.length,itemBuilder:(context,index){returnListTile(leading:constCircleAvatar(child:Icon(Icons.bakery_dining)),title:Text(_items[index]),);},),),);}}四、第三步:给骨架屏加上 “呼吸动画”,软乎乎超治愈🎀
为了让骨架屏更可爱,我给它加了个呼吸动画!用 shimmer 库实现了渐变闪烁效果,就像面包在烤箱里轻轻膨胀,加载过程看起来超治愈,用户的等待焦虑一下子就没啦~
带呼吸动画的骨架屏代码
dartimport'package:shimmer/shimmer.dart';// 给骨架屏加上呼吸动画WidgetbuildShimmerSkeleton(Widgetchild){returnShimmer.fromColors(baseColor:Colors.grey[200]!,highlightColor:Colors.grey[100]!,period:constDuration(milliseconds:1500),// 呼吸周期child:child,);}// 使用方式:// _isLoading ? buildShimmerSkeleton(buildListSkeleton(10)) : ...五、第四步:适配鸿蒙细节,让小面包更乖💡
在鸿蒙真机上测试的时候,我发现了几个需要注意的小细节,调整后骨架屏的表现更稳定啦~
- 性能优化:给骨架屏 “穿件透气小衣服”
一开始在鸿蒙设备上,骨架屏动画偶尔会有轻微卡顿,后来我给骨架屏外层包了个RepaintBoundary,减少了不必要的重绘,动画一下子就丝滑多了!
dartWidgetbuildShimmerSkeleton(Widgetchild){returnRepaintBoundary(child:Shimmer.fromColors(baseColor:Colors.grey[200]!,highlightColor:Colors.grey[100]!,period:constDuration(milliseconds:1500),child:child,),);}- 避免快速加载时的 “闪一下”
有时候网络特别快,数据 100ms 就加载完成了,骨架屏会一闪而过,反而影响体验。我加了个最小显示时间,加载时间小于 300ms 就不显示骨架屏啦~
dart bool _isLoading=true;lateDateTime_loadStartTime;@overridevoidinitState(){super.initState();_loadStartTime=DateTime.now();Future.delayed(constDuration(seconds:2),(){finalloadDuration=DateTime.now().difference(_loadStartTime);if(loadDuration.inMilliseconds<300){// 快速加载,不显示骨架屏setState(()=>_isLoading=false);}else{setState((){_items=List.generate(10,(i)=>'软乎乎的小面包$i');_isLoading=false;});}});}这是我的运行截图:
六、真机体验:小面包在鸿蒙设备上 “呼吸” 啦🎉
我把这个软乎乎的骨架屏装到鸿蒙真机上试了试,效果超棒:
数据加载的时候,骨架屏会乖乖显示,和真实列表长得一模一样
呼吸动画软乎乎的,像面包在轻轻膨胀,一点都不刺眼
加载完成后,骨架屏会平滑切换成真实内容,一点都不突兀
就算网络慢一点,用户也不会觉得无聊,看着小面包 “呼吸” 超治愈~
七、复盘小技巧:让骨架屏在鸿蒙上更乖💡
折腾下来,我也总结了几个让骨架屏变乖的小技巧:
骨架屏的尺寸、间距一定要和真实页面一模一样,切换才不会跳
用 AnimatedSwitcher 做切换动画,加载完成的过渡会更丝滑
呼吸动画的周期别太短,1.5 秒左右最合适,不会闪眼睛
给骨架屏加上 RepaintBoundary,鸿蒙设备上动画更流畅
真机测试比模拟器靠谱,鸿蒙设备上的动画流畅度只有真机才看得出来~
