前言:
各位同学大家好,很久没有更新文章了,最近看了下flutter的下拉刷新和上拉加载,今天有时间就把之前帮朋友做的毕业设计的做到一半的电影app 加上 ,下拉刷新和上拉加载的功能 分享给大家,那么废话不多说我们正式开始
准备工作:
需要安装flutter的开发环境:大家可以去看看之前的教程:
1 win系统flutter开发环境安装教程: https://www.jianshu.com/p/152447bc8718
2 mac系统flutter开发环境安装教程:https://www.jianshu.com/p/bad2c35b41e3
需要用到的三方库:
dio: ^3.0.9
toast: ^0.1.5
请在pubspec.yaml 文件中添加依赖 如图:
然后在控制台输入flutter pub get 下载依赖
效果图:
具体实现:
底部tab切换
bottomNavigationBar:Container(
decoration: BoxDecoration(
color: Colors.black
),
height: 50,
child: TabBar(
labelStyle: TextStyle(
height: 0,
fontSize: 10
),
tabs: [
Tab(icon: Icon(Icons.movie_filter),text: "正在热映",),
Tab(icon: Icon(Icons.movie_creation),text: "即将上映",),
Tab(icon: Icon(Icons.local_movies),text: "Top250",),
],
),
),
body: TabBarView(
children: [
Movielist(mt: "in_theaters",),
Movielist(mt:"coming_soon"),
Movielist(mt:"top250"),
],
),
这边是用到了 bottomNavigationBar 组件里面嵌套3个tab组件来实现底部tab的切换之前有讲到过 ,然后我们在body里面 添加3个widget 来处理里上面电影列表的展示页面
body: TabBarView(
children: [
Movielist(mt: "in_theaters",),
Movielist(mt:"coming_soon"),
Movielist(mt:"top250"),
],
),
这边因为3个列表页面的结构都是一样的,我们就用了一个页面分别传入不同的参数来实现即可
侧边栏实现:
效果图:
我们在Scaffold脚手架框架里面添加了一个 Drawer组件实现的侧边栏 然后我们Drawer组件里面嵌套了一个listview组件并且使用listview的多布局来实现侧边栏的布局
drawer:new Drawer(
child: ListView.builder(
padding: EdgeInsets.all(0),
itemCount: 5,
itemBuilder: (BuildContext context, int position){
if(position==0){
return item1(position);
}else{
return item2(position);
}
}) ,
) ,
当然在这里你也可以用其他组件来实现 (例如Column线性布局组件来排版也行)
电影列表(下拉刷新 上拉加载 实现)
效果图:下拉刷新
下来刷新我们需要在listview 的外面嵌套一层 RefreshIndicator 然后在RefreshIndicator里面实现onRefresh方法。
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
child:RefreshIndicator(
onRefresh:_refresh ,
child: ListView.builder(
controller: _controller, //指明控制器加载更多使用
itemCount: data==null?0:data.length,
itemBuilder: (BuildContext context, int position){
return itemWidget(position);
},
),
)
);
}
onRefresh方法的实现_refresh,注意这里必须使用async 不然报错
Future _refresh()async {
print("上拉加载");
page=0;
data.clear();
getMovielist();
return null;
}
异步加载数据 我们使用dio进行网络请求拿到数据,然后重写 setState方法刷新UI
getMovielist()async {
this.page++;
int offset=(page-1)*pagesize;
Dio dio=new Dio();
Response response=await dio.get("http://www.liulongbin.top:3005/api/v2/movie/${widget.mt}?
start=$offset&count=$pagesize");
setState(() {
var result=response.data.toString();
print("result ---- 首次请求数据 --- > "+result);
movieBean =MovieBean.fromJson(response.data);
data.addAll(movieBean.subjects);
});
}
这里我们通过MovieBean 数据模型类 讲json数据转成实体来获取里面的数据 ,我们调用list集合里面的
addAll将每次加载的返回的list 集合全部都添加到 List
上拉加载
加载更多需要对ListView进行监听,所以需要进行监听器的设置,在State中进行监听器的初始化。
//初始化滚动监听器,加载更多使用
ScrollController _controller = new ScrollController();
在构造器中设置监听
_MovielistState() {
//固定写法,初始化滚动监听器,加载更多使用
_controller.addListener(() {
var maxScroll = _controller.position.maxScrollExtent;
var pixel = _controller.position.pixels;
//
if (maxScroll == pixel&& data.length < total) {
setState(() {
loadMoreText = "正在加载中...";
loadMoreTextStyle =
new TextStyle(color: const Color(0xFF4483f6), fontSize: 14.0);
});
getMovielist();
} else {
setState(() {
loadMoreText = "没有更多数据";
loadMoreTextStyle =
new TextStyle(color: const Color(0xFF999999), fontSize: 14.0);
});
}
});
}
然后我们在listview中添加监听controller方法
child: ListView.builder(
controller: _controller, //指明控制器加载更多使用
itemCount: data==null?0:data.length,
itemBuilder: (BuildContext context, int position){
return itemWidget(position);
},
至此我们的flutter下拉刷新和上拉加载就讲完了
完整的movie_list 类代码示例
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'movie_bean.dart';
import '../toast_util.dart';
/**
*
* 创建人:xuqing
* 创建时间:2020年8月2日16:25:52
* 类说明:电影列表
*/
class Movielist extends StatefulWidget {
final String mt;
Movielist({Key key,this.mt}) : super(key: key);
@override
_MovielistState createState() {
return _MovielistState();
}
}
class _MovielistState extends State {
int page=1;
int pagesize=5;
var mlist=[];
var total=0;
int datasize=0;
MovieBean movieBean;
Subject _subject;
Listdata=new List();
String loadMoreText;
ScrollController _controller = new ScrollController();
_MovielistState() {
//固定写法,初始化滚动监听器,加载更多使用
_controller.addListener(() {
var maxScroll = _controller.position.maxScrollExtent;
var pixel = _controller.position.pixels;
if (maxScroll == pixel&& data.length < total&&datasize<=5) {
loadMoreText = "正在加载中...";
ToastUtil.showInfo(context, loadMoreText);
getMovielist();
} else {
loadMoreText = "没有更多数据";
ToastUtil.showInfo(context, loadMoreText);
}
});
}
@override
void initState() {
super.initState();
getMovielist();
}
@override
void dispose() {
super.dispose();
}
getMovielist()async {
this.page++;
int offset=(page-1)*pagesize;
Dio dio=new Dio();
Response response=await dio.get("http://www.liulongbin.top:3005/api/v2/movie/${widget.mt}?start=$offset&count=$pagesize");
setState(() {
var result=response.data.toString();
print("result ---- 首次请求数据 --- > "+result);
movieBean =MovieBean.fromJson(response.data);
data.addAll(movieBean.subjects);
total=movieBean.total;
datasize=movieBean.subjects.length;
print("datasize --- > "+datasize.toString());
});
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return data.length==0?
new Center(child: new CircularProgressIndicator())
:Container(
child:RefreshIndicator(
onRefresh:_refresh ,
child: ListView.builder(
controller: _controller, //指明控制器加载更多使用
itemCount: data==null?0:data.length,
itemBuilder: (BuildContext context, int position){
return itemWidget(position);
},
),
)
);
}
Future _refresh()async {
print("上拉加载");
page=0;
data.clear();
getMovielist();
return null;
}
/**
* item布局
*
*/
Widget itemWidget(int index){
_subject=data[index];
Rating rating=_subject.rating;
if(_subject==null){
return Container(
child: Center(
child: Text("暂时没有数据",style: TextStyle(
color: Colors.red,fontSize: 30
),),
),
);
}else{
return GestureDetector(
child:Container(
height: 200,
decoration: BoxDecoration(color: Colors.white,border: Border(
top: BorderSide(
color: Colors.black
)
)),
child:Row(
children: [
Image.network(_subject.images.small,
width: 130,
height: 180,
fit: BoxFit.fill,),
Container(
height: 200,
child:Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("电影名称:"+_subject.title),
Text("上映年份"+_subject.year),
Text("电影类型:"+_subject.genres.join(",")),
Text("豆瓣评分:"+_subject.rating.average+"分"),
Row(
children: [
Text("主要演员"),
Container(
width: 30,
height: 30,
child: Image.network(_subject.images.small),
),
Container(
width: 30,
height: 30,
child: Image.network(_subject.images.large),
),
Container(
width: 30,
height: 30,
child: Image.network(_subject.images.medium)
)
],
)
],
),
)
],
) ,
)
);
}
}
}
最后总结:
flutter中下来刷新和上来加载主要是处理好 RefreshIndicator 外层嵌套的组件里面的onRefresh 刷新方法和listview 组件中的 controller 滑动监听 就可以实现了比起原生要简单很多,当然我们也可以用一些三方库来实现下来刷新和上拉加载 例如( dynamic_list_view 组件)等等 有兴趣学的的同学可以私下多多交流 最后希望我的文章能帮助到各位解决问题 ,以后我还会贡献更多有用的代码分享给大家。各位同学如果觉得文章还不错 ,麻烦给关注和star,小弟在这里谢过啦
项目地址:
码云 :https://gitee.com/qiuyu123/flutter_movie.git