[- Flutter 必備 -] ListView的使用

張風捷特烈發表於2019-08-03

1.ListView 的基本使用

ListView 是一個盛放多個孩子的容器。我們從下面的例子開始介入:

[- Flutter 必備 -] ListView的使用


1.1:三個構造
  • 使用ListView構造方法

和Flex,Wrap類似,將子元素一個一個按順序排列。

var caverStyle= TextStyle(fontSize: 18, shadows: [//文字樣式
  Shadow(
      color: Colors.white, offset: Offset(-0.5, 0.5), blurRadius: 0)
]);
var show = ListView(//ListView的構造方法
  padding: EdgeInsets.all(8.0),//邊距
  children: <Widget>[//孩子們
    Container(
      height: 50,
      color: Color(0xffff0000),
      child: Center(child: Text('紅色',style: caverStyle,)),
    ),
    Container(
      height: 50,
      color: Color(0xffFFFF00),
      child: Center(child: Text('黃色',style: caverStyle,)),
    ),
    Container(
      height: 50,
      color: Color(0xff00FF00),
      child: Center(child: Text('綠色',style: caverStyle,)),
    ),
    Container(
      height: 50,
      color: Color(0xff0000FF),
      child: Center(child: Text('藍色',style: caverStyle,)),
    ),
  ],
);
複製程式碼

  • 使用ListView.builder方法構造

使用builder方法對List或Map資料進行批量生成。

const colorMap = {//資料來源
  0xffff0000: "紅色",
  0xffFFFF00: "黃色",
  0xff00FF00: "綠色",
  0xff0000FF: "藍色",
};
var caverStyle= TextStyle(fontSize: 18, shadows: [//文字樣式
  Shadow(
      color: Colors.white, offset: Offset(-0.5, 0.5), blurRadius: 0)
]);
var show = ListView.builder(//使用builder方法進行構造
    padding: EdgeInsets.all(8.0),
    itemCount: colorMap.length,//條目的個數
    itemBuilder: (BuildContext context, int index) {//條目構造器
      return Container(
        height: 50,
        color: Color(colorMap.keys.toList()[index]),
        child: Center(
            child: Text(
          '${colorMap.values.toList()[index]}',
          style: caverStyle,
        )),
      );
    });
複製程式碼

  • 使用ListView.separated方法構造

separated方法和builder類似,但是可以通過separatorBuilder屬性建立分隔線。

const colorMap = {//資料來源
  0xffff0000: "紅色",
  0xffFFFF00: "黃色",
  0xff00FF00: "綠色",
  0xff0000FF: "藍色",
};
var caverStyle = TextStyle(fontSize: 18, shadows: [//文字樣式
  Shadow(color: Colors.white, offset: Offset(-0.5, 0.5), blurRadius: 0)
]);
var show = ListView.separated(//使用separated方法進行構造
  padding: EdgeInsets.all(8.0),
    itemBuilder: (context, index) {//條目構造器
      return Container(
        height: 50,
        color: Color(colorMap.keys.toList()[index]),
        child: Center(
            child: Text(
          '${colorMap.values.toList()[index]}',
          style: caverStyle,
        )),
      );
    },
    separatorBuilder: (context, index) {//分隔線構造器
      return Container();
    },
    itemCount: colorMap.length);
複製程式碼

2.ListView進階使用

2.1:完成條目的封裝

比較簡單,我也不分析這麼佈局了,直接上程式碼。

[- Flutter 必備 -] ListView的使用

  • 資訊描述類
class PoemItem {
  ImageProvider image;//圖片
  var title;//標題
  var author;//作者
  var summary;//摘要
  PoemItem({this.image, this.title, this.author, this.summary});
}
複製程式碼
  • 條目的封裝
