如何做到Android Flutter自适应瀑布流
发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,今天就跟大家聊聊有关如何做到Android Flutter自适应瀑布流,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。Flutter自适应瀑布流
千家信息网最后更新 2026年11月03日如何做到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安全错误
数据库的锁怎样保障安全
什么是科技互联网
深圳恒合互联网络科技有限公司
盈实互联网科技
科技互联网技术
万易互联网科技有限公司
无线互联网科技
新浪科技互联网
狮子座互联网科技培训
互联网科技大会案例
江苏南京365互联网科技
黑科技闪耀互联网之光
互联网科技公司有风险吗
坤平互联网科技
民营互联网公司国防科技
允成互联网科技有限公司融资
爱医互联网络科技
江苏互联网智能科技软件
互联网科技快讯热点
上海互联网科技公司ppt
恒生互联网科技趋势
上海卓越科技互联网
互联网公司 科技公司
奇酷互联网络科技公司
茂名市互联网科技产业园
搜狐科技互联网
滨湖区互联网智能科技产品有哪些
云南省自由互联网科技有限公司
互联网营销推广黑科技
昆明中奇联合互联网科技
中国科技和美国互联网