Flutter 流式布局列表實例+上拉加載


頁面變化的幾種方式:

一、StatefulWidget的setState形式

先聲明兩個變量。

int page = 1;
List<Map> list = [];

寫了一個方法,獲取數據:

void _getHotGoods(){
    var formData = {'page':page};
    request('post', 'homePageBelowConten',formData: formData).then((val){
      var data = json.decode(val.toString());
      List<Map> newList = (data['data'] as List).cast();
      setState(() {
        list.addAll(newList); //新的列表加到老的列表之上
        page ++;
      });
    });
  }

然后實現頁面布局

標題:

//火爆專區標題 變量的形式
  Widget hotTitle = Container(
    margin: EdgeInsets.only(top: 10.0),
    alignment: Alignment.center,
    color: Colors.transparent, //透明背景色
    padding: EdgeInsets.all(5.0),
    child: Text('火爆專區'),
  );

每個子項:

//火爆專區子項 方法的形式
  Widget _wrapList(){
    if(list.length != 0){
      List<Widget> listWidget = list.map((val){ //把Map類型的List包裝成Widget,再放回List里,並賦值給流式布局
        return InkWell(
          onTap: (){},
          child: Container(
            width: ScreenUtil().setWidth(372),
            color: Colors.white,
            padding: EdgeInsets.all(5.0),
            margin: EdgeInsets.only(bottom: 3.0),
            child: Column(
              children: <Widget>[
                Image.network(val['image'],width:ScreenUtil().setWidth(370)),
                Text(
                  val['name'],
                  maxLines: 1,
                  overflow:TextOverflow.ellipsis,
                  style:TextStyle(color:Colors.blueGrey,fontSize:ScreenUtil().setSp(26)),
                ),
                Row(
                  children: <Widget>[
                    Text('¥${val['mallPrice']}'),
                    Text(
                      '¥${val['price']}',
                      style: TextStyle(color: Colors.black26,decoration: TextDecoration.lineThrough),
                    )
                  ],
                ),
              ],
            ),
          ),
        );
      }).toList(); //把Map類型的List包裝成Widget,再放回List里,並賦值給流式布局

      return Wrap( //返回流式布局
        spacing: 2, //每行2列
        children: listWidget,
      );
    }else{
      return Text(''); //沒有數據時返回空
    }
  }

組合在一起:

//火爆專區組合
  Widget _hotGoods(){
    return Container(
      child: Column(
        children: <Widget>[
          hotTitle,
          _wrapList(),
        ],
      ),
    );
  }

完整代碼:

import 'package:flutter/material.dart';
import '../service/service_method.dart';
import 'dart:convert';
import 'package:flutter_screenutil/flutter_screenutil.dart';

class CategoryPage extends StatefulWidget {

  _CategoryPageState createState() => _CategoryPageState();
}

class _CategoryPageState extends State<CategoryPage> {
  int page = 1;
  List<Map> list = [];