typedef OnItemClickListener = void Function();
class PoemItemView extends StatelessWidget {
  final PoemItem data;
  final OnItemClickListener onItemClickListener;
  PoemItemView({Key key, this.data, this.onItemClickListener})
      : super(key: key);
  @override
  Widget build(BuildContext context) {
    var headIcon = Container(//左邊頭部
        decoration: BoxDecoration(
          color: Colors.white,
          shape: BoxShape.circle,
          boxShadow: [
            BoxShadow(
              color: Colors.grey.withOpacity(0.3),
              offset: Offset(0.0, 0.0),
              blurRadius: 3.0,
              spreadRadius: 0.0,
            ),
          ],
        ),
        width: 70,
        height: 70,
        child: Padding(
          padding: EdgeInsets.all(3),
          child: CircleAvatar(
            backgroundImage: data.image,
          ),
        ));
    var center = Column(//中間介紹
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.start,
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        Text(data.title,
            style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
        Padding(
          padding: EdgeInsets.only(top: 8),
          child: Text(
            "作者:${data.author}",
            style: TextStyle(color: Colors.grey, fontSize: 12),
          ),
        ),
      ],
    );
    var summary = Text(//尾部摘要
      data.summary,
      maxLines: 3,
      overflow: TextOverflow.ellipsis,
      style: TextStyle(color: Colors.grey, fontSize: 12),
    );
    var item = Row(//條目拼合
      mainAxisAlignment: MainAxisAlignment.start,
      children: <Widget>[
        SizedBox(width: 10),
        headIcon,
        Padding(
          padding: EdgeInsets.symmetric(horizontal: 20),
          child: center,
        ),
        Expanded(
          child: summary,
        ),
        SizedBox(width: 10),
      ],
    );
    var result = Card(//卡片化+事件監聽
        elevation: 5,
        child: InkWell(
            onTap: onItemClickListener,
            child: Padding(
              padding: EdgeInsets.all(10),
              child: item,
            )));
    return result;
  }
}
複製程式碼
2.2:ListView的使用

在構造器構造條目時,使用資料對條目進行資料填充,側達到資料展示效果

[- Flutter 必備 -] ListView的使用

var data = <PoemItem>[];
for (var i = 0; i < 20; i++) {
  data.add(PoemItem(
      image: AssetImage("images/wy_200x300.jpg"),
      title: "$i:以夢為馬",
      author: "海子",
      summary: "我要做遠方的忠誠的兒子,和物質的短暫情人,和所有以夢為馬的詩人一樣,我不得不和烈士和小丑走在同一道路上"));
}
var show = ListView.builder(
    padding: EdgeInsets.all(8.0),
    itemCount: data.length, //條目的個數
    itemBuilder: (BuildContext context, int index) {
      return PoemItemView(//資料填充條目
        data: data[index],
        onItemClickListener: () {//事件響應
          print(index);
        },
      );
    });
複製程式碼

2.3:分隔線的新增

這裡現將Card元件去掉。separated可以很輕鬆的實現下劃線分隔,並且容易改變長短,顏色
如果你願意,也可以去定義分隔的元件,專門作為分隔線。而且還能使用索引進行個性化設計

[- Flutter 必備 -] ListView的使用

var data = <PoemItem>[];
for (var i = 0; i < 20; i++) {
  data.add(PoemItem(
      image: AssetImage("images/wy_200x300.jpg"),
      title: "$i:以夢為馬",
      author: "海子",
      summary: "我要做遠方的忠誠的兒子,和物質的短暫情人,和所有以夢為馬的詩人一樣,我不得不和烈士和小丑走在同一道路上"));
}
var show = ListView.separated(
    padding: EdgeInsets.all(8.0),
    itemCount: data.length, //條目的個數
    itemBuilder: (BuildContext context, int index) {
      return PoemItemView(//資料填充條目
        data: data[index],
        onItemClickListener: () {//事件響應
          print(index);
        },
      );
    },
    separatorBuilder: (BuildContext context, int index) {
  return Padding(padding: EdgeInsets.only(left: 90),child: Divider(height: 1,color: Colors.orangeAccent,),);
},
    );
複製程式碼

2.4:一個ListView中不同的條目樣式。

在抽取條目時,可以定義一個type屬性,條目樣式由Widget自身決定。

[- Flutter 必備 -] ListView的使用

var random = Random();
var data = <ChartItem>[];
var strs = [
  "我是要成為程式設計之王的男人,你是要成為程式設計之王的女人",
  "憑君莫話封侯事,一將功成萬骨枯。你覺得如何?",
  "在蒼茫的大海上,狂風摺積著烏雲,在烏雲和大海之間,海燕像黑色的閃電,在高傲的飛翔。"
];
for (var i = 0; i < 20; i++) {
  data.add(ChartItem(
      headIcon: AssetImage(
          i.isEven ? "images/wy_200x300.jpg" : "images/icon_head.png"),
      text: strs[random.nextInt(strs.length)],
      type: i.isEven ? ChartType.left : ChartType.right));
}
var show = ListView.builder(
  itemCount: data.length, //條目的個數
  itemBuilder: (BuildContext context, int index) {
    return ChartWidget(//資料填充條目
      chartItem: data[index],
    );
  },
);
複製程式碼

