Flutter 布局之 ListView

356 阅读3分钟

1 ListView 简介

ListView 是一个列表组件, 继承于 BoxScrollView, 是可以垂直或者水平滑动的,

ListView 的继承链:
ListView -> BoxScrollView -> ScrollView -> StatelessWidget

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(title: const Text('ListView Demo'),),
        body: Center(
          child: createListView(),
        ),
      ),
    );
  }
}

2 ListView 构造

2.1 ListView 同名构造函数

我们用同名构造函数来写一个简单的列表, 需要传入一个 Widget 数组, 数组中的每一条 item 都是一个 Widget, 这种方法适合静态页面, 数据不发生变化的情况; 同时可以设置一个内边距, 这个内边距是 ListView 内部的, 而不是每一条 item 的,

  Widget createListView() {
    return Container(
      color: Colors.yellow,
      child: ListView(
        scrollDirection: Axis.horizontal,
        padding: const EdgeInsets.all(10),
        children: [
          Container(
            color: Colors.grey,
            width: 100,
            height: 100,
            child: const Text(
              'C 1',
              style: TextStyle(fontSize: 20,),
            ),
          ),
          Container(
            color: Colors.green,
            width: 100,
            height: 100,
            child: const Text(
              'C 2',
              style: TextStyle(fontSize: 20,),
            ),
          ),
          Container(
            color: Colors.blue,
            width: 100,
            height: 100,
            child: const Text(
              'C 3',
              style: TextStyle(fontSize: 20,),
            ),
          ),
          Container(
            color: Colors.grey,
            width: 100,
            height: 100,
            child: const Text(
              'C 4',
              style: TextStyle(fontSize: 20,),
            ),
          ),
          Container(
            color: Colors.green,
            width: 100,
            height: 100,
            child: const Text(
              'C 5',
              style: TextStyle(fontSize: 20,),
            ),
          ),
          Container(
            color: Colors.blue,
            width: 100,
            height: 100,
            child: const Text(
              'C 6',
              style: TextStyle(fontSize: 20,),
            ),
          ),
          Container(
            color: Colors.grey,
            width: 100,
            height: 100,
            child: const Text(
              'C 7',
              style: TextStyle(fontSize: 20,),
            ),
          ),
          Container(
            color: Colors.green,
            width: 100,
            height: 100,
            child: const Text(
              'C 8',
              style: TextStyle(fontSize: 20,),
            ),
          ),
          Container(
            color: Colors.blue,
            width: 100,
            height: 100,
            child: const Text(
              'C 9',
              style: TextStyle(fontSize: 20,),
            ),
          ),
        ],
      ),
    );
  }
  • 垂直滚动效果, 此时 item 设置宽度是不生效的, 只有高度有效.

QQ20211012-115403-HD.gif

  • 水平滚动效果, 此时 item 设置高度是不生效的, 只有宽度生效.

QQ20211012-120234-HD.gif

2.1 ListView.builder

builder 这个构造函数, 有一个必传参数 itemBuilder, 这个参数的类型是 IndexedWidgetBuilder 这个类型是一个函数; 另外一个常用参数是 itemCount, 就是列表中 cell 的数量, 如果不传会显示无数个.

  • IndexedWidgetBuilder IndexedWidgetBuilder 和我们 OC UItableViewcellForRow 方法非常像, 传入一个 context, 一个下标 index, 函数返回一个 Widget, 列表根据数量循环调用.
typedef IndexedWidgetBuilder = Widget Function(BuildContext context, int index);

我们按照 IndexedWidgetBuilder 这个类型, 自定义一个函数 Widget cellForRow(BuildContext context, int index), 作为 itemBuilder 参数传入 builder, 数量来个20, 来看一下效果, 这个就只展示垂直滚动的, 水平滚动就不展示了, 只是方向不同而已.

  Widget createListView() {
    return Container(
      color: Colors.white,
      child: ListView.builder(
        itemCount: 20,
        itemBuilder: cellForRow,
      ),
    );
  }


  // 自定义一个 cell
  Widget cellForRow(BuildContext context, int index) {
    return Column(
      children: [
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Container(
              color: Colors.grey,
              padding: const EdgeInsets.fromLTRB(10, 10, 10, 10),
              width: 120,
              height: 170,
              child: Image.asset(
                'images/icon_img.png', //200 * 300
              ),
            ),
            Container(
                margin: const EdgeInsets.only(right: 10),
                color: Colors.blue,
                child: const SizedBox(
                  width: 50,
                  height: 80,
                )
            )
          ],
        ),
        Container(
          height: 1,
          color: Colors.yellow,
          child: Row(
            children: [
              Container(
                width: 10,
                color: Colors.white,
              ),
            ]
          ),
        )
      ],
    );
}

QQ20211012-163013-HD.gif

2.2 ListView.separated

separated 是一个带分割线的列表, 可以单独设置分割线, 在最后一个 cell 下边是没有的, 也就是说分割线的数量比 cell 数量少 1, 除了 itemBuilder 是必传参数, 还有 itemCountseparatorBuilder 也是必传参数, separatorBuilderitemBuilder 类型相同.

Widget createListView() {
    return Container(
      color: Colors.white,
      child: ListView.separated(
        itemCount: 20,
        itemBuilder: cellForRow,
        separatorBuilder: separatorForRow,
      ),
    );
  }

  // 自定义分割线
  Widget separatorForRow(BuildContext context, int index) {
    return Container(
      height: 2,
      color: Colors.red,
    );
  }

  Widget cellForRow(BuildContext context, int index) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        Container(
          color: Colors.grey,
          padding: const EdgeInsets.fromLTRB(10, 10, 10, 10),
          width: 120,
          height: 170,
          child: Image.asset(
            'images/icon_img.png', //200 * 300
          ),
        ),
        Container(
            margin: const EdgeInsets.only(right: 10),
            color: Colors.blue,
            child: const SizedBox(
              width: 50,
              height: 80,
            )
        )
      ],
    );
}

QQ20211012-174051-HD.gif

2.3 ListView.custom

待更新...