본문 바로가기

Flutter Widget of the Week 톺아보기

06. FutureBuilder in Flutter [Flutter Widget of the Week]톺아보기

Flutter of the week란?

 공식 유튜브 채널에서 주 1회  Flutter의 다양한 기능, 팁, 패키지등의 사용 사례에 대한 간결하고 유익한 정보를 제공하는 영상시리즈
바로가기

 

FutureBuilder 란?

A widget that builds itself based on the latest snapshot of interaction with a Future.
 - (미래에)어떤 작업이 끝날 때까지 기다렸다가, 그 작업의 최신 결과를 기반(스냅샷을 통해)으로 자동으로 화면을 업데이트하는 기능을 갖춘 위젯

 

FutureBuilder는 플러터에서 비동기 작업을 수행하고 해당 작업의 결과에 따라 UI를 업데이트하는 데 사용되는 위젯입니다. 주로 네트워크 호출, 데이터 로딩 또는 유저의 입력에 따라 데이터를 업데이트해야 될 경우 사용됩니다.

*비동기 : 프로그램이 작업의 완료를 기다리지 않고, 동시에 여러 작업을 수행할 수 있는 프로그래밍 방식

FutureBuilder 위젯 사용법

FutureBuilder(
  future: fetchData(),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return CircularProgressIndicator();
    } else if (snapshot.hasError) {
      return Text("데이터 가져오기에 실패했습니다.");
    } else {
      List<String> data = snapshot.data;
      return ListView.builder(
        itemCount: data.length,
        itemBuilder: (context, index) {
          return Text(data[index]);
        },
      );
    }
  },
)
  • future 속성에는 Future타입의 class가 들어가며 해당 클래스의 상태가 snapshot안에 담기게 됩니다.
  • snapshot
    • snapshot.hasData : 데이터 여부를 확인 하는 bool값입니다 현재 데이터가 있다면 true가 반환됩니다.
    • snapshot.hasError : Error 여부를 확인하는 bool값입니다.
    • snapshot.data : snapshot의 데이터를 가져옵니다. 해당 데이터로 ui에 적용하거나 비즈니스로직을 처리합니다.
  • snapshot.connectionState
    • connectionState 에는 연결 상태값이 들어오게 됩니다.  
    • ConnectionState.waiting(기다리는중), ConnectionState.done(완료), ConnectionState.none(연결 없음) 등이 들어오게 됩니다.

사용예제

FutureBuilder  가상의 서버에서 로그인 인증을 하는 로직으로 구성했습니다.

 

 1.유저가 입력한 값이 저장된 id값과 일치하면 true, 일치하지 않으면 false를 돌려주는 Future함수를 만듭니다.

Future<bool> login() async {
    //로그인 정보 확인
    await Future.delayed(const Duration(seconds: 2));
    if (userId == 'abc') {
      return true;
    }
    return false;
  }

 

2. FutureBuilder를 생성하고 1.에서 생성한 Future함수를 future속성에 넣어줍니다.

snapshot.data가 false일경우 ui에 '아이디를 확인해주세요'라는 안내문구를 보여줍니다.

FutureBuilder(
                future: login(),
                builder: (context, snapshot) {
                  if (snapshot.connectionState == ConnectionState.waiting) {
                    return const CircularProgressIndicator();
                  } else {
                    if (snapshot.hasError) {
                      return Text('error :${snapshot.error}');
                    } else {
                      if (snapshot.data! == true) {
                        return Text('로그인 성공');
                      }
                      return Text('아이디를 확인해주세요');
                    }
                  }
                },
              ),

 

 

3. 마지막으로 버튼을 눌렀을때 유저가 현재 입력한 값과 저장된 ID값이 같다면 로그인 성공 페이지로 넘겨주는 함수를 생성합니다.

void handleLogin() async {
    setState(() {
      userId = controller.text;
    });
    bool loginResult = await login();
    if (loginResult) {
      Navigator.of(context).push(MaterialPageRoute(builder: (_) => LoginScreen()));
    } 
  }

 

전체코드

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_of_the_week/screen/login.dart';

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

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

class _HomeScreenState extends State<HomeScreen> {
  TextEditingController controller = TextEditingController();
  String userId = '';

  Widget build(BuildContext context) {


    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.orange,
        title: Text(
          'FutureBuilder로 로그인 구현하기',
        ),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Text(
              'ID',
              style: TextStyle(fontSize: 32),
            ),
            TextFormField(
              controller: controller,
              decoration: const InputDecoration(
                border: OutlineInputBorder(),
                focusedBorder: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.orange)),
              ),
            ),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              FutureBuilder(
                future: login(),
                builder: (context, snapshot) {
                  if (snapshot.connectionState == ConnectionState.waiting) {
                    return const CircularProgressIndicator();
                  } else {
                    if (snapshot.hasError) {
                      return Text('error :${snapshot.error}');
                    } else {
                      if (snapshot.data! == true) {
                        return Text('로그인 성공');
                      }
                      return Text('아이디를 확인해주세요');
                    }
                  }
                },
              ),
            ],
          ),
            ElevatedButton(
                onPressed: handleLogin,

                child: Text('로그인'))
          ],
        ),
      ),
    );
  }
  void handleLogin() async {
    setState(() {
      userId = controller.text;
    });
    bool loginResult = await login();
    if (loginResult) {
      Navigator.of(context).push(MaterialPageRoute(builder: (_) => LoginScreen()));
    } else {}
  }

  Future<bool> login() async {
    //로그인 정보 확인
    await Future.delayed(const Duration(seconds: 2));
    if (userId == 'abc') {
      return true;
    }
    return false;
  }
}