flutter-实现瀑布流布局及下拉刷新上拉加载更多

文章目录

  • 1. 效果预览
  • 2. 结构分析
  • 3. 完整代码
  • 4. 总结

1. 效果预览

在 Flutter 应用开发中,瀑布流布局常用于展示图片、商品列表等需要以不规则但整齐排列的内容。同时,下拉刷新和上拉加载更多功能,能够极大提升用户体验,让用户方便地获取最新和更多的数据。预览图如下:

预览

2. 结构分析

  1. 先安装依赖插件
# 瀑布流布局:https://pub.dev/packages/waterfall_flowwaterfall_flow: ^3.0.3# 上拉加载更多+下拉刷新:https://pub.dev/packages/pull_to_refreshpull_to_refresh: ^2.0.0
  1. 引入必要的库
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:专门用于创建瀑布流布局的库,让我们能够轻松实现不规则的列表排列效果。
  1. 定义ImageWaterfallFlow组件
class ImageWaterfallFlow extends StatefulWidget {const ImageWaterfallFlow({super.key});State<ImageWaterfallFlow> createState() => ImageWaterfallFlowState();
}

这里定义了ImageWaterfallFlow组件,它是一个有状态的组件。有状态组件允许我们在运行时根据用户操作或其他事件改变组件的状态,从而动态更新 UI。createState方法返回了ImageWaterfallFlowState实例,这个实例负责管理组件的状态和构建具体的 UI。

  1. 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库。这个控制器用于控制下拉刷新和上拉加载更多的操作状态,比如完成刷新、完成加载等。
  1. 刷新和加载更多的方法
/// 刷新
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()方法表示加载更多操作完成。
  1. 构建 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()方法负责构建包含瀑布流和刷新、加载更多功能的实际内容。

  1. 构建瀑布流列表的方法
/// 列表
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
  • 简书
  • 博客园
  • 掘金

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/73670.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在 Ubuntu 下通过 Docker 部署 Nginx 服务器

1. Docker 和 Nginx 简介以及实验环境 Docker 是一个开源的容器化平台&#xff0c;允许开发者将应用程序及其依赖项打包成一个轻量级的、可移植的容器。通过 Docker&#xff0c;开发者可以在任何支持 Docker 的环境中运行应用&#xff0c;从而实现一致的开发和生产环境。Docke…

IoT平台实时监测机器人状态的实现方案

通过IoT平台实时监测机器人状态的实现方案与可执行路径 一、整体架构设计 #mermaid-svg-6xMlDfFSZM4Wc8tA {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-6xMlDfFSZM4Wc8tA .error-icon{fill:#552222;}#mermaid-sv…

mybatis里in关键字拼接id问题

