0. 主要实现功能
- 网络请求
- Json数据转Model
- TabBar切换不重新init页面
- 无限列表
- 下拉刷新、上拉加载
1. 页面布局
页面布局使用的是PageView + BottomNavigationBar。
用此布局,然后再PageView的Widget里采用AutomaticKeepAliveClientMixin,即可实现切换Tab保存Widget状态。
class CurrentMovieState extends State<CurrentMovie> with AutomaticKeepAliveClientMixin<CurrentMovie> {
@override
bool get wantKeepAlive => true;
...
}
2. 网络请求和数据解析
网络请求用的是dio。
// 网络请求
Future getDouBanCurrentMovie() async {
try {
Response response = await Dio()
.get("https://api.douban.com/v2/movie/in_theaters?count=20");
print(response.statusCode); // 此处应该判断状态码
final jsonResponse = json.decode(response.toString());
print(jsonResponse);
DouBanMovieData tempDoubanModel = new DouBanMovieData.fromJson(jsonResponse);
print(tempDoubanModel.movies[0].image);
setState(() {
movies = tempDoubanModel.movies;
});
}catch (e) {
print(e);
}
}
因为只有一种数据模型,我只新建了一个model文件,douban_data.dart里面存放了数据Model。
/// 豆瓣列表Data Model
class DouBanMovieData {
String title;
// double count; // 每页最多数量
// double total; // 当前页条目数
List<Movie> movies; // 电影列表
DouBanMovieData({
this.title,
// this.count,
// this.total,
this.movies,
});
factory DouBanMovieData.fromJson(Map<String, dynamic> json) {
var list = json['subjects'] as List;
List<Movie> movieList = list.map((i) => Movie.fromJson(i)).toList();
return DouBanMovieData(
title: json['title'],
// count: json['count'],
// total: json['total'],
movies: movieList
);
}
}
/// 豆瓣电影Model
class Movie {
num rating; // 电影评分
int collectCount; // 收藏数 | 观看人数
String title; // 电影名
List<Person> casts; // 演员表
String originalTitle; // 原始名
String subtype; // 类型
String year; // 电影年份
String image; // 电影海报
List<String> genres; // 电影类型
List<Person> directors; // 电影导演表
String alt; // 电影简介地址
String id;
Movie({
this.rating,
this.collectCount,
this.title,
this.casts,
this.originalTitle,
this.subtype,
this.year,
this.image,
this.genres,
this.directors,
this.alt,
this.id
});
factory Movie.fromJson(Map<String, dynamic> mJson) {
var gList = mJson['genres'];
List<String> genresList = new List<String>.from(gList);
var cList = mJson['casts'] as List;
List<Person> castsList = cList.map((i) => Person.fromJson(i)).toList();
var dList = mJson['directors'] as List;
List<Person> directorList = dList.map((i) => Person.fromJson(i)).toList();
num average = mJson['rating']['average'];
return Movie(
rating: average,
collectCount: mJson['collect_count'],
title: mJson['title'],
casts: castsList,
originalTitle: mJson['original_title'],
subtype: mJson['subtype'],
year: mJson['year'],
image: mJson['images']['medium'],
genres: genresList,
directors: directorList,
alt: mJson['alt'],
id: mJson['id']
);
}
}
class Person {
String alt; // 简介
String avatar; // 头像
String name; // 名字
String id;
Person({
this.alt,
this.avatar,
this.name,
this.id,
});
factory Person.fromJson(Map<String, dynamic> pJson) {
var avatar = '';
if (pJson['avatars'] != null) {
avatar = pJson['avatars']['medium'];
}
return Person(
alt: pJson['alt'],
avatar: avatar,
name: pJson['name'],
id: pJson['id']
);
}
}
解析的时候有点小插曲,在解析 Movie
时,有个属性 rating; //电影评分
。我先开始设的属性是double类型,在解析正在热映列表的时候,没出现问题,但是到了解析即将上映电影列表的时候,就报错,一直报 a int type not a subtype a double
。因为有好几个double
类型的属性,我也不知道哪一个。我看了下数据,发现在即将上映的电影中有的评分是0,然后就会被认为 int
。
所以遇到这种不确定的数字数据类型,可以使用 num
来解析。
如果是使用 num
声明的变量,可以随意的转换类型,但是如果是使用了 int
或者 double
明确的声明,那么就不能转换了。
4. 下拉刷新、下拉加载
下拉刷新动画使用的是liquid_pull_to_refresh。集成很简单可参考文档。
上拉加载没找到好的库😳。然后搜资料,都是根据监听List的controller的滑动位置,来加载更多。
@override
void initState() {
super.initState();
getDouBanTopMovie();
scrollController.addListener(() {
if (scrollController.offset == scrollController.position.maxScrollExtent) {
if (!isNoMore) {
loadMore();
}
}
});
}
loadMore里设置动画属性和请求更多数据。
完整代码已上传github