Kodokon kodokon.com

รายการประสิทธิภาพสูงด้วย ListView.builder

แสดงรายการที่มีไอเทมนับพันโดยไม่หน่วง ด้วย ListView.builder, ListTile และการแยกไอเทมออกเป็นวิดเจ็ตของตัวเอง

8 นาที · 3 คำถาม

เปิดบทเรียนนี้ใน Kodokon

แอปในโลกจริงเกือบทุกครั้งจะแสดงรายการ ไม่ว่าจะเป็นข้อความ สินค้า หรือรายชื่อผู้ติดต่อ คอนสตรักเตอร์ ListView(children: ...) ที่คุณรู้จักอยู่แล้วจะสร้างลูก ทั้งหมด ในคราวเดียว ซึ่งไม่มีปัญหาสำหรับสิบไอเทม แต่กลายเป็นหายนะเมื่อมีเป็นพัน ListView.builder จะสร้างเฉพาะไอเทมที่อยู่ใกล้พื้นที่ที่มองเห็น แบบตามความต้องการ ในขณะที่คุณเลื่อนดู

DART
import 'package:flutter/material.dart';

class ContactsScreen extends StatelessWidget {
  const ContactsScreen({super.key, required this.names});

  final List<String> names;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Contacts')),
      body: ListView.builder(
        itemCount: names.length,
        itemBuilder: (context, index) {
          return ListTile(
            leading: const Icon(Icons.person),
            title: Text(names[index]),
          );
        },
      ),
    );
  }
}
สร้างเฉพาะไอเทมที่มองเห็นเท่านั้น แม้จะมีชื่อ 10,000 รายชื่อก็ตาม

พารามิเตอร์สองตัวคือหัวใจของกลไกทั้งหมด itemCount ประกาศจำนวนไอเทมทั้งหมด และ itemBuilder สร้างไอเทมที่ดัชนีหนึ่ง ๆ เฉพาะตอนที่มันใกล้จะเข้ามาในหน้าจอ สำหรับเนื้อหาของแต่ละแถว ListTile คือพันธมิตร Material ของคุณ โดยมี leading, title, subtitle และ trailing ช่วยจัดโครงสร้างของแถว และ onTap ให้เอฟเฟกต์หมึกกระเพื่อมเมื่อสัมผัส แต่ทันทีที่ไอเทมเริ่มใหญ่ขึ้น อย่าปล่อยให้มันบวมอยู่ใน itemBuilder จงแยกมันออกไปเป็น วิดเจ็ตของตัวเอง

DART
import 'package:flutter/material.dart';

class ContactTile extends StatelessWidget {
  const ContactTile({
    super.key,
    required this.name,
    required this.onTap,
  });

  final String name;
  final VoidCallback onTap;

  @override
  Widget build(BuildContext context) {
    return ListTile(
      leading: CircleAvatar(child: Text(name[0])),
      title: Text(name),
      trailing: const Icon(Icons.chevron_right),
      onTap: onTap,
    );
  }
}
ไอเทมของรายการที่ถูกแปลงเป็นวิดเจ็ตที่นำกลับมาใช้ซ้ำและทดสอบได้

ไอเทมที่แยกออกมาสามารถทดสอบแบบแยกเดี่ยวได้ นำกลับไปใช้ในหน้าจออื่นได้ และทำให้ itemBuilder เรียบง่ายลง ยังมีความต้องการหนึ่งที่พบบ่อยเหลืออยู่ นั่นคือตัวคั่น ListView.separated ทำงานเหมือน builder แต่มีพารามิเตอร์ตัวที่สามชื่อ separatorBuilder ซึ่งจะถูกเรียกระหว่างไอเทมแต่ละคู่ โดยสมาชิกตัวสุดท้ายจะไม่มีตัวคั่นตามหลังเลย

DART
import 'package:flutter/material.dart';

class ContactList extends StatelessWidget {
  const ContactList({super.key, required this.names});

  final List<String> names;

  @override
  Widget build(BuildContext context) {
    return ListView.separated(
      itemCount: names.length,
      separatorBuilder: (context, index) {
        return const Divider(height: 1);
      },
      itemBuilder: (context, index) {
        return ContactTile(
          name: names[index],
          onTap: () => debugPrint(names[index]),
        );
      },
    );
  }
}
separatorBuilder แทรก Divider ระหว่างไอเทมแต่ละคู่

ทดสอบความรู้

ตรวจสอบว่าคุณจำประเด็นสำคัญของบทเรียนนี้ได้ครบถ้วน

  1. ทำไมจึงควรเลือก ListView.builder มากกว่า ListView(children: ...) สำหรับรายการที่มี 500 ไอเทม?
    • มันเรียงลำดับไอเทมให้โดยอัตโนมัติ
    • มันสร้างเฉพาะไอเทมที่มองเห็นเท่านั้น แบบตามความต้องการ
    • มันแคชรูปภาพ
    • มันเพิ่มตัวคั่นให้โดยอัตโนมัติ
  2. คอนสตรักเตอร์ใดแทรกวิดเจ็ตระหว่างไอเทมแต่ละคู่?
    • ListView.divided
    • ListView.builder
    • ListView.separated
  3. การแยกไอเทมของรายการออกเป็นคลาสวิดเจ็ตของตัวเองมีประโยชน์อย่างไร?
    • มันจำเป็นเพื่อให้ ListView.builder คอมไพล์ได้
    • มันเปิดใช้ const แยกการรีบิลด์ให้เป็นอิสระ และทำให้นำกลับมาใช้ซ้ำได้ง่ายขึ้น
    • มันทำให้รายการเลื่อนได้
    • มันช่วยให้คุณไม่ต้องเขียน itemCount