  @override
  void initState() { 
    super.initState();
    _getHotGoods(); //火爆專區獲取值
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child:Column(
        children: <Widget>[
           _hotGoods(),
         ],
      ),      
    );
  }

  //火爆商品接口
  void _getHotGoods(){
    var formData = {'page':page}; //Map類型
    request('post', 'homePageBelowConten',formData: formData).then((val){
      var data = json.decode(val.toString());
      List<Map> newList = (data['data'] as List).cast();
      setState(() {
        list.addAll(newList); //新的列表加到老的列表之上
        page ++;
      });
    });
  }
  //火爆專區標題 變量的形式
  Widget hotTitle = Container(
    margin: EdgeInsets.only(top: 10.0),
    alignment: Alignment.center,
    color: Colors.transparent, //透明背景色
    padding: EdgeInsets.all(5.0),
    child: Text('火爆專區'),
  );
  //火爆專區子項 方法的形式
  Widget _wrapList(){
    if(list.length != 0){
      List<Widget> listWidget = list.map((val){ //Map循環的形式:把Map類型的List包裝成Widget,再放回List里,並賦值給流式布局
        return InkWell(
          onTap: (){},
          child: Container(
            width: ScreenUtil().setWidth(372),
            color: Colors.white,
            padding: EdgeInsets.all(5.0),
            margin: EdgeInsets.only(bottom: 3.0),
            child: Column(
              children: <Widget>[
                Image.network(val['image'],width:ScreenUtil().setWidth(370)),
                Text(
                  val['name'],
                  maxLines: 1,
                  overflow:TextOverflow.ellipsis,
                  style:TextStyle(color:Colors.blueGrey,fontSize:ScreenUtil().setSp(26)),
                ),
                Row(
                  children: <Widget>[
                    Text('¥${val['mallPrice']}'),
                    Text(
                      '¥${val['price']}',
                      style: TextStyle(color: Colors.black26,decoration: TextDecoration.lineThrough),
                    )
                  ],
                ),
              ],
            ),
          ),
        );
      }).toList(); //Map循環的形式:把Map類型的List包裝成Widget,再放回List里,並賦值給流式布局

      return Wrap( //返回流式布局
        spacing: 2, //每行2列
        children: listWidget,
      );
    }else{
      return Text(''); //沒有數據時返回空
    }
  }
  //火爆專區組合
  Widget _hotGoods(){
    return Container(
      child: Column(
        children: <Widget>[
          hotTitle,
          _wrapList(),
        ],
      ),
    );
  }

}

flutter_easyrefresh插件:

EasyRefresh很容易就能在Flutter應用上實現下拉刷新以及上拉加載操作,它支持幾乎所有的Flutter控件,但前提是需要包裹成ScrollView。它的功能與Android的SmartRefreshLayout很相似,同樣也吸取了很多三方庫的優點。EasyRefresh中集成了多種風格的Header和Footer,但是它並沒有局限性,你可以很輕松的自定義。使用Flutter強大的動畫,甚至隨便一個簡單的控件也可以完成。EasyRefresh的目標是為Flutter打造一個強大,穩定,成熟的下拉刷新框架。

github:https://github.com/xuelongqy/flutter_easyrefresh

flutter_easyrefresh優點:

  • 能夠自定義酷炫的Header和Footer,也就是上拉和下拉的效果。
  • 更新及時,不斷在完善,錄課截至時已經是v1.2.7版本了。
  • 有一個輔導群,雖然文檔不太完善,但是有輔導群和詳細的案例。
  • 回掉方法簡單,這個具體可以看下面的例子。

引入依賴

直接在pubspec.yaml中的dependencies中進行引入,主要要用最新版本,文章中的版本不一定是最新版本。

flutter_easyrefresh: ^1.2.7

引入后,在要使用的頁面用import引入package,代碼如下:

import 'package:flutter_easyrefresh/easy_refresh.dart';

制作上拉加載效果

使用這個插件,要求我們必須是一個ListView,所以我們要改造以前的代碼,SingleChildScrollView改造成ListView。並添加loadMore,把_getHotGoods的內容粘貼過來,

_getHotGoods();就可以注掉了。
return EasyRefresh(
      child:ListView(
        children: <Widget>[
           _hotGoods(),
         ],
      ),
      loadMore: () async{
        var formData = {'page':page}; //Map類型
        await request('post', 'homePageBelowConten',formData: formData).then((val){
          var data = json.decode(val.toString());
          List<Map> newList = (data['data'] as List).cast();
          setState(() {
            list.addAll(newList); //新的列表加到老的列表之上
            page ++;
          });
        });
      },     
    );

現在運行已經可以看到效果了,不過還需要修改下。

自定義上拉加載效果

因為它自帶的樣式是藍色的,與我們的界面不太相符,所以我們改造一下,它的底部上拉刷新效果。如果你有興趣做出更炫酷的效果,可以自行查看一下Github,學習一下。

    refreshFooter:ClassicsFooter( //自定義上拉加載效果
           key:_footerKey,
           bgColor:Colors.white,
           textColor: Colors.blueGrey,
           moreInfoColor: Colors.blueGrey,
           showMore: true,
           noMoreText: '',
           moreInfo: '加載中', //加載時顯示的文字
           loadReadyText: '上拉加載...', //准備時顯示的文字
         ),

