[Flutter] Future Builder와 ListView Builder 같이 사용하기

데이터베이스나 인터넷을 이용해서 Future 형식의 데이터를 받아 리스트를 화면에 보여줄 때 Future Builder를 이용하면 편리합니다. 이 글에서는 Future Builder와 ListView Builder를 같이 사용하는 방법에 대해서 알아보겠습니다.

처음 시작 코드

* 설명의 목록번호와 코드의 주석번호를 매칭하여 보시면 됩니다.

처음 시작 코드는 다음과 같습니다.

  1. 화면에 FloatingActionButton이 하나 있으며, 이 버튼을 클릭할 경우에 add_student로 명명된 페이지로 이동합니다.
import 'package:flutter/material.dart';

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
        centerTitle: true,
      ),
      body: Column(
        children: const [],
      ),
      //1
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {
          Navigator.pushNamed(context, '/add_student');
        },
        icon: const Icon(Icons.add),
        label: const Text('Add Student'),
      ),
    );
  }
}
시작하는 화면

Future Builder, ListView.Builder 사용하기

본 글에서는 이 화면에 drift 데이터베이스로부터 학생 목록을 얻어서 리스트로 나열하는 방법에 대해서 정리하고자 합니다.

  1. 일단 데이터베이스를 연결하고 이 연결로부터 Future 형식의 리스트를 얻어와야 하기 때문에 stateless widget을 stateful widget으로 변경합니다.
  2. Drift 패키지를 이용하여 미리 정의한 AppDb 클래스 타입의 _db를 late 형식으로 정의합니다.
  3. initState를 정의합니다. initState 내부에서 _db를 AppDb()로 초기화를 시켜줍니다.
  4. dispose 함수도 같이 정의해줍니다. 데이터베이스를 닫을 때에는 Close() 메서드를 이용합니다.
  5. FutureBuilder 위젯을 만듭니다. future와 builder 두 개의 인자를 전달해야 하는데 future는 drift 데이터베이스를 통해서 만든 get 함수를 적용합니다. builder의 경우 두 개의 인자를 전달받는데 이 중 snapshot으로부터 데이터가 들어오게 됩니다.
  6. StudentInfo 클래스에는 학생정보를 정의해 놓았습니다. studentId, name, gender 정보가 정의되어 있습니다. snapshot.data에 _db의 List 정보가 전달되게 됩니다. 이를 studentInfos라는 변수에 저장합니다.
  7. snapshot의 connectionState 상태를 확인합니다. Future 타입의 경우 데이터를 모두 수신한 경우 ConnectionState.done 상태를 갖게 됩니다. 이를 갖기 전까지 CircularProgressIndicator가 표시되도록 합니다.
  8. snapshot.hasError는 에러가 발생하였는지를 확인할 수 있습니다. 에러가 발생한 경우 에러메시지를 화면에 표시합니다.
  9. studentInfos가 null이 아닌 경우에 ListView.builder를 통해 데이터를 화면에 보여줍니다. itemCount와 itemBuilder 두 개의 인자를 전달하게 되는데 itemBuilder의 index 인자를 이용해서 List의 아이템을 순차적으로 접근할 수 있습니다.
  10. Card 형식으로 각각의 아이템의 정보를 전달합니다.
import 'package:flutter/material.dart';

import '../data/local/db/app_db.dart';

//1
class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  //2
  late AppDb _db;

  //3
  @override
  void initState() {
    _db = AppDb();
    super.initState();
  }

  //4
  @override
  void dispose() {
    _db.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
        centerTitle: true,
      ),
      //5
      body: FutureBuilder<List<StudentInfo>>(
        future: _db.getStudents(),
        builder: (context, snapshot) {
          //6
          final List<StudentInfo>? studentInfos = snapshot.data;

          //7
          if (snapshot.connectionState != ConnectionState.done) {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }

          //8
          if (snapshot.hasError) {
            return Center(
              child: Text(snapshot.error.toString()),
            );
          }

          //9
          if (studentInfos != null) {
            return ListView.builder(
              itemCount: studentInfos.length,
              itemBuilder: (context, index) {
                final studentInfo = studentInfos[index];
                return Padding(
                  padding: const EdgeInsets.all(8.0),
                  //10
                  child: Card(
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(studentInfo.id.toString()),
                        Text(studentInfo.studentId.toString()),
                        Text(studentInfo.name.toString()),
                        Text(studentInfo.gender.toString()),
                      ],
                    ),
                  ),
                );
              },
            );
          }
          return const Text('no data found');
        },
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {
          Navigator.pushNamed(context, '/add_student');
        },
        icon: const Icon(Icons.add),
        label: const Text('Add Student'),
      ),
    );
  }
}
FutureBuilder와 ListView.Builder 사용하기

Leave a Comment