1. 效果图

2. 下拉加载的实现RefreshIndicator
在Flutter官方sdk中给我们提供了下拉刷新的组件RefreshIndicator。
dart
// 显示内容列表
Widget _showNewsListWidget() {
if (_newsDataList.isNotEmpty) {
// RefreshIndicator 来实现下拉加载的功能
return RefreshIndicator(
onRefresh: () async {
print("下拉加载");
// 网络数据的请求
_getNewDataRequest();
},
child: ListView.builder(
itemCount: _newsDataList.length,
itemBuilder: (context, index) {
return Column(
children: [
ListTile(
title: Text(_newsDataList[index]["title"]),
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => NewsdetailDemo(cid: _newsDataList[index]["aid"])),
);
},
)
],
);
},
)
);
} else {
// loading
return Center(
child: CircularProgressIndicator(),
);
}
}
3. 上拉加载的实现
flutter是没有提供上拉分页加载更多的组件,但是在Flutter ListView中有一个ScrollController
组件,它就是专门来控制ListView
滑动事件,在
这里我们可以根据ListView的位置来判断是否滑动到了底部来做加载更多的处理
。
上拉分页加载更多主要通过ListView监听
ScrollController
实现
_scrollController.position.pixels
滚动的距离
_scrollController.position.maxScrollExtent
总距离
核心代码,就是在 initState
方法中,添加监听:
dart
_scrollController = ScrollController(); //listview的控制器
_scrollController.addListener((){
double scrollOffsetY = _scrollController.position.pixels;
double scrollContentHeight = _scrollController.position.maxScrollExtent;
if (scrollOffsetY > scrollContentHeight + 20) {
print("上拉加载更多");
_getNewDataRequest();
}
});
在ListView
中的controller
中 关联_scrollController
, 代码如下:
dart
ListView.builder(
// controller 关联_scrollController
controller: _scrollController,
itemCount: _newsDataList.length,
itemBuilder: (context, index) {
return Column();
})
)
4. 实现列表上下拉加载的完整代码
dart
import 'dart:convert';
import 'package:demoapp/Demo/NewsDemo/NewsDetail_demo.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
class NewsDemo extends StatefulWidget {
const NewsDemo({super.key});
@override
State<NewsDemo> createState() => _NewsDemoState();
}
class _NewsDemoState extends State<NewsDemo> {
final ScrollController _scrollController = ScrollController();
List _newsDataList = [];
int _page = 1;
bool _isLoadingData = false;
bool _hasMoreData = true;
// 获取网络请求的数据
void _getNewDataRequest() async {
if (_isLoadingData == true ) {
print("数据加载中,请勿重新加载");
return;
}
if (_hasMoreData == false) {
print("没有更多的数据了");
return;
}
_isLoadingData = true;
var apiUri = "https://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=$_page";
print("apiUri = $apiUri");
final response = await Dio().get(apiUri);
List resultList = json.decode(response.data)["result"];
setState(() {
print("数据请求成功");
if (_page == 1) {
_newsDataList = resultList;
} else {
_newsDataList.addAll(resultList);
}
_isLoadingData = false;
if (resultList.isNotEmpty) {
_page += 1;
}
if (resultList.length < 20) {
_hasMoreData = false;
}
});
}
// cell 底部视图的设置
Widget _newsListCellBottomWidget(int index) {
if (index == _newsDataList.length - 1) {
if (_hasMoreData) {
return CircularProgressIndicator();
} else {
return Text("---我也是有底线的---");
}
} else {
return Divider();
}
}
// 显示内容列表
Widget _showNewsListWidget() {
if (_newsDataList.isNotEmpty) {
// RefreshIndicator 来实现下拉加载的功能
return RefreshIndicator(
onRefresh: () async {
print("下拉加载");
_page = 1;
_hasMoreData = true;
_getNewDataRequest();
},
child: ListView.builder(
controller: _scrollController,
itemCount: _newsDataList.length,
itemBuilder: (context, index) {
return Column(
children: [
ListTile(
title: Text(_newsDataList[index]["title"]),
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => NewsdetailDemo(cid: _newsDataList[index]["aid"])),
);
},
),
_newsListCellBottomWidget(index)
],
);
},
)
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}
@override
void initState() {
super.initState();
_scrollController.addListener((){
double scrollOffsetY = _scrollController.position.pixels;
double scrollContentHeight = _scrollController.position.maxScrollExtent;
if (scrollOffsetY > scrollContentHeight + 20) {
print("上拉加载更多");
_getNewDataRequest();
}
});
_getNewDataRequest();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("模拟新闻列表上下拉加载 + 加载html的数据"),
),
body: _showNewsListWidget(),
);
}
}
5. 加载 Html 的数据
- 第三方插件:
flutter_widget_from_html_core: ^0.16.0
- 引入头文件:
dart
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
- 使用代码
dart
return HtmlWidget(
"xxxx==> Html 的数据",
onTapImage: (imageMetadata) {
print(imageMetadata);
},
onTapUrl: (url) {
print(url);
return true;
},
);
6. 实现加载html的数据,完整代码
dart
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
class NewsdetailDemo extends StatefulWidget {
final String cid;
const NewsdetailDemo({super.key, required this.cid});
@override
State<NewsdetailDemo> createState() => _NewsdetailDemoState();
}
class _NewsdetailDemoState extends State<NewsdetailDemo> {
List _resultDetailList = [];
// 获取详情的数据
void _getNewsDetailRequest() async {
String apiUri = "https://www.phonegap100.com/appapi.php?a=getPortalArticle&aid=${widget.cid}";
final response = await Dio().get(apiUri);
final result = json.decode(response.data)["result"];
setState(() {
_resultDetailList = result;
});
}
// 内容视图
Widget _newsDetailWidget() {
if (_resultDetailList.isNotEmpty) {
Map resultInfo = _resultDetailList.isNotEmpty ? _resultDetailList[0] : {};
return Padding(
padding: EdgeInsets.all(10),
child: ListView(
children: [
Text(resultInfo["title"], textAlign: TextAlign.center, style: TextStyle(fontSize: 24),),
SizedBox(height: 20),
HtmlWidget(
resultInfo["content"],
onTapImage: (imageMetadata) {
print(imageMetadata);
},
onTapUrl: (url) {
print(url);
return true;
},
)
],
),
);
} else {
return Center(child: CircularProgressIndicator());
}
}
@override
void initState() {
super.initState();
_getNewsDetailRequest();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("详情"),
),
body: _newsDetailWidget(),
);
}
}