flutter 项目实战八 下拉刷新 上拉加载

本项目借用 逛丢 网站的部分数据,仅作为 flutter 开发学习之用。 逛丢官方网址:https://guangdiu.com/

flutter windows开发环境设置

flutter 项目实战一 新建 flutter 项目

flutter 项目实战二 网络请求

flutter 项目实战三 json数据解析以及Gson格式化flutter 项目实战二 网络请求

flutter 项目实战四 列表数据展示

flutter 项目实战五 item 点击跳转,webview加载

flutter 项目实战六 drawer侧边栏

flutter 项目实战七 bottomNavigationBar

flutter 项目实战八 下拉刷新 上拉加载

flutter 项目实战九 小时风云榜

在实战四中我们展示了数据列表,但是由于数据量的以及新数据的问题,一般我们都会给列表加上下拉刷新以及上拉加载的功能,来实现加载更多数据以及刷新新的数据。

flutter 提供了  ScrollController 来监听 listview 的滑动状态,我们未listview.build添加属性

controller: _scrollController,

在 IndexWidget 的构造方法中初始化 _scrollController,增加监听

_scrollController.addListener((){
    var pix=_scrollController.position.pixels;
    var max=_scrollController.position.maxScrollExtent;
    if(pix==max){
      //已下拉到底部,调用加载更多方法
      _loadMoreData();
    }
});

_scrollController.position.pixels指的是当前列表滑动到底部的长度,_scrollController.position.maxScrollExtent为列表的最大长度,当两者相等时,即可认为列表滑动到了底部。为了在滑动到底部时展示一个加载的伪动画效果,我们在列表最下面添加一个圆形的进度条,让其模拟正在加载动画。修改 listview.build的内容:

ListView.builder(
  itemCount: _listData==null?0:_listData.length+1,//此处 +1 是为了在最后添加一个progress
  itemBuilder: (content,index){
    return _listItem(index);
  },
  controller: _scrollController,)

_listItem 方法代码为:

Widget _listItem(int index){
  if(index==_listData.length){//最后一个item为加载条
    return Center(
      child: Container(
        height: 20,
        width: 20,
        margin: EdgeInsets.all(5),
        child: CircularProgressIndicator(//加载条
          backgroundColor: Colors.green,
        ),
      ),
    );
  }else{
    RstData data=_listData[index];
    return GestureDetector(
      child: IndexItem(rstData: data,),
      onTap:(){ _itemClick(data);},
    );
  }
}

上拉的方法已经完成了,下面开始添加下拉刷新的部分。

flutter 提供 RefreshIndicator 来帮助我们实现这个功能。RefreshIndicator 必须要实现 onRefresh 属性方法,注意此处的onRefresh 方法是有返回值的,我们用 Future<Null> 作为返回值即可。

RefreshIndicator(
    child: ListView.builder(
      itemCount: _listData==null?0:_listData.length+1,//此处 +1 是为了在最后添加一个progress
      itemBuilder: (content,index){
        return _listItem(index);
      },
      controller: _scrollController,
    ), 
    onRefresh: _onRefresh
),

当使用上拉下拉时,就会涉及到页码的问题,即当前加载到了第几页,所以我们需要定义一个 int 类型的pager 来记录,并对获取到的数据进行一定的操作。

void _getGuangdiuIndexData(FormData formData) async{
  String url="getlist.php";
  Response resp=await HttpUtil().post(url,data: formData).then((resp){
    RespResult respResult=RespResult.fromJson(resp.data);
    var data=_listData;
    if(pager==1){// 只加载获取到的数据
      data=respResult.data;
    }else{
      data.addAll(respResult.data);//将获取的数据追加到原有数据的后面
    }
    setState(() {
      _listData=data;
    });
  });
}

由于侧边栏的存在以及侧边栏的点击涉及到列表数据的分类,所以需要增加 type以及typeValue对数据类型加以记录。

下面给出整个修改后的代码:

import 'package:flutter/material.dart';
import 'table/resp_result.dart';
import 'util/http_util.dart';
import 'package:dio/dio.dart';
import 'widget/index_item.dart';
import 'ItemInfoDetail.dart';
import 'configure/type.dart';

class HomeIndex extends StatefulWidget{
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return IndexWidget();
  }
}

class IndexWidget extends State<HomeIndex>{

  List<RstData> _listData;//设置列表数据
  GlobalKey<ScaffoldState> _globalKeyState=GlobalKey();
  String title="首页";
  ScrollController _scrollController=ScrollController();
  String type="";
  String typeValue="";
  int pager=1;

