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: <Widget>[
              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: <Widget>[
            Movielist(mt: "in_theaters",),
            Movielist(mt:"coming_soon"),
            Movielist(mt:"top250"),
          ],
        ),

这边是用到了 bottomNavigationBar 组件里面嵌套3个tab组件来实现底部tab的切换之前有讲到过 ,然后我们在body里面 添加3个widget 来处理里上面电影列表的展示页面

    body: TabBarView(
          children: <Widget>[
            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 集合全部都添加到 List<Subject>data=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<Movielist> {
  int  page=1;
  int pagesize=5;
  var  mlist=[];
  var total=0;
  int  datasize=0;
  MovieBean movieBean;
  Subject _subject;
  List<Subject>data=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: <Widget>[
        Image.network(_subject.images.small,
          width: 130,
          height: 180,
          fit: BoxFit.fill,),
        Container(
          height: 200,
          child:Column(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Text("电影名称:"+_subject.title),
              Text("上映年份"+_subject.year),
              Text("电影类型:"+_subject.genres.join(",")),
              Text("豆瓣评分:"+_subject.rating.average+"分"),
              Row(
                children: <Widget>[
                  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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,056评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,842评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,938评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,296评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,292评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,413评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,824评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,493评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,686评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,502评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,553评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,281评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,820评论 3 305
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,873评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,109评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,699评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,257评论 2 341