可延遲載入的 ListView


在〈捲動單一元件 SingleChildScrollView〉中第二個範例,也可以使用 ListView 如下撰寫:

import 'package:flutter/material.dart';

void main() => runApp(
  MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: Text('Openhome.cc')),
      body: Body(),
    )
  )
);

class Body extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _Body();
}

class _Body extends State<Body> {
  final scrollController = ScrollController();
  @override
  void dispose() {
    scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    var children = List<Widget>();
    for(var i = 0; i < 20; i++) {
      children.add(RawMaterialButton(
        child: Text('$i'),
        onPressed: () {
          print(scrollController.offset);
        },
        fillColor: Colors.lightGreen,
        splashColor: Colors.red,
      ));
    }

    return Center(
      child: Container(
        height: 50,
        child: ListView( // 使用 ListView
          controller: scrollController,
          scrollDirection: Axis.horizontal,
          children: children,
        ),
      ),
    );
  }
}

這樣就能獲得延遲載入的效果了嗎?並不會!畢竟你還是在 build 時,建立了 ListView 全部的子元件,以上的使用方式,跟使用 SingleChildScrollView 時,其實沒有什麼差別。

如果想要有延遲載入的效果,可以透過 ListView.builder 建構式,指定 itemBuilder 來定義捲動時元件的生成方式。例如:

import 'package:flutter/material.dart';

void main() => runApp(
  MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: Text('Openhome.cc')),
      body: ListView.builder(
        itemCount: 50,
        itemBuilder: (BuildContext context, int i) {
          print(i);
          return ListTile(
            title: RawMaterialButton(
              child: Text('$i'),
              fillColor: Colors.lightGreen,
            )
          );
        }
      ),
    )
  )
);

操作時的效果如下,可以看到一開始先建立了索引 0 到 10,接著依捲動按需地建立元件,由於 itemCount 指定為 50,因此最多只會到索引 49,如果 itemCount 指定為 null,表示元件長度不受限:

可延遲載入的 ListView

如果要自訂分隔元件,可以透過 ListView.separated 建構式,指定 separatorBuilder 來定義分隔元件:

import 'package:flutter/material.dart';

void main() {
  Widget blueDivider = Divider(
    thickness: 4,
    color: Colors.blue
  );
  Widget redDivider = Divider(
    thickness: 4,
    color: Colors.green
  );
  runApp(
    MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Openhome.cc')),
        body: ListView.separated(
          itemCount: 50,
          itemBuilder: (BuildContext context, int i) {
            return ListTile(
              title: RawMaterialButton(
                child: Text('$i'),
              )
            );
          },
          separatorBuilder: (BuildContext context, int i) {
            return i % 2 == 0 ? blueDivider : redDivider;
          }
        ),
      )
    )
  );
}

執行後的效果如下:

可延遲載入的 ListView

話說範例中使用了 ListTile,這是做列表時還蠻常用的小元件,可以設定圖示、子標題等內容,例如:

import 'package:flutter/material.dart';

void main() {
  Widget blueDivider = Divider(
    thickness: 4,
    color: Colors.blue
  );
  Widget redDivider = Divider(
    thickness: 4,
    color: Colors.green
  );
  runApp(
    MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Openhome.cc')),
        body: ListView.separated(
          itemCount: 30,
          itemBuilder: (BuildContext context, int i) {
            return ListTile(
              leading: Icon(
                IconData(59677 + i, fontFamily: 'MaterialIcons')
              ),
              title: Text('$i'),
              subtitle: Text('圖示 ${59677 + i}'),
            );
          },
          separatorBuilder: (BuildContext context, int i) {
            return i % 2 == 0 ? blueDivider : redDivider;
          }
        ),
      )
    )
  );
}

執行後的效果如下:

可延遲載入的 ListView