  IndexWidget(){
    _scrollController.addListener((){
        var pix=_scrollController.position.pixels;
        var max=_scrollController.position.maxScrollExtent;
        if(pix==max){
          //已下拉到底部,调用加载更多方法
          _loadMoreData();
        }
    });
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
        appBar: AppBar(
          title: Text(title,style: TextStyle(color: Colors.white),),
          leading: IconButton(
              icon: Icon(Icons.menu,color: Colors.white,),
              onPressed: (){
                _changeDrawer();//切换drawer的关闭打开
              }),
          backgroundColor: Colors.green[500],
        ),
        body: _listInfos(),
    );
  }

  //list列表
  Widget _listInfos(){
    return Scaffold(
      key: _globalKeyState,
      body: RefreshIndicator(
          child: ListView.builder(
            itemCount: _listData==null?0:_listData.length+1,//此处 +1 是为了在最后添加一个progress
            itemBuilder: (content,index){
              return _listItem(index);
            },
            controller: _scrollController,
          ),
          onRefresh: _onRefresh
      ),
      drawer: _setDrawerList(),
    );
  }

  //item
  Widget _listItem(int index){
    if(index==_listData.length){
      return Center(
        child: Container(
          height: 20,
          width: 20,
          margin: EdgeInsets.all(5),
          child: CircularProgressIndicator(
            backgroundColor: Colors.green,
          ),
        ),
      );
    }else{
      RstData data=_listData[index];
      return GestureDetector(
        child: IndexItem(rstData: data,),
        onTap:(){ _itemClick(data);},
      );
    }
  }

  //切换侧边栏的打开与关闭
  void _changeDrawer(){
    if(_globalKeyState.currentState.isDrawerOpen){
      _globalKeyState.currentState.openEndDrawer();
    }else{
      _globalKeyState.currentState.openDrawer();
    }
  }

  //侧边栏列表
  Widget _setDrawerList(){
    var type=index_type;
    return Container(
      width: 200,
      color: Colors.white,
      child: ListView.builder(
        itemCount: type.length,
        itemBuilder: (context,index){
          Map<String,String> map=type[index];
          return _drawerItem(map);
        },
      ),
    );
  }
  //侧边栏 item
  Widget _drawerItem(Map<String,String> map){
    if(map["title"]=="=="){
      return Container(
        height: 1,
        decoration: BoxDecoration(
          color: Colors.black54,
        ),
      );
    }else{
      return GestureDetector(
        child: Container(
          height: 35,
          color: Colors.white,
          padding: EdgeInsets.only(left: 5),
          alignment: Alignment.centerLeft,
          child: Text(map["title"]),
        ),
        onTap: (){
          _drawerItemClick(map);
        },
      );
    }
  }
  //侧边栏点击事件
  void _drawerItemClick(Map<String,String> map){
    FormData formData=FormData();
    if(_listData!=null && _listData.length>0){
      formData.add("markid", "${_listData[0].id}");
    }else {
      formData.add("markid", "5685521");
    }
    if(map["type"]=="all"){
      setState(() {
        title = "首页";
      });
      type="";
    }else {
      setState(() {
        title = "首页-${map["title"]}";
      });
      type=map["type"];
      if (type == "hot") {
        typeValue="1";
        formData.add("onlyhots", typeValue);
      } else if (type == "mall") {
        typeValue=map["mall"];
        formData.add("mall", typeValue);
      } else if (type == "cate") {
        typeValue=map["cate"];
        formData.add("cate", typeValue);
      }
    }
    pager=1;//页码归一
    _getGuangdiuIndexData(formData);
    _changeDrawer();
  }

  void _itemClick(RstData data){
    Navigator.push(context, MaterialPageRoute(builder: (cx)=>ItemInfoDetail(id: data.id,title: data.title,)));
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _onRefresh();
  }

  //加载更多
  void _loadMoreData(){
    FormData formData=FormData();
    if(_listData!=null && _listData.length>0){
      formData.add("sinceid", _listData[_listData.length-1].id);
    }else{
      formData.add("sinceid", "5685521");
    }
    if(type!=""){
      formData.add(type, typeValue);
    }
    pager++;
    _getGuangdiuIndexData(formData);
  }

  //下拉刷新
  Future<Null> _onRefresh() async {
    pager=1;
    FormData formData=FormData();
    if(_listData!=null && _listData.length>0){
      formData.add("markid", _listData[0].id);
    }else {
      formData.add("markid", "5685521");
    }
    _getGuangdiuIndexData(formData);
    return null;
  }

  void _getGuangdiuIndexData(FormData formData) async{
    String url="getlist.php";
    Response resp=await HttpUtil().post(url,data: formData).then((resp){
      RespResult respResult=RespResult.fromJson(resp.data);
      var data=_listData;
      if(data==null){
        data=respResult.data;
      }else {
        if (pager == 1) {
          data.clear();
        }
        data.addAll(respResult.data);
      }
      setState(() {
        _listData=data;
      });
      if(pager==1){
        _scrollController.position.moveTo(0);//滑动到最顶端
      }
    });
  }

}

app运行效果图:

  

哈哈 gif 没做好,有点糊。

码云 git 下载

kotlin版本的源码下载:git下载地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值