Flutter 下拉刷新上拉加载(电影app客户端案例)

前言:

各位同学大家好,很久没有更新文章了,最近看了下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 文件中添加依赖 如图:


QQ截图20200829180130.png

然后在控制台输入flutter pub get 下载依赖

效果图:

Screenrecorder-2020-08-29-19-16-21-704[00_00_06--00_00_26].gif
Screenrecorder-2020-08-29-19-16-12-440[00_00_03--00_00_23].gif

具体实现:

QQ截图20200829192253.png

底部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个列表页面的结构都是一样的,我们就用了一个页面分别传入不同的参数来实现即可

侧边栏实现:

效果图:


QQ截图20200829192735.png

我们在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线性布局组件来排版也行)

电影列表(下拉刷新 上拉加载 实现)

效果图:
Screenrecorder-2020-08-29-19-16-21-704[00_00_06--00_00_26].gif

下拉刷新

下来刷新我们需要在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 集合全部都添加到 Listdata=new List();我们定义的data集合里面就完成了上拉加载数据的目的

上拉加载

加载更多需要对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

你可能感兴趣的:(Flutter 下拉刷新上拉加载(电影app客户端案例))