還要在上面定義Key:

GlobalKey<RefreshFooterState> _footerKey = new GlobalKey<RefreshFooterState>(); //定義key

完整代碼:

import 'package:flutter/material.dart';
import '../service/service_method.dart';
import 'dart:convert';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart';

class CategoryPage extends StatefulWidget {

  _CategoryPageState createState() => _CategoryPageState();
}

class _CategoryPageState extends State<CategoryPage> {
  int page = 1;
  List<Map> list = [];

  GlobalKey<RefreshFooterState> _footerKey = new GlobalKey<RefreshFooterState>(); //定義key

  @override
  void initState() { 
    super.initState();
    //_getHotGoods(); //火爆專區獲取值
  }

  @override
  Widget build(BuildContext context) {
    return EasyRefresh(
      refreshFooter:ClassicsFooter( //自定義上拉加載效果
           key:_footerKey,
           bgColor:Colors.white,
           textColor: Colors.blueGrey,
           moreInfoColor: Colors.blueGrey,
           showMore: true,
           noMoreText: '',
           moreInfo: '加載中', //加載時顯示的文字
           loadReadyText: '上拉加載...', //准備時顯示的文字
      ),
      child:ListView(
        children: <Widget>[
           _hotGoods(),
         ],
      ),
      loadMore: () async{
        var formData = {'page':page}; //Map類型
        await request('post', 'homePageBelowConten',formData: formData).then((val){
          var data = json.decode(val.toString());
          List<Map> newList = (data['data'] as List).cast();
          setState(() {
            list.addAll(newList); //新的列表加到老的列表之上
            page ++;
          });
        });
      },     
    );
  }

  //火爆商品接口
  // void _getHotGoods(){
  //   var formData = {'page':page}; //Map類型
  //   request('post', 'homePageBelowConten',formData: formData).then((val){
  //     var data = json.decode(val.toString());
  //     List<Map> newList = (data['data'] as List).cast();
  //     setState(() {
  //       list.addAll(newList); //新的列表加到老的列表之上
  //       page ++;
  //     });
  //   });
  // }
  //火爆專區標題 變量的形式
  Widget hotTitle = Container(
    margin: EdgeInsets.only(top: 10.0),
    alignment: Alignment.center,
    color: Colors.transparent, //透明背景色
    padding: EdgeInsets.all(5.0),
    child: Text('火爆專區'),
  );
  //火爆專區子項 方法的形式
  Widget _wrapList(){
    if(list.length != 0){
      List<Widget> listWidget = list.map((val){ //Map循環的形式:把Map類型的List包裝成Widget類型,再放回List里,並賦值給流式布局
        return InkWell(
          onTap: (){},
          child: Container(
            width: ScreenUtil().setWidth(372),
            color: Colors.white,
            padding: EdgeInsets.all(5.0),
            margin: EdgeInsets.only(bottom: 3.0),
            child: Column(
              children: <Widget>[
                Image.network(val['image'],width:ScreenUtil().setWidth(370)),
                Text(
                  val['name'],
                  maxLines: 1,
                  overflow:TextOverflow.ellipsis,
                  style:TextStyle(color:Colors.blueGrey,fontSize:ScreenUtil().setSp(26)),
                ),
                Row(
                  children: <Widget>[
                    Text('¥${val['mallPrice']}'),
                    Text(
                      '¥${val['price']}',
                      style: TextStyle(color: Colors.black26,decoration: TextDecoration.lineThrough),
                    )
                  ],
                ),
              ],
            ),
          ),
        );
      }).toList(); //Map循環的形式:把Map類型的List包裝成Widget類型,再放回List里,並賦值給流式布局

      return Wrap( //返回流式布局
        spacing: 2, //每行2列
        children: listWidget,
      );
    }else{
      return Text(''); //沒有數據時返回空
    }
  }
  //火爆專區組合
  Widget _hotGoods(){
    return Container(
      child: Column(
        children: <Widget>[
          hotTitle,
          _wrapList(),
        ],
      ),
    );
  }

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM