文章目录
- 1. 效果预览
- 2. 结构分析
- 3. 完整代码
- 4. 总结
1. 效果预览
在 Flutter 应用开发中,瀑布流布局常用于展示图片、商品列表等需要以不规则但整齐排列的内容。同时,下拉刷新和上拉加载更多功能,能够极大提升用户体验,让用户方便地获取最新和更多的数据。预览图如下:
2. 结构分析
- 先安装依赖插件
# 瀑布流布局:https://pub.dev/packages/waterfall_flowwaterfall_flow: ^3.0.3# 上拉加载更多+下拉刷新:https://pub.dev/packages/pull_to_refreshpull_to_refresh: ^2.0.0
- 引入必要的库
import 'dart:async';
import 'package:demo3/constant/imageEnum.dart';
import 'package:flutter/material.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:waterfall_flow/waterfall_flow.dart';
- dart:async:提供了处理异步操作的能力,在我们的代码中用于处理刷新和加载更多时的延迟操作。
- package:demo3/constant/imageEnum.dart:这是一个自己的本地图片Map库,用于引入图片枚举数据,在代码中用于获取瀑布流展示的图片资源路径。
- package:flutter/material.dart:Flutter 的核心 UI 库,提供了构建用户界面所需的各种组件,如Scaffold、SafeArea、Container、Text等。
- package:pull_to_refresh/pull_to_refresh.dart:这个库用于实现下拉刷新和上拉加载更多的功能,是实现页面交互性的关键库。
- package:waterfall_flow/waterfall_flow.dart:专门用于创建瀑布流布局的库,让我们能够轻松实现不规则的列表排列效果。
- 定义ImageWaterfallFlow组件
class ImageWaterfallFlow extends StatefulWidget {const ImageWaterfallFlow({super.key});State<ImageWaterfallFlow> createState() => ImageWaterfallFlowState();
}
这里定义了ImageWaterfallFlow组件,它是一个有状态的组件。有状态组件允许我们在运行时根据用户操作或其他事件改变组件的状态,从而动态更新 UI。createState方法返回了ImageWaterfallFlowState实例,这个实例负责管理组件的状态和构建具体的 UI。
- ImageWaterfallFlowState类的详细解析
class ImageWaterfallFlowState extends State<ImageWaterfallFlow> {/// 字体样式final TextStyle myTxtStyle = const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.w800);/// 模拟数据(初始数据)List imageList = [ImageEnum.banner1,ImageEnum.banner2,ImageEnum.banner3,ImageEnum.model1,ImageEnum.model2,ImageEnum.model3,ImageEnum.model4,ImageEnum.banner1,ImageEnum.banner2,ImageEnum.banner3,ImageEnum.model1,ImageEnum.model2,ImageEnum.model3,ImageEnum.model4];/// 模拟数据(加载更多使用)List moreList = [ImageEnum.banner1, ImageEnum.banner2, ImageEnum.banner3];/// 上拉下拉控制器final RefreshController myRefreshController = RefreshController();
- 字体样式定义:myTxtStyle定义了一种字体样式,包括白色字体颜色、24 的字体大小和粗体字重,用于在瀑布流的图片容器中显示图片序号。
- 模拟数据定义:
imageList用于存储瀑布流初始显示的图片数据。这里通过ImageEnum枚举引入了多个图片资源,组成了一个初始的图片列表。 - moreList是用于上拉加载更多时的数据。当用户触发加载更多操作时,这个列表中的数据会被添加到imageList中。
- 上拉下拉控制器:myRefreshController是一个RefreshController实例,它来自pull_to_refresh库。这个控制器用于控制下拉刷新和上拉加载更多的操作状态,比如完成刷新、完成加载等。
- 刷新和加载更多的方法
/// 刷新
void onRefresh() async {await Future.delayed(const Duration(milliseconds: 1000));myRefreshController.refreshCompleted();
}/// 加载更多
void onLoadMore() async {await Future.delayed(const Duration(milliseconds: 1000));imageList.addAll(moreList);if (mounted) {setState(() {});}myRefreshController.loadComplete();
}
- 刷新方法onRefresh:当用户触发下拉刷新操作时,这个方法会被调用。Future.delayed函数模拟了一个 1 秒的延迟,代表实际应用中可能需要的网络请求或数据处理时间。延迟结束后,调用myRefreshController.refreshCompleted()方法通知pull_to_refresh库刷新操作已经完成,此时页面会恢复到正常状态。
- 加载更多方法onLoadMore:当用户触发上拉加载更多操作时,该方法被执行。同样先通过Future.delayed模拟 1 秒的延迟。然后将moreList中的数据添加到imageList中,更新数据源。if (mounted)条件判断确保组件仍然在 Widget 树中(防止在组件被销毁后尝试更新状态),然后通过setState(() {})方法通知 Flutter 框架状态发生了变化,需要重新构建 UI 以显示新添加的数据。最后调用myRefreshController.loadComplete()方法表示加载更多操作完成。
- 构建 UI 的方法
/// 布局
Widget build(BuildContext context) {return Scaffold(backgroundColor: Colors.black,body: SafeArea(child: SizedBox(width: MediaQuery.of(context).size.width,height: MediaQuery.of(context).size.height,child: listWidget())));
}
在build方法中,首先创建了一个Scaffold组件,设置背景颜色为黑色。Scaffold是 Flutter 应用的基本结构,它提供了一个默认的导航栏、抽屉等布局。接着,使用SafeArea组件确保内容不会被设备的刘海屏或其他安全区域遮挡。在SafeArea内部,通过SizedBox设置了一个与屏幕大小相同的区域,并将listWidget()返回的内容作为其子组件。listWidget()方法负责构建包含瀑布流和刷新、加载更多功能的实际内容。
- 构建瀑布流列表的方法
/// 列表
Widget listWidget() {return SmartRefresher(enablePullDown: true,enablePullUp: true,header: const ClassicHeader(),footer: const ClassicFooter(),controller: myRefreshController,onRefresh: onRefresh,onLoading: onLoadMore,child: WaterfallFlow.builder(physics: const BouncingScrollPhysics(),gridDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(crossAxisCount: 2,crossAxisSpacing: 20,mainAxisSpacing: 20,viewportBuilder: (int index1, int index2) {print('变化:$index1-$index2');},lastChildLayoutTypeBuilder: (index) => index == imageList.length? LastChildLayoutType.fullCrossAxisExtent: LastChildLayoutType.none,),itemCount: imageList.length,itemBuilder: (BuildContext context, int index) {return Container(color: Colors.white,height: (index + 1) % 2 == 0? 100 : 200,child: Container(alignment: Alignment.center,decoration: BoxDecoration(color: Colors.blue.shade300,image: DecorationImage(image: AssetImage(imageList[index]),fit: BoxFit.cover,)),child: Text('第${index + 1}张', style: myTxtStyle)),);},),);
}
- 使用SmartRefresher组件:这是pull_to_refresh库中的核心组件,用于实现下拉刷新和上拉加载更多功能。
- enablePullDown: true和enablePullUp: true分别启用了下拉刷新和上拉加载更多功能。
- header: const ClassicHeader()和footer: const ClassicFooter()分别设置了下拉刷新和上拉加载更多时显示的头部和底部样式,这里使用了pull_to_refresh库提供的经典样式。
- controller: myRefreshController关联了之前定义的刷新控制器。
- onRefresh: onRefresh和onLoading: onLoadMore分别指定了下拉刷新和上拉加载更多时执行的回调函数,即前面定义的onRefresh和onLoadMore方法。
- 使用WaterfallFlow.builder构建瀑布流:
- physics: const BouncingScrollPhysics()设置了瀑布流的滚动物理效果,这里使用了类似于 iOS 的弹性滚动效果。
- gridDelegate属性:
- crossAxisCount: 2指定了瀑布流每行显示两个项目。
- crossAxisSpacing: 20和mainAxisSpacing: 20分别设置了项目在交叉轴(水平方向)和主轴(垂直方向)上的间距。
- viewportBuilder是一个回调函数,只是简单打印了索引变化,但在实际应用中可以用于监控视口内项目的变化情况。
- lastChildLayoutTypeBuilder用于设置最后一个子项的布局类型。当索引等于imageList的长度时,将最后一个子项设置为占据整个交叉轴宽度,否则不进行特殊设置。
- itemCount: imageList.length指定了瀑布流中项目的数量,即imageList的长度。
- itemBuilder属性:这个回调函数用于构建每个瀑布流项目。每个项目是一个Container,外层- Container设置了白色背景,高度根据索引奇偶性分别为 100 或 200。内层Container设置了蓝色背景和图片装饰,图片通过AssetImage从imageList中获取,并使用BoxFit.cover进行适配。同时,在图片上显示了图片的序号,使用了之前定义的myTxtStyle字体样式。
3. 完整代码
import 'dart:async';
import 'package:demo3/constant/imageEnum.dart';
import 'package:flutter/material.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:waterfall_flow/waterfall_flow.dart';/// 瀑布流
class ImageWaterfallFlow extends StatefulWidget {const ImageWaterfallFlow({super.key});State<ImageWaterfallFlow> createState() => ImageWaterfallFlowState();
}/// 瀑布流状态
class ImageWaterfallFlowState extends State<ImageWaterfallFlow> {/// 字体样式final TextStyle myTxtStyle = const TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.w800);/// 模拟数据(初始数据)List imageList = [ImageEnum.banner1,ImageEnum.banner2,ImageEnum.banner3,ImageEnum.model1,ImageEnum.model2,ImageEnum.model3,ImageEnum.model4,ImageEnum.banner1,ImageEnum.banner2,ImageEnum.banner3,ImageEnum.model1,ImageEnum.model2,ImageEnum.model3,ImageEnum.model4];/// 模拟数据(加载更多使用)List moreList = [ImageEnum.banner1, ImageEnum.banner2, ImageEnum.banner3];/// 上拉下拉控制器final RefreshController myRefreshController = RefreshController();/// 刷新void onRefresh() async {await Future.delayed(const Duration(milliseconds: 1000));myRefreshController.refreshCompleted();}/// 加载更多void onLoadMore() async {await Future.delayed(const Duration(milliseconds: 1000));imageList.addAll(moreList);if (mounted) {setState(() {});}myRefreshController.loadComplete();}/// 布局Widget build(BuildContext context) {return Scaffold(backgroundColor: Colors.black,body: SafeArea(child: SizedBox(width: MediaQuery.of(context).size.width,height: MediaQuery.of(context).size.height,child: listWidget())));}/// 列表Widget listWidget() {return SmartRefresher(enablePullDown: true,enablePullUp: true,header: const ClassicHeader(),footer: const ClassicFooter(),controller: myRefreshController,onRefresh: onRefresh,onLoading: onLoadMore,child: WaterfallFlow.builder(physics: const BouncingScrollPhysics(),gridDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(crossAxisCount: 2,crossAxisSpacing: 20,mainAxisSpacing: 20,viewportBuilder: (int index1, int index2) {print('变化:$index1-$index2');},lastChildLayoutTypeBuilder: (index) => index == imageList.length? LastChildLayoutType.fullCrossAxisExtent: LastChildLayoutType.none,),itemCount: imageList.length,itemBuilder: (BuildContext context, int index) {return Container(color: Colors.white,height: (index + 1) % 2 == 0 ? 100 : 200,child: Container(alignment: Alignment.center,decoration: BoxDecoration(color: Colors.blue.shade300,image: DecorationImage(image: AssetImage(imageList[index]),fit: BoxFit.cover,)),child: Text('第${index + 1}张', style: myTxtStyle)),);},),);}
}
4. 总结
通过这段代码,我们成功地在 Flutter 中实现了一个带有瀑布流布局、下拉刷新和上拉加载更多功能的页面。从引入必要的库,到定义组件、管理状态以及构建复杂的 UI 结构,每一步都紧密配合。pull_to_refresh库和waterfall_flow库的使用是实现这些功能的关键,合理地设置各种属性和回调函数,让页面具备了良好的交互性和美观的布局效果。希望这篇文章能帮助你理解并在自己的 Flutter 项目中运用类似的功能。
本次分享就到这儿啦,我是鹏多多,如果您看了觉得有帮助,欢迎评论,关注,点赞,转发,我们下次见~
往期文章
- flutter-使用extended_image操作图片的加载和状态处理以及缓存和下载
- flutter-制作可缩放底部弹出抽屉评论区效果
- flutter-实现Tabs吸顶的PageView效果
- Vue2全家桶+Element搭建的PC端在线音乐网站
- 助你上手Vue3全家桶之Vue3教程
- 助你上手Vue3全家桶之VueX4教程
- 助你上手Vue3全家桶之Vue-Router4教程
- 超详细!Vue的九种通信方式
- 超详细!Vuex手把手教程
- 使用nvm管理node.js版本以及更换npm淘宝镜像源
- vue中利用.env文件存储全局环境变量,以及配置vue启动和打包命令
个人主页
- CSDN
- GitHub
- 简书
- 博客园
- 掘金