比如這裡的ChartItem,本身定義了ChartType,在實現ChartWidget的時候,根據型別進行生成相應Widget,從而達到多種樣式的效果。

enum ChartType { right, left }

class ChartItem {
  ImageProvider headIcon;
  double maxWith;
  ChartType type;
  String text;

  ChartItem({this.headIcon, this.text,this.maxWith = 250,this.type=ChartType.right});
}

class ChartWidget extends StatelessWidget {
  final ChartItem chartItem;

  ChartWidget({Key key, this.chartItem}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    switch (chartItem.type) {
      case ChartType.right:
        return _buildRight();
        break;
      case ChartType.left:
        return _buildLeft();
        break;
    }
  }
複製程式碼

3.ListView滑動控制器(上拉重新整理和下拉更新)

3.1:滑動控制器ScrollController的使用
class _ListViewPageState extends State<ListViewPage> {
  ScrollController _scrollController = ScrollController();//定義變數及初始化

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(() {//新增監聽
     print("滑動了:${_scrollController.position.pixels},
     離頂部高:${_scrollController.position.maxScrollExtent}");
    });
  }

  @override
  void dispose() {
    _scrollController.dispose();//銷燬控制器
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
   //略同...
    var show = ListView.builder(
        controller: _scrollController,  //使用ScrollController
          //略同...
        );
  }
}
複製程式碼

3.2:上拉重新整理

內建元件:RefreshIndicator,包裹一下即可

[- Flutter 必備 -] ListView的使用

  return RefreshIndicator(child: show, onRefresh: _onRefresh//使用RefreshIndicator
}

bool isLoading = false;
Future<void> _onRefresh() async {//下拉重新整理,非同步延遲三秒
  if (isLoading) {
    return;
  }
  await Future.delayed(Duration(seconds: 3), () {
    setState(() {
      isLoading = false;
    });
  });
}
複製程式碼

3.3:下拉載入更多

[- Flutter 必備 -] ListView的使用

_scrollController.addListener(() {
  if (_scrollController.position.pixels ==//說明滑到底部
      _scrollController.position.maxScrollExtent) {
    _loadMore();
  }
});

bool isLoaded = false;

Future<void> _loadMore() async {
  await Future.delayed(Duration(seconds: 3), () {
    setState(() {
        isLoaded=true;
    });
  });
}

var show = ListView.builder(
    controller: _scrollController,
    itemCount: data.length + 1, //條目的個數
    itemBuilder: (BuildContext context, int index) {
      if (index == data.length) {
        return Offstage(//使用Offstage控制顯隱
            offstage: isLoaded,
            child: Padding(
              padding: EdgeInsets.all(8.0),
              child: Center(
                child: CircularProgressIndicator(),
              ),
            ));
      } else {
        return ChartWidget(
          //資料填充條目
          chartItem: data[index],
        );
      }
    });
複製程式碼

4. physics屬性

physics對應ScrollPhysics類,其下有6個子類

[- Flutter 必備 -] ListView的使用

  • FixedExtentScrollPhysics()NeverScrollableScrollPhysics
ListView無法滑動
複製程式碼

  • PageScrollPhysics()
會根據你的滑動來自動調節,比如你滑的距離小,則自動還原,
到達一定大小,也會自動下滑一個條目
複製程式碼

  • BouncingScrollPhysics()
頂底會有而外空間,讓富有彈性
複製程式碼

[- Flutter 必備 -] ListView的使用


  • ClampingScrollPhysics()AlwaysScrollableScrollPhysics()
頂底會有藍色陰影
複製程式碼

[- Flutter 必備 -] ListView的使用

另外還有reverse屬性控制資料是否反序,scrollDirection控制ListView的方向


結語

本文到此接近尾聲了,如果想快速嚐鮮Flutter,《Flutter七日》會是你的必備佳品;如果想細細探究它,那就跟隨我的腳步,完成一次Flutter之旅。
另外本人有一個Flutter微信交流群,歡迎小夥伴加入,共同探討Flutter的問題,本人微訊號:zdl1994328,期待與你的交流與切磋。

相關文章