如何做到Android Flutter自适应瀑布流
发表于:2025-11-14 作者:千家信息网编辑
千家信息网最后更新 2025年11月14日,今天就跟大家聊聊有关如何做到Android Flutter自适应瀑布流,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。Flutter自适应瀑布流
千家信息网最后更新 2025年11月14日如何做到Android Flutter自适应瀑布流
今天就跟大家聊聊有关如何做到Android Flutter自适应瀑布流,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
Flutter自适应瀑布流
前言:在电商app经常会看到首页商品推荐的瀑布流,或者类似短视频app首页也是瀑布流,这些都是需要自适应的,才能给用户带来好的体验
话不多说先上效果图:
根据效果图可以分为四步:
图片自适应
自适应标签
上拉刷新和下拉加载
底部的点赞按钮可以去掉或者自己修改样式,我这里使用的like_button库
注:本文使用的库:为啥这么多呢,因为我把图片缓存这样东西都加上了,单纯的瀑布流就用waterfall_flow
waterfall_flow: ^3.0.1extended_image: anyextended_sliver: anyff_annotation_route_library: anyhttp_client_helper: anyintl: anylike_button: anyloading_more_list: anypull_to_refresh_notification: anyurl_launcher: any
1.图片自适应:
Widget image = Stack( children:[ ExtendedImage.network( item.imageUrl, shape: BoxShape.rectangle, //clearMemoryCacheWhenDispose: true, border: Border.all(color: Colors.grey.withOpacity(0.4), width: 1.0), borderRadius: const BorderRadius.all( Radius.circular(10.0), ), loadStateChanged: (ExtendedImageState value) { if (value.extendedImageLoadState == LoadState.loading) { Widget loadingWidget = Container( alignment: Alignment.center, color: Colors.grey.withOpacity(0.8), child: CircularProgressIndicator( strokeWidth: 2.0, valueColor: AlwaysStoppedAnimation (Theme.of(c).primaryColor), ), ); if (!konwSized) { //todo: not work in web loadingWidget = AspectRatio( aspectRatio: 1.0, child: loadingWidget, ); } return loadingWidget; } else if (value.extendedImageLoadState == LoadState.completed) { item.imageRawSize = Size( value.extendedImageInfo.image.width.toDouble(), value.extendedImageInfo.image.height.toDouble()); } return null; }, ), Positioned( top: 5.0, right: 5.0, child: Container( padding: const EdgeInsets.all(3.0), decoration: BoxDecoration( color: Colors.grey.withOpacity(0.6), border: Border.all(color: Colors.grey.withOpacity(0.4), width: 1.0), borderRadius: const BorderRadius.all( Radius.circular(5.0), ), ), child: Text( '${index + 1}', textAlign: TextAlign.center, style: const TextStyle(fontSize: fontSize, color: Colors.white), ), ), ) ],);if (konwSized) { image = AspectRatio( aspectRatio: item.imageSize.width / item.imageSize.height, child: image, ); } else if (item.imageRawSize != null) { image = AspectRatio( aspectRatio: item.imageRawSize.width / item.imageRawSize.height, child: image, ); } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ image, const SizedBox( height: 5.0, ), buildTagsWidget(item), const SizedBox( height: 5.0, ), buildBottomWidget(item), ], );}
2.自适应标签:
Widget buildTagsWidget( TuChongItem item, { int maxNum = 6,}) { const double fontSize = 12.0; return Wrap( runSpacing: 5.0, spacing: 5.0, children: item.tags.take(maxNum).map((String tag) { final Color color = item.tagColors[item.tags.indexOf(tag)]; return Container( padding: const EdgeInsets.all(3.0), decoration: BoxDecoration( color: color, border: Border.all(color: Colors.grey.withOpacity(0.4), width: 1.0), borderRadius: const BorderRadius.all( Radius.circular(5.0), ), ), child: Text( tag, textAlign: TextAlign.start, style: TextStyle( fontSize: fontSize, color: color.computeLuminance() < 0.5 ? Colors.white : Colors.black), ), ); }).toList());} 3.上拉刷新和下拉加载
class PullToRefreshHeader extends StatelessWidget { const PullToRefreshHeader(this.info, this.lastRefreshTime, {this.color}); final PullToRefreshScrollNotificationInfo info; final DateTime lastRefreshTime; final Color color; @override Widget build(BuildContext context) { if (info == null) { return Container(); } String text = ''; if (info.mode == RefreshIndicatorMode.armed) { text = 'Release to refresh'; } else if (info.mode == RefreshIndicatorMode.refresh || info.mode == RefreshIndicatorMode.snap) { text = 'Loading...'; } else if (info.mode == RefreshIndicatorMode.done) { text = 'Refresh completed.'; } else if (info.mode == RefreshIndicatorMode.drag) { text = 'Pull to refresh'; } else if (info.mode == RefreshIndicatorMode.canceled) { text = 'Cancel refresh'; } final TextStyle ts = const TextStyle( color: Colors.grey, ).copyWith(fontSize: 13); final double dragOffset = info?.dragOffset ?? 0.0; final DateTime time = lastRefreshTime ?? DateTime.now(); final double top = -hideHeight + dragOffset; return Container( height: dragOffset, color: color ?? Colors.transparent, //padding: EdgeInsets.only(top: dragOffset / 3), //padding: EdgeInsets.only(bottom: 5.0), child: Stack( children: [ Positioned( left: 0.0, right: 0.0, top: top, child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: Container( alignment: Alignment.centerRight, child: RefreshImage(top), margin: const EdgeInsets.only(right: 12.0), ), ), Column( children: [ Text( text, style: ts, ), Text( 'Last updated:' + DateFormat('yyyy-MM-dd hh:mm').format(time), style: ts.copyWith(fontSize: 12), ) ], ), Expanded( child: Container(), ), ], ), ) ], ), ); }}class RefreshImage extends StatelessWidget { const RefreshImage(this.top); final double top; @override Widget build(BuildContext context) { const double imageSize = 40; return ExtendedImage.asset( Assets.assets_fluttercandies_grey_png, width: imageSize, height: imageSize, afterPaintImage: (Canvas canvas, Rect rect, ui.Image image, Paint paint) { final double imageHeight = image.height.toDouble(); final double imageWidth = image.width.toDouble(); final Size size = rect.size; final double y = (1 - min(top / (refreshHeight - hideHeight), 1)) * imageHeight; canvas.drawImageRect( image, Rect.fromLTWH(0.0, y, imageWidth, imageHeight - y), Rect.fromLTWH(rect.left, rect.top + y / imageHeight * size.height, size.width, (imageHeight - y) / imageHeight * size.height), Paint() ..colorFilter = const ColorFilter.mode(Color(0xFFea5504), BlendMode.srcIn) ..isAntiAlias = false ..filterQuality = FilterQuality.low); //canvas.restore(); }, ); }} 4.底部的点赞按钮
LikeButton( size: 18.0, isLiked: item.isFavorite, likeCount: item.favorites, countBuilder: (int count, bool isLiked, String text) { final ColorSwatch color = isLiked ? Colors.pinkAccent : Colors.grey; Widget result; if (count == 0) { result = Text( 'love', style: TextStyle(color: color, fontSize: fontSize), ); } else { result = Text( count >= 1000 ? (count / 1000.0).toStringAsFixed(1) + 'k' : text, style: TextStyle(color: color, fontSize: fontSize), ); } return result; }, likeCountAnimationType: item.favorites < 1000 ? LikeCountAnimationType.part : LikeCountAnimationType.none, onTap: (bool isLiked) { return onLikeButtonTap(isLiked, item); },) 这样自适应的瀑布流就完成了。
看完上述内容,你们对如何做到Android Flutter自适应瀑布流有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注行业资讯频道,感谢大家的支持。
瀑布
内容
图片
底部
按钮
效果
效果图
标签
首页
下拉
东西
前言
商品
常会
更多
样式
用户
知识
篇文章
缓存
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
纪念册设计软件开发
linux 开启数据库
shell脚本数据库回滚
软件开发 nexus
辽宁企业党性体检软件开发
如何提升网络安全管控
种子搜索器服务器正在连接
数据库文件的定义
网易我的世界最火的生存服务器
北京华润网络技术学院
网络安全护网保障工作
数据库是否有数据冗余
网络安全行情分析
广东学习软件开发排行
数据库系统设计学生信息管理
交互设计在软件开发中的发展前景
杭川全速网络技术有限公司地址
pg数据库怎么删除500万数据
crm软件开发工程师
青岛软件开发职位
哪家软件开发培训班最强
社交网络安全有哪些注意事项
cf网络安全限制登录
金川集团全员网络安全学习
安恒信息科技互联网教育
软件开发竞聘岗位认知
杭州橙子互联网络科技
滴滴被网络安全审查柳青
app是如何去访问服务器的
h3c服务器电源闪烁