我们一般会把ids集合用StrUtil.join(‘,’)转成"1,2,3"这种形式 然后放入in中 我们会这么写: select id, nick_name, icon from tb_user where id in (#{ids}) order by FIELD(id, #{ids})结果发现sql执行是这样的: select id, nick_name, icon from tb_user where…

4.用 Excel 录入数据

一 用 Excel 录入数据的两种方式 用鼠标键盘录入数据和从网上爬取数据。 二 用鼠标键盘录入数据 1.录入数据的规范 横着录入数据&#xff08;横着一条条录入数据&#xff09;。 2.使用快捷键进行数据录入 tab 键和 enter 键。 tab 键&#xff1a;向右移动一个单元格。 tab 键…

C++类与对象-3.23笔记

今天学习了类的概述和写类的基本框架 在哔哩哔哩学习的这个老师的C面向对象高级语言程序设计教程&#xff08;118集全&#xff09;讲的很不错&#xff08;真的&#xff01;&#xff01;&#xff01;&#xff09;&#xff0c;C语言也是在这个老师的带领下学习的 #include<io…

Android读写权限分析

Android系统使用的是Linux内核&#xff0c;所以Android系统沿用了linux系统的那一套文件读写权限。 目录 1&#xff0c;权限解读1.1&#xff0c;权限分为三种类型&#xff1a;1.2&#xff0c;权限针对的三类对象&#xff1a;1.3&#xff0c;文件和目录的权限区别1.3.1&#xf…

Python二分查找【清晰易懂】

1. 二分查找是什么&#xff1f; 想象你在玩“猜数字”游戏&#xff1a; 对方心里想一个 1~100 的数字&#xff0c;你每次猜一个数&#xff0c;对方会告诉你是“大了”还是“小了”。 最快的方法&#xff1a;每次都猜中间的数&#xff01;比如第一次猜50&#xff0c;如果大了&…

关于Qt的各类问题

目录 1、问题&#xff1a;Qt中文乱码 2、问题&#xff1a;启动时避免ComBox控件出现默认值 博客会不定期的更新各种Qt开发的Bug与解决方法,敬请关注! 1、问题&#xff1a;Qt中文乱码 问题描述&#xff1a;我在设置标题时出现了中文乱码 this->setWindowTitle("算法…

关于我对接了deepseek之后部署到本地将数据存储到mysql的过程

写在前面 今天写一下使用nodejs作为服务端&#xff0c;vue作为客户端&#xff0c;mysql的数据库&#xff0c;对接deepseek的全过程&#xff0c;要实现一个很简单的效果就是&#xff0c;可以自由的询问&#xff0c;然后可以将询问的过程存储到mysql的数据库中。 文档对接 deeps…

游戏引擎学习第182天

回顾和今天的计划 昨天的进展令人惊喜&#xff0c;原本的调试系统已经被一个新的系统完全替换&#xff0c;新系统不仅能完成原有的所有功能&#xff0c;还能捕获完整的调试信息&#xff0c;包括时间戳等关键数据。这次的替换非常顺利&#xff0c;效果很好。 今天的重点是在此基…

CSS终极指南:从基础到高级实践

目录 一、CSS基础概念与核心语法 1.1 CSS的本质与作用 1.2 CSS语法结构 二、CSS与HTML结合的四种方式 2.1 内联样式&#xff08;Inline Style&#xff09; 2.2 内部样式表&#xff08;Internal Style Sheet&#xff09; 2.3 外部样式表&#xff08;External Style Sheet…

MATLAB 2024b深度学习新特性全面解析与DeepSeek大模型集成开发

MATLAB 2024b深度学习工具箱通过架构创新与功能强化&#xff0c;为科研创新和行业应用提供了全栈式解决方案。 第一&#xff1a;MATLAB 2024b深度学习工具箱新特性 1、MATLAB Deep Learning Toolbox 2、实时脚本&#xff08;Live Script&#xff09;与交互控件&#xff08…

.NET开源的智能体相关项目推荐

一、AntSK 由AIDotNet团队开发的人工智能知识库与智能体框架&#xff0c;支持多模型集成和离线部署能力。 核心能力&#xff1a; • 支持OpenAI、Azure OpenAI、星火、阿里灵积等主流大模型&#xff0c;以及20余种国产数据库&#xff08;如达梦&#xff09; • 内置语义内核&a…

Qt弹出新窗口并关闭(一个按钮)

参考&#xff1a;Qt基础 练习&#xff1a;弹出新窗口并关闭的两种实现方式&#xff08;两个按钮、一个按钮&#xff09;_qt打开一个窗口另一个关闭-CSDN博客 实现&#xff1a; 一个按钮&#xff0c;点击一次&#xff0c;按钮的名字从open window变为close window&#xff0c;…

PHP中yield关键字的使用

PHP版本>5.5 原理&#xff1a;yield关键字会生成一个Generator类的对象&#xff0c;PHP通过Generator实例计算出下一次迭代的值&#xff0c;再次返回一个Generator对象并停止循环&#xff08;即循环一次执行一次&#xff09;。 理解&#xff1a;使用在for/foreach/while循…

SpringBoot集成腾讯云OCR实现身份证识别

OCR身份证识别 官网地址&#xff1a;https://cloud.tencent.com/document/product/866/33524 身份信息认证&#xff08;二要素核验&#xff09; 官网地址&#xff1a;https://cloud.tencent.com/document/product/1007/33188 代码实现 引入依赖 <dependency><…

Tabby 一:如何在Mac配置保姆级教程(本地模型替换hugging face下载)

1. brew安装 mac需要先安装brew&#xff0c;如果本地已经安装过brew这一步可以忽略&#xff0c;遇到问题可以自己ai问 /bin/bash -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" 可能遇到source .zprofile失败&#xff0c;因为…

C++中使用CopyFromRecordset将记录集拷贝到excel中时,如果记录集为0个,函数崩溃,是什么原因

文章目录 原因分析解决方案1. 检查记录集是否为空2. 安全调用COM方法3.进行异常捕获4. 替代方案&#xff1a;手动处理空数据 总结 在C中使用CopyFromRecordset将空记录集&#xff08;0条记录&#xff09;复制到Excel时崩溃的原因及解决方法如下&#xff1a; 原因分析 空记录集…

【算法学习计划】贪心算法(上)

目录 前言&#xff08;什么是贪心&#xff09; leetcode 860.柠檬水找零 leetcode 2208.将数组和减半的最少操作次数 leetcode 179.最大数 leetcode 376.摆动序列 leetcode 300.最长递增子序列 leetcode 334.递增的三元子序列 leetcode 674.最长连续递增序列 leetcode …

PC名词解释-笔记本的S0,S1,S2,S3,S4,S5状态

​&#x1f393;作者简介&#xff1a;程序员转项目管理领域优质创作者 &#x1f48c;个人邮箱&#xff1a;[2707492172qq.com] &#x1f310;PMP资料导航&#xff1a;PM菜鸟&#xff08;查阅PMP大纲考点&#xff09; &#x1f4a1;座右铭&#xff1a;上善若水&#xff0c;水善利…