1.模仿安卓的刷新样式
这部分的文章感谢晓果博客,原文链接https://blog.csdn.net/huangxiaoguo1/article/details/85603172
1.1 下拉刷新
如果实现下拉刷新,可以借助RefreshIndicator,在listview外面包裹一层RefreshIndicator,然后在RefreshIndicator里面实现onRefresh方法。
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
backgroundColor: Colors.white,
appBar: new AppBar(
leading: new IconButton(
icon: const Icon(Icons.arrow_back),
onPressed:null ,
),
title: new Text(typeName != null ? typeName : "正在加载中...",
style: new TextStyle(color: Colors.black)),
backgroundColor: Colors.white,
),
//判断数组是否为空,如果不为空才去进行下一步操作
body: movieList.length == 0
? new Center(child: new CircularProgressIndicator())
: new RefreshIndicator(
color: const Color(0xFF4483f6),
//下拉刷新
child: ListView.builder(
itemCount: movieList.length + 1,
itemBuilder: (context, index) {
if (index == movieList.length) {
return _buildProgressMoreIndicator();
} else {
return renderRow(index, context);
}
},
controller: _controller, //指明控制器加载更多使用
),
onRefresh: _pullToRefresh,
),
);
}
onRefresh方法的实现_pullToRefresh,注意这里必须使用async 不然报错.
Future _pullToRefresh() async {
currentPage = 0;
movieList.clear();
loadMoreData();
return null;
}
异步加载数据,注意:在Flutter中刷新数据使用的是setState,不然无效,数据不会刷新;数据的获取需要使用[]取值,不能使用对象“ . ”的取值方法!
//加载列表数据
loadMoreData() async {
this.currentPage++;
var start = (currentPage - 1) * pageSize;
var url =
"https://api.douban.com/v2/movie/$movieType?start=$start&count=$pageSize";
Dio dio = new Dio();
Response response = await dio.get(url);
setState(() {
movieList.addAll(response.data["subjects"]);
totalSize = response.data["total"];
});
}
1.2 上拉加载更多
加载更多需要对ListView进行监听,所以需要进行监听器的设置,在State中进行监听器的初始化.
//初始化滚动监听器,加载更多使用
ScrollController _controller = new ScrollController();
在构造器中设置监听
_controller.addListener(() {
var maxScroll = _controller.position.maxScrollExtent;
var pixel = _controller.position.pixels;
if (maxScroll == pixel && movieList.length < totalSize) {
setState(() {
loadMoreText = "正在加载中...";
loadMoreTextStyle =
new TextStyle(color: const Color(0xFF4483f6), fontSize: 14.0);
});
loadMoreData();
} else {
setState(() {
loadMoreText = "没有更多数据";
loadMoreTextStyle =
new TextStyle(color: const Color(0xFF999999), fontSize: 14.0);
});
}
});
在listView中添加监听controller方法,
return new Scaffold(
backgroundColor: Colors.white,
appBar: new AppBar(
leading: new IconButton(
icon: const Icon(Icons.arrow_back),
onPressed:null ,
),
title: new Text(typeName != null ? typeName : "正在加载中...",
style: new TextStyle(color: Colors.black)),
backgroundColor: Colors.white,
),
body: movieList.length == 0
? new Center(child: new CircularProgressIndicator())
: new RefreshIndicator(
color: const Color(0xFF4483f6),
//下拉刷新
child: ListView.builder(
itemCount: movieList.length + 1,
itemBuilder: (context, index) {
if (index == movieList.length) {
return _buildProgressMoreIndicator();
} else {
return renderRow(index, context);
}
},
controller: _controller, //指明控制器加载更多使用
),
onRefresh: _pullToRefresh,
),
);
}
完整的代码
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:douban/pages/movie/movieDetail.dart';
class MovieList extends StatefulWidget {
String movieType;
//构造器传递数据(并且接收上个页面传递的数据)
MovieList({Key key, this.movieType}) : super(key: key);
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new MovieListState(movieType: this.movieType);
}
}
class MovieListState extends State<MovieList> {
String movieType;
String typeName;
List movieList = new List();
int currentPage = 0; //第一页
int pageSize = 10; //页容量
int totalSize = 0; //总条数
String loadMoreText = "没有更多数据";
TextStyle loadMoreTextStyle =
new TextStyle(color: const Color(0xFF999999), fontSize: 14.0);
TextStyle titleStyle =
new TextStyle(color: const Color(0xFF757575), fontSize: 14.0);
//初始化滚动监听器,加载更多使用
ScrollController _controller = new ScrollController();
/**
* 构造器接收(MovieList)数据
*/
MovieListState({Key key, this.movieType}) {
//固定写法,初始化滚动监听器,加载更多使用
_controller.addListener(() {
var maxScroll = _controller.position.maxScrollExtent;
var pixel = _controller.position.pixels;
if (maxScroll == pixel && movieList.length < totalSize) {
setState(() {
loadMoreText = "正在加载中...";
loadMoreTextStyle =
new TextStyle(color: const Color(0xFF4483f6), fontSize: 14.0);
});
loadMoreData();
} else {
setState(() {
loadMoreText = "没有更多数据";
loadMoreTextStyle =
new TextStyle(color: const Color(0xFF999999), fontSize: 14.0);
});
}
});
}
//加载列表数据
loadMoreData() async {
this.currentPage++;
var start = (currentPage - 1) * pageSize;
var url =
"https://api.douban.com/v2/movie/$movieType?start=$start&count=$pageSize";
Dio dio = new Dio();
Response response = await dio.get(url);
setState(() {
movieList.addAll(response.data["subjects"]);
totalSize = response.data["total"];
});
}
@override
void initState() {
super.initState();
//设置当前导航栏的标题
switch (movieType) {
case "in_theaters":
typeName = "正在热映";
break;
case "coming_soon":
typeName = "即将上映";
break;
case "top250":
typeName = "Top250";
break;
}
//加载第一页数据
loadMoreData();
}
/**
* 下拉刷新,必须异步async不然会报错
*/
Future _pullToRefresh() async {
currentPage = 0;
movieList.clear();
loadMoreData();
return null;
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
backgroundColor: Colors.white,
appBar: new AppBar(
leading: new IconButton(
icon: const Icon(Icons.arrow_back),
onPressed:null ,
),
title: new Text(typeName != null ? typeName : "正在加载中...",
style: new TextStyle(color: Colors.black)),
backgroundColor: Colors.white,
),
body: movieList.length == 0
? new Center(child: new CircularProgressIndicator())
: new RefreshIndicator(
color: const Color(0xFF4483f6),
//下拉刷新
child: ListView.builder(
itemCount: movieList.length + 1,
itemBuilder: (context, index) {
if (index == movieList.length) {
return _buildProgressMoreIndicator();
} else {
return renderRow(index, context);
}
},
controller: _controller, //指明控制器加载更多使用
),
onRefresh: _pullToRefresh,
),
);
}
/**
* 加载更多进度条
*/
Widget _buildProgressMoreIndicator() {
return new Padding(
padding: const EdgeInsets.all(15.0),
child: new Center(
child: new Text(loadMoreText, style: loadMoreTextStyle),
),
);
}
/**
* 列表的ltem
*/
renderRow(index, context) {
var movie = movieList[index];
var id = movie["id"];
var title = movie["title"];
var type = movie["genres"].join("、");
var year = movie["year"];
var score = movie["rating"]["average"];
return new Container(
height: 200,
color: Colors.white,
child: new InkWell(
onTap: () {
Navigator.of(context).push(new MaterialPageRoute(
builder: (ctx) => new MovieDetail(movieId: id)));
},
child: new Column(
children: <Widget>[
new Container(
height: 199,
// color: Colors.blue,
child: new Row(
children: <Widget>[
new Container(
width: 120.0,
height: 180.0,
margin: const EdgeInsets.all(10.0),
child: Image.network(movie["images"]["small"]),
),
Expanded(
child: new Container(
height: 180.0,
margin: const EdgeInsets.all(12.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Text(
"电影名称:$title",
style: titleStyle,
overflow: TextOverflow.ellipsis,
),
new Text(
"电影类型:$type",
style: titleStyle,
overflow: TextOverflow.ellipsis,
),
new Text(
"上映年份:$year",
style: titleStyle,
overflow: TextOverflow.ellipsis,
),
new Text(
"豆瓣评分:$score",
style: titleStyle,
overflow: TextOverflow.ellipsis,
)
],
),
),
),
],
),
),
//分割线
new Divider(height: 1)
],
),
));
}
}
2.模仿iOS的刷新样式
模仿iOS的刷新,我们可以使用github上flutter_easyrefresh.
我们先简单看一个例子:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:flutter_easyrefresh/bezier_circle_header.dart';//如果要使用炫酷的样式需要引入,不同的样式引入不同的文件,详见官方api
import 'package:flutter_easyrefresh/bezier_bounce_footer.dart';//如果要使用炫酷的样式需要引入,不同的样式引入不同的文件,详见官方api
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
// App名字
title: 'EasyRefresh',
// App主题
theme: new ThemeData(
primarySwatch: Colors.orange,
),
// 主页
home: BasicPage(),
);
}
}
class BasicPage extends StatefulWidget {
@override
_BasicPageState createState() => _BasicPageState();
}
class _BasicPageState extends State<BasicPage> {
List<String> addStr = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"];
List<String> str = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"];
GlobalKey<EasyRefreshState> _easyRefreshKey =
new GlobalKey<EasyRefreshState>();
GlobalKey<RefreshHeaderState> _headerKey =
new GlobalKey<RefreshHeaderState>();
GlobalKey<RefreshFooterState> _footerKey =
new GlobalKey<RefreshFooterState>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("EasyRefresh"),
),
body: Center(
child: new EasyRefresh(
key: _easyRefreshKey,
refreshHeader: BezierCircleHeader(
key: _headerKey,
color: Theme
.of(context)
.scaffoldBackgroundColor,
),
refreshFooter: BezierBounceFooter(
key: _footerKey,
color: Theme
.of(context)
.scaffoldBackgroundColor,
),
child: new ListView.builder(
//ListView的Item
itemCount: str.length,
itemBuilder: (BuildContext context, int index) {
return new Container(
height: 70.0,
child: Card(
child: new Center(
child: new Text(
str[index],
style: new TextStyle(fontSize: 18.0),
),
),
));
}),
onRefresh: () async {
await new Future.delayed(const Duration(seconds: 1), () {
setState(() {
str.clear();
str.addAll(addStr);
});
});
},
loadMore: () async {
await new Future.delayed(const Duration(seconds: 1), () {
if (str.length < 20) {
setState(() {
str.addAll(addStr);
});
}
});
},
)),
// persistentFooterButtons: <Widget>[
// FlatButton(
// onPressed: () {
// _easyRefreshKey.currentState.callRefresh();
// },
// child: Text("refresh", style: TextStyle(color: Colors.black))),
// FlatButton(
// onPressed: () {
// _easyRefreshKey.currentState.callLoadMore();
// },
// child: Text("loadMore", style: TextStyle(color: Colors.black)))
// ], // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
我们需要先定义三种key来表示每一种操作:
GlobalKey<EasyRefreshState> _easyRefreshKey =
new GlobalKey<EasyRefreshState>();
GlobalKey<RefreshHeaderState> _headerKey =
new GlobalKey<RefreshHeaderState>();
GlobalKey<RefreshFooterState> _footerKey =
new GlobalKey<RefreshFooterState>();
想了解更多Flutter学习知识请联系:QQ(814299221)
网友评论