如何做到Android Flutter自适应瀑布流
发表于:2025-01-21 作者:千家信息网编辑
千家信息网最后更新 2025年01月21日,今天就跟大家聊聊有关如何做到Android Flutter自适应瀑布流,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。Flutter自适应瀑布流
千家信息网最后更新 2025年01月21日如何做到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 ColorSwatchcolor = 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安全错误
数据库的锁怎样保障安全
域服务器管理软件使用
我的世界服务器收费吗
网络安全等级三级保护
苏州服务器pdu价格
软件开发行业缴纳多少税点
网络安全专业大学排名及学科
苏州软件开发一个多少钱
谷歌服务器的云盘怎么用
网页版微信的数据库
狭义的数据库模式仅仅指
棱镜多元网络技术有限公司
数据库I是什么意思
华三有网络安全设备么
老人为什么连接服务器失败
不可替代的app软件开发
用漫画讲解网络技术
工业网络技术基础课件
云开发可以有多个数据库吗
access拆分数据库
方舟服务器1
淘宝买数据库代做
社保中心网络安全应急预案
数据库技术运用在哪些方面
宝山区什么是网络技术分类
mc开服务器好友不能破坏方块
宏脉连接服务器失败
服务器 专利
搞网络安全考什么证书
dnf正在连接服务器是什么意思
魔兽世界服务器登陆失败