文章目錄
- 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
- 簡書
- 博客園
- 掘金