关键词:
Flutter自适应瀑布流
前言:在电商app经常会看到首页商品推荐的瀑布流,或者类似短视频app首页也是瀑布流,这些都是需要自适应的,才能给用户带来好的体验
(具体代码请联系我,当天会回复)
话不多说先上效果图:
根据效果图可以分为四步:
1.图片自适应
2.自适应标签
3.上拉刷新和下拉加载
4.底部的点赞按钮可以去掉或者自己修改样式,我这里使用的like_button库
注:本文使用的库:为啥这么多呢,因为我把图片缓存这样东西都加上了,单纯的瀑布流就用waterfall_flow
waterfall_flow: ^3.0.1
extended_image: any
extended_sliver: any
ff_annotation_route_library: any
http_client_helper: any
intl: any
like_button: any
loading_more_list: any
pull_to_refresh_notification: any
url_launcher: any
1.图片自适应:
Widget image = Stack(
children: <Widget>[
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<Color>(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: <Widget>[
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<Widget>((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: <Widget>[
Positioned(
left: 0.0,
right: 0.0,
top: top,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: Container(
alignment: Alignment.centerRight,
child: RefreshImage(top),
margin: const EdgeInsets.only(right: 12.0),
),
),
Column(
children: <Widget>[
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<int> 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);
,
)
这样自适应的瀑布流就完成了。
欢迎留言纠正 ~ 不妨给个点赞哈哈
我是阿T一个幽默的程序员 我们下期再见~
添加我为你的好友,领取源码以及Flutter学习资料~
加入我们吧,一起学习,一起进步~
flutter最酷炫瀑布流实现(代码片段)
...置这样才能实现并展示瀑布流的效果在pubspec.yaml文件添加flutter_staggered_grid_view:^0.4.1flutter_screenutil:^5. 查看详情
javascript自适应宽度的瀑布流实现思路
这里主要介绍瀑布流的一种实现方法:绝对定位(css)+javascript+ajax+json。简单一点如果不做滚动加载的话就是绝对定位(css)+javascript了,ajax和json是滚动加载更多内容的时候用到的,感兴趣的你可以参考下哦 这样的布局并不陌生... 查看详情
详细分享uicollectionview的自定义布局(瀑布流,线性,圆形...)(代码片段)
前言:本篇文章不是分享collectionView的详细使用教程,而是属于比较‘高级‘的collectionView使用技巧,阅读之前,我想你已经很熟悉collectionView的基本使用,如果不是很熟悉,建议在以后熟悉一下.那么在本篇结束后,你也能够很轻松的使用c... 查看详情
swift自定义布局实现瀑布流视图(代码片段)
本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索HelloWorld杰少即可关注。自打Apple在iOS6中引入UICollectionView这个控件之后,越来越多的iOS开发者选择将它作为构建UI的首选,如此吸引人的原因在... 查看详情
html瀑布流(代码片段)
android自定义view手写瀑布流组件flowlayout(代码片段)
...和系统API的奥秘、真谛。进入主题,今天来手写一个瀑布流组件FlowLayout,温习下自定义view的流程和关键点,先来张效果图 FlowLayout实现关键步骤:1、创建一个view继承自ViewGroupclassZSFlowLayout:... 查看详情
浅谈瀑布流(代码片段)
瀑布流又称瀑布流式布局,是比较流行的一种网站页面布局方式。视觉表现为参差不齐的多栏布局,最早采用此布局的是网站是Pinterest,后逐渐在国内流行。瀑布流布局效果即多行等宽元素排列,后面的元素依次添加到其后,等... 查看详情
瀑布流(代码片段)
constdefaultConfig=boxName:‘#waterfall‘,itemName:‘.waterfall__item‘,gap:10,boxLeft:15;module.exports=classWaterfallconstructor(userConfig)const_this=this;this.config=Object.assign(,defaultConfig 查看详情
text瀑布流列#css(代码片段)
瀑布流画廊(代码片段)
importReactfrom‘react‘importCard,Row,Col,Modalfrom‘antd‘exportdefaultclassGalleryextendsReact.Componentstate=visible:falseopenGallery=(imgSrc)=>this.setState(visible:true,currentImg:‘/galler 查看详情
flex布局实现瀑布流排版(代码片段)
网上有很多有关js(jq)实现瀑布流和有关瀑布流的插件很多,例如:插件(Masonry,Wookmark等等)。按照正常的逻辑思维,瀑布流的排版(item列表)一般都是由左到右,上而下排序的结果,单纯的css实现这一点有些困难,下面分... 查看详情
前端jquery实现瀑布流(代码片段)
瀑布流是我们经常会见到的东西,一直刷新一直有,其实它实现起来是很简单的。具体代码如下1、css代码 <style>*margin:0;padding:0;.containerposition:relative;.container.boxfloat:left;padding:15px0015px;.container.box.picborder:1pxsolid 查看详情
flutterintrinsicheight自适应组件大小(代码片段)
1简说IntrinsicHeight是Flutter中的一个WidgetIntrinsicHeight是一种根据子Widget的固有高度来调整子Widget尺寸的小部件。2Row中的两个子Widget无限填充classInstrinsicPage01extendsStatelessWidget@overrideWidgetbuild(BuildContextcontext)returnSc 查看详情
flutterintrinsicheight自适应组件大小(代码片段)
1简说IntrinsicHeight是Flutter中的一个WidgetIntrinsicHeight是一种根据子Widget的固有高度来调整子Widget尺寸的小部件。2Row中的两个子Widget无限填充classInstrinsicPage01extendsStatelessWidget@overrideWidgetbuild(BuildContextcontext)returnSc 查看详情
flutterintrinsicheight自适应组件大小(代码片段)
1简说IntrinsicHeight是Flutter中的一个WidgetIntrinsicHeight是一种根据子Widget的固有高度来调整子Widget尺寸的小部件。2Row中的两个子Widget无限填充classInstrinsicPage01extendsStatelessWidget@overrideWidgetbuild(BuildContextcontext)returnSc 查看详情
瀑布流(代码片段)
<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.01//EN""http://www.w3.org/TR/html4/strict.dtd"><htmllang="en"><head><metahttp-equiv="Content-Type"content="text/html;charset=UTF-8"><tit 查看详情
flutter流式布局自动换行(wrapflow)(代码片段)
...烦的,甚至需要自定义view或者使用第三方的库。而在Flutter中,官方为我们提供了流式布局的控件,我们可以很方便的实现流式布局。Wrap首先来看Wrap,Wrap是一个可以使子控件自动换行的控件 查看详情
androidui之recyclerview瀑布流(代码片段)
基础用法添加依赖 //RecyclerAdapterimplementation'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30'//glideimplementation'com.github.bumptech.glide:glide:4.8.0';效果图布局文件activity_fruit.xml& 查看详情