[flutter] 비동기 작업 화면 만들기, 나만의 CustomFutureBuilder 위젯 만들기

flutter 개발자·2022년 5월 18일

flutter

목록 보기
2/3
post-thumbnail

들어가기

사용자로 하여금, 같은 어플리케이션 내에서 같은 UX를 경험하게 하는 것은 매우 중요하다고 생각한다. 그래서 화면마다 FutureBuilder 위젯을 그대로 사용하지 않고 별도의 CustomFutureBuilder 클래스를 생성하여 한번에 관리할 수 있도록 하는 것이 이번글의 목표이다.

지난 포스팅에서는 flutter 에서 모델 클래스를 만들고 http 패키지를 사용하여 통신하여 받은 결과 JSON을 객체화 하는 내용을 기록했다.
오늘은 지난글에 이어서 비동기에 대해서 자세히 다루고 화면에 어떻게 나타내는지 보여주려고 한다.

동기 vs 비동기

  • 동기 : 요청을 한 후 응답이 올 때까지 코드를 진행하지 않고 기다렸다가 응답을 받으면 다음 코드를 순차적으로 진행한다.
  • 비동기 : 요청하고 나서 응답 받지 않았는데도 대기하지 않고 다음 코드를 진행한다. 언제든 응답이 오면 그때 응답을 처리한다.

서버 요청과 같이 시간이 오래 걸리는 작업을 기다리는 동안 그 작업이 끝날 때까지 사용자 이벤트나 화면을 처리할 수 없게 되면 성능이 떨어져 보이는 문제가 발생한다. 이럴 때, 비동기적으로 작업을 진행하면 좋다.

Future 와 FutureBuilder

Future

Future는 미래에 받아올 값을 뜻한다.

Future<int> id;
Future<String> name;

5초 뒤에 숫자를 받아서 result 변수에 저장하는 함수

Future<int> function() {
	return Future.delayed(
  	const Duration(seconds: 5),
      () => 10,
	);
}

FutureBuilder

Future는 미래의 데이터임으로 바로 화면에 출력할 수 없다.
결과가 나올 때까지 대기했다가 화면에 출력해주는 위젯이 필요한데 FutureBuilder를 사용하면 가능하다.

아래는 FutureBuilder의 가장 기본적인 예제이다.

FutureBuilder(
  future: futureFunction(),
  builder: (context, snapshot) {
    if(snapshot.hasData) {
    return Text('${snapshot.data}');
    }
  	return CircularProgressIndicator();
})

futureFunction() 함수를 통해 받아온 결과값을 snapshot.data를 통해서 화면에 출력할 수 있다.

await와 async

작업의 처리가 끝날 때까지 대기하고 싶은 비동기 함수가 있을 수 있다.
혹은 비동기 작업이 끝난 후에 뒤에 작업들이 진행해야 하는 경우가 있다.
이럴 때, then 과 같은 역할이자, 함수 앞에 붙일 수 있는 await 이 있다.

await 은 실행 영역에 붙이고, async는 선언 영역에 작성한다.

Future<int> futureFunction() async {
	int a = await funA();
    int b = await funB(a);
    return b;
}

then, catchError

예문

합을 더해서 3초 뒤에 return하는 함수를 구현했다고 가정해보자.

Future<int> future = sum(1,2);
future.then((value) => print('result : $value'));
future.catchError((error) => print('error : $error'));

then과 whenComplete의 차이

WhenComplete는 Future가 끝나면 무조건 실행된다.
Error 발생 유무에 관계가 없다.

그치만 then은 Error가 Return 되면 출력이 되지 않는다.
에러없이 결괏값을 Return 받을 때 실행한다.

그때 그때 필요에 따라서 사용하면 될 것 같다.

Stream

Future가 한번 결과값을 리턴 받는 것에 사용한다면 Stream은 여러번 결과값을 리턴받는데 사용한다. 예전에 한 프로젝트에서 사용한 적이 있는데 다음에 기회가 되면 자세히 포스팅할 예정이다.

나만의 FutureBuilder 위젯 만들기

같은 어플리케이션 내에서 같은 UX를 경험하게 하는 것은 중요하다고 생각한다. 그래서 화면마다 FutureBuilder를 그대로 사용하지 않고 별도의 각 화면에서 comlete 화면과 future에 대해서만 정의하면 구현할 수 있도록 CustomFutureBuilder 클래스를 생성하여 같이 관리할 수 있도록 하였다.

class CustomFutureBuilder<T> extends StatelessWidget {
  final Future<T> future;
  final T? initialData;
  final Function(BuildContext context, Object error)? onError;
  final Function(BuildContext context)? onLoading;
  final Function(BuildContext context, T data) onComplete;
  final Function(BuildContext context)? onEmpty;

  const CustomFutureBuilder(
      {Key? key,
      required this.future,
      this.initialData,
      this.onError,
      this.onLoading,
      required this.onComplete,
      this.onEmpty})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    double deviceWidth = MediaQuery.of(context).size.width;
    double deviceHeight = MediaQuery.of(context).size.height;

    return FutureBuilder<T>(
      future: future,
      initialData: initialData,
      builder: (context, snapshot) {
        final status = snapshot.connectionState;

        if (snapshot.hasError) {
          if (onError != null) {
            return onError!(context, snapshot.hasError);
          }
        }

        if (status == ConnectionState.waiting) {
          if (onLoading != null) {
            return onLoading!(context);
          } else {
            return Container(
              width: deviceWidth,
              height: deviceHeight,
              color: BG_COLOR,
              child: const Center(
                child: CircularProgressIndicator(color: PRIME_COLOR),
              ),
            );
          }
        }

        if (status == ConnectionState.done) {
          if (snapshot.hasData) {
            return onComplete(context, snapshot.data!);
          } else {
            if (onEmpty != null) {
              return onEmpty!(context);
            }
          }
        }

        return Container();
      },
    );
  }
}

여러종류의 파싱 예제

  • List, string, 객체, 여러 종류 리스트 혼합형, CustomScrollView 예제 등 다음에 추가로 작업하도록 하겠다.
profile
매일, 꾸준히

0개의 댓글