可以用ScrollController
组件来实现这样列表上拉加载更多
的功能:
1. 定义变量
在StatefulWidget
的组件内,添加三个属性:
// 滚动视图的控制器
final ScrollController _scrollController = ScrollController();
// 是否已显示了上拉加载中
bool _isShowMore = false;
// 是否有更多的数据
bool _isHaveMoreData = true;
2. 在初始化的方法中,添加对ScrollController
的监听
重写initState
的方法,并在initState
方法中,添加ScrollController
的监听,代码如下:
void initState() {
super.initState();
// 对 _scrollController 添加监听
_scrollController.addListener((){
// 内容视图最大的高度
double maxScrollExtent = _scrollController.position.maxScrollExtent;
// 视图滚动的大小
double scrollContentOffY = _scrollController.position.pixels;
if (scrollContentOffY > maxScrollExtent + Screenadapter.height(40)) {
// 超出了内容视图高度的20,就重新上拉加载更多
if (_isShowMore == false && _isHaveMoreData == true) {
// 未显示,
print("上拉加载更多");
// 添加网络请求
_getProductListDataRequest();
}
}
});
3. 在ListView
中,关联ScrollController
代码如下:
ListView.builder(
// 关联 ScrollController
controller: _scrollController,
itemBuilder: (BuildContext context, int index) {
return Column();
}
)
4. 写一个简易的Loading
的加载视图
代码如下:
import 'package:fangjd/Services/ScreenAdapter.dart';
import 'package:flutter/material.dart';
class Loadingwidget extends StatelessWidget {
const Loadingwidget({super.key});
Widget build(BuildContext context) {
return Center(
child: Container(
padding: EdgeInsets.all(Screenadapter.width(20)),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(strokeWidth: 1.0),
SizedBox(width: Screenadapter.width(20)),
Text("加载中....")
],
),
),
);
}
}
5. 在列表的底部视图,添加加载中
的视图或者添加我是我底线
的视图
代码如下:
// 商品列表底部的视图
Widget _productBottomWiget(int index) {
if (_isHaveMoreData == true) {
// 如果有更多的数据,上拉,就显示“加载中”, 其中_productList 是数据源
if (_isShowMore && (index == _productList.length -1)) {
// 只有最后一个数据,才添加 “加载中”的loading视图
return Loadingwidget();
} else {
// 其他数据 就什么都不显示
return SizedBox(height: 1);
}
} else {
// 如果没有更多的数据,
if (index == _productList.length -1) {
// 在最后一个数据,添加 “--我也是有底线的--”的视图
return Padding(padding:EdgeInsets.only(top: Screenadapter.height(30)), child: Text("--我也是有底线的--", textAlign: TextAlign.center,));
} else {
// 其他数据 就什么都不显示
return SizedBox(height: 1);
}
}
}
6. 模拟网络数据的请求
代码如下:
// -----网络请求--------
void _getProductListDataRequest() async {
setState(() {
// 当调用网络请求的时候,设置_isShowMore 为 true, 表示需要展示“加载中”的底部视图
_isShowMore = true;
});
// 模拟网络延迟
await Future.delayed(Duration(seconds: 2));
List<ProductItemModel> mockDataList = _mockData();
setState(() {
_productList.addAll(mockDataList);
// 等网路请求成功后,设置 _isShowMore 为 false, 表示 隐藏 “加载中”的底部视图
_isShowMore = false;
if (_productList.length > 20) {
// 模拟没有更多数据了,就展示“我是有底线的”的视图
_isHaveMoreData = false;
}
});
}
// 模拟数据
List<ProductItemModel> _mockData() {
// return
List<ProductItemModel> mockList = [];
for (var index in [1,2,3,4,5,6,7,8,9,0]) {
var model = ProductItemModel(iId: 1, title: '笔记本电脑$index', price: "¥3980", oldPrice: "¥4999", pic: "https://www.itying.com/images/flutter/hot${index+1}.jpg");
mockList.add(model);
}
return mockList;
}
7. 完整的代码实例
import 'package:fangjd/CommonWidget/LoadingWidget.dart';
import 'package:fangjd/Models/ProductModel.dart';
import 'package:fangjd/Services/ScreenAdapter.dart';
import 'package:flutter/material.dart';
class ProductlistPage extends StatefulWidget {
const ProductlistPage({super.key});
State<ProductlistPage> createState() => _ProductlistPageState();
}
class _ProductlistPageState extends State<ProductlistPage> {
// ScaffoldState, 控制侧边栏的显示
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
// 筛选导航栏的索引
int _selectIndex = 1;
// 商品列表的数据
List<ProductItemModel> _productList = [];
// 滚动视图的控制器
final ScrollController _scrollController = ScrollController();
// 是否已显示了上拉加载中
bool _isShowMore = false;
// 是否有更多的数据
bool _isHaveMoreData = true;
void initState() {
super.initState();
_scrollController.addListener((){
// 内容视图最大的高度
double maxScrollExtent = _scrollController.position.maxScrollExtent;
// 视图滚动的大小
double scrollContentOffY = _scrollController.position.pixels;
if (scrollContentOffY > maxScrollExtent + Screenadapter.height(40)) {
// 超出了内容视图高度的20,就重新上拉加载更多
if (_isShowMore == false && _isHaveMoreData == true) {
// 未显示
_getProductListDataRequest();
}
}
});
// -----网络请求--------
_getProductListDataRequest();
}
// -----网络请求--------
void _getProductListDataRequest() async {
setState(() {
_isShowMore = true;
});
// 模拟网络延迟
await Future.delayed(Duration(seconds: 2));
List<ProductItemModel> mockDataList = _mockData();
setState(() {
_productList.addAll(mockDataList);
_isShowMore = false;
if (_productList.length > 20) {
_isHaveMoreData = false;
}
});
}
// 模拟数据
List<ProductItemModel> _mockData() {
// return
List<ProductItemModel> mockList = [];
for (var index in [1,2,3,4,5,6,7,8,9,0]) {
var model = ProductItemModel(iId: 1, title: '笔记本电脑$index', price: "¥3980", oldPrice: "¥4999", pic: "https://www.itying.com/images/flutter/hot${index+1}.jpg");
mockList.add(model);
}
return mockList;
}
// -----视图设置--------
// 设置 筛选透视图的底部选中的线
Border _selectHeaderBoder() {
return Border(
bottom: BorderSide(
width: 1,
color: Colors.red
)
);
}
// 筛选导航栏
Widget _subHeaderWidget() {
return Positioned(
top: 0.0,
child: Container(
height: Screenadapter.height(80),
width: Screenadapter.screenWidth(),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1,
color: Colors.black12
),
),
),
child: Row(
children: [
Expanded(
child: InkWell(
onTap: () {
setState(() {
_selectIndex = 1;
});
},
child: Container(
decoration: BoxDecoration(
border: _selectIndex == 1 ? _selectHeaderBoder() : null
),
child: Center(child: Text("综合", style: TextStyle(color: _selectIndex == 1 ? Colors.red : Colors.black))),
),
)
),
Expanded(
child: InkWell(
onTap: () {
setState(() {
_selectIndex = 2;
});
},
child: Container(
decoration: BoxDecoration(
border: _selectIndex == 2 ? _selectHeaderBoder() : null
),
child: Center(child: Text("销量", style: TextStyle(color: _selectIndex == 2 ? Colors.red : Colors.black))),
),
)
),
Expanded(
child: InkWell(
onTap: () {
setState(() {
_selectIndex = 3;
});
},
child: Container(
decoration: BoxDecoration(
border: _selectIndex == 3 ? _selectHeaderBoder() : null
),
child: Center(child: Text("价格", style: TextStyle(color: _selectIndex == 3 ? Colors.red : Colors.black))),
),
)
),
Expanded(
child: InkWell(
onTap: () {
setState(() {
_selectIndex = 4;
});
_scaffoldKey.currentState?.openEndDrawer();
},
child: Container(
decoration: BoxDecoration(
border: _selectIndex == 4 ? _selectHeaderBoder() : null
),
child: Center(child: Text("筛选", style: TextStyle(color: _selectIndex == 4 ? Colors.red : Colors.black))),
),
)
),
],
),
)
);
}
// 商品列表底部的视图
Widget _productBottomWiget(int index) {
if (_isHaveMoreData == true) {
if (_isShowMore && (index == _productList.length -1)) {
return Loadingwidget();
} else {
return Text("");
}
} else {
if (index == _productList.length -1) {
return Padding(padding:EdgeInsets.only(top: Screenadapter.height(30)), child: Text("--我也是有底线的--", textAlign: TextAlign.center,));
} else {
return SizedBox(height: 1);
}
}
}
// 商品列表
Widget _productListWidget() {
if (_productList.isNotEmpty) {
return Container(
padding: EdgeInsets.all(Screenadapter.width(10)),
margin: EdgeInsets.only(top: Screenadapter.height(80)),
child: ListView.builder(
controller: _scrollController,
itemBuilder: (BuildContext context, int index) {
ProductItemModel itemModel = _productList[index];
return Column(
children: [
Row(
children: [
Container(
padding: EdgeInsets.all(Screenadapter.width(20)),
width: Screenadapter.width(220),
height: Screenadapter.height(220),
child: AspectRatio(
aspectRatio: 1,
child: Image.network(itemModel.pic, fit: BoxFit.cover)
),
),
Expanded(child: Container(
height: Screenadapter.height(220),
padding: EdgeInsets.only(top: Screenadapter.height(10)),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
itemModel.title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
SizedBox(height: Screenadapter.height(20)),
Container(
height: Screenadapter.height(36),
width: double.infinity,
child: Row(
children: [
Container(
height: Screenadapter.height(40),
width: Screenadapter.width(80),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(Screenadapter.height(18)),
color: Colors.black12
),
child: Center(child: Text(' 4G ', style: TextStyle(color: Colors.black87))),
),
SizedBox(width: Screenadapter.width(20)),
Container(
height: Screenadapter.height(40),
width: Screenadapter.width(80),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(Screenadapter.height(18)),
color: Colors.black12
),
child: Center(child: Text(' 126 ', style: TextStyle(color: Colors.black87))),
),
],
),
),
SizedBox(height: Screenadapter.width(20)),
Text(itemModel.price, style: TextStyle(color: Colors.red, fontSize: 18))
]
)
))
],
),
Divider(height: Screenadapter.height(2)),
// 添加列表底部视图:显示“加载中”还是显示“我是有底线的”
_productBottomWiget(index)
],
);
},
itemCount: _productList.length
),
);
} else {
return Loadingwidget();
}
}
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text("商品分类"),
actions: [
// 去掉’endDrawer‘默认设置的图标
Text("")
],
),
endDrawer: Drawer(
child: Center(child: Text("我是侧边栏")),
),
body: Stack(
children: [
// 筛选导航栏
_subHeaderWidget(),
// 商品列表
_productListWidget()
],
),
);
}
}