develop/Flutter

Flutter FutureBuilder 사용하기 비동기 대응하기 with. Listview

방뎁 2023. 8. 7. 22:23
반응형

FutureBuilder는 비동기로 데이터를 받을때 결과를 기반으로 UI를 업데이트 하는 위젯이다. 

비동기에 대한 이해를 먼저 해야 빌더를 사용할 때 바로적용할 수 있다.

공식 문서는 아래와 같다.

https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html

 

FutureBuilder class - widgets library - Dart API

A widget that builds itself based on the latest snapshot of interaction with a Future. Managing the future The future must have been obtained earlier, e.g. during State.initState, State.didUpdateWidget, or State.didChangeDependencies. It must not be create

api.flutter.dev

 

공식 예제는 아래와 같다. 

import 'package:flutter/material.dart';

/// Flutter code sample for [FutureBuilder].

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: FutureBuilderExample(),
    );
  }
}

class FutureBuilderExample extends StatefulWidget {
  const FutureBuilderExample({super.key});

  @override
  State<FutureBuilderExample> createState() => _FutureBuilderExampleState();
}

class _FutureBuilderExampleState extends State<FutureBuilderExample> {
  final Future<String> _calculation = Future<String>.delayed(
    const Duration(seconds: 2),
    () => 'Data Loaded',
  );

  @override
  Widget build(BuildContext context) {
    return DefaultTextStyle(
      style: Theme.of(context).textTheme.displayMedium!,
      textAlign: TextAlign.center,
      child: FutureBuilder<String>(
        future: _calculation, // a previously-obtained Future<String> or null
        builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
          List<Widget> children;
          if (snapshot.hasData) {
            children = <Widget>[
              const Icon(
                Icons.check_circle_outline,
                color: Colors.green,
                size: 60,
              ),
              Padding(
                padding: const EdgeInsets.only(top: 16),
                child: Text('Result: ${snapshot.data}'),
              ),
            ];
          } else if (snapshot.hasError) {
            children = <Widget>[
              const Icon(
                Icons.error_outline,
                color: Colors.red,
                size: 60,
              ),
              Padding(
                padding: const EdgeInsets.only(top: 16),
                child: Text('Error: ${snapshot.error}'),
              ),
            ];
          } else {
            children = const <Widget>[
              SizedBox(
                width: 60,
                height: 60,
                child: CircularProgressIndicator(),
              ),
              Padding(
                padding: EdgeInsets.only(top: 16),
                child: Text('Awaiting result...'),
              ),
            ];
          }
          return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: children,
            ),
          );
        },
      ),
    );
  }
}

 

실제 예제는 어떠할까

지금 하고 있는 프로젝트에서는 Provider를 사용하고 있다. 예제는 Provider이다.

provider.dart 

  Future<List<Datas>> readDatas() async {
    return Future.delayed(Duration(seconds: 2), () async {
      return data = await DBHelper.instance.getData();
    });
  }

 

view.dart

SafeArea(
    child: Consumer<provider>(
      builder: (context, provider, child) {
        return FutureBuilder<List<Data>>(
          future: provider.readDatas(),
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              final List<Datas>? datas = snapshot.data;
              if (datas != null) {
                return ListView.builder( 
                  itemCount: provider.datas.length,
                  itemBuilder: (context, index) { 
                    return Container(
                    	child: Text(datas[index]),
                    )
                  },
                );
              } else if (snapshot.hasError) {
                return Center(
                  child:
                      Text("Sorry, there is a problem"),
                );
              }
            }
            return Center(
              child: CircularProgressIndicator(),
            );
          },
        );
      },
    ),
  ),
);

 

아무튼 이렇게 빌더를 사용하면 된다.

버전에 따라 사용하는 방법이 좀 다르니 공식문서를 참조하면 된다. 

그리고 빌더와 리스트뷰를 사용하는건 가끔 퍼포먼스가 안좋다는 글을 봤는데 

일단 그런 생각은 앱 완성한 후에 해야겠다. 

조만간 비동기글도 가져올께요.

반응형