구글에서 파이어베이스 데이터를 가져오는 방법을 검색해보면 단순히 메서드 형태로 만들어서 print를 통해 데이터를 보여주는 예제들만 있어서, 이 데이터를 실제로 화면으로 보여주는 과정에서 어려움을 겪었습니다. 또한 여러 document를 ListView나 GridView형태로 보여주는 것은 비교적 쉽게 작동했지만 특정 document의 단일 데이터를 보여주는 부분에서는 다양한 문제가 발생하였습니다. 이 과정에서 발생한 문제들을 해결하고 최종적으로 완성된 코드를 아래 예제를 통해 쉽고 자세하게 알아보도록 하겠습니다.
파이어베이스 DB에 저장된 사용자 데이터를 가져와서 Text 위젯으로 보여주는 간단한 예제를 만들어 보겠습니다.
DB 구조는, ‘userInfo’라는 컬렉션 밑에 사용자 uid로 된 문서가 있고 필드에 사용자 정보가 저장되어 있습니다. 문서 안에 있는 필드 데이터를 가져와보겠습니다.
먼저 아래 내용을 import 해주세요
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
stateful 위젯으로 빈 페이지를 하나 만들고, DB에서 데이터를 가져오는 메서드를 만들겠습니다.
class GetDataTest extends StatefulWidget {
const GetDataTest({Key? key}) : super(key: key);
@override
State<GetDataTest> createState() => GetDataTestState();
}
class GetDataTestState extends State<GetDataTest> {
String uid = FirebaseAuth.instance.currentUser!.uid;
getUserInfo() async {
var result = await FirebaseFirestore.instance.collection('userInfo').doc(uid).get();
return result.data();
}
DB에서 문서 ID가 사용자 uid로 되어있으므로, 먼저 현재 로그인 되어있는 사용자 firebase uid를 String 타입의 uid변수로 받습니다. 그러고 getUserInfo() 라는 메서드를 만들어서 파이어베이스에 저장되어 있는 경로에 해당되는 데이터를 get() 메서드로 받아서 result 변수에 저장합니다. 이 때, 서버에서 가져오는 시간이 있으므로 반드시 async await 키워드를 사용해주시기 바랍니다. 그러고 최종적으로 result 변수의 data()를 리턴해주도록 합니다.
그 다음, 페이지 화면을 구성해보겠습니다. 일반적으로 DB에서 데이터를 가져와서 보여줄 때는 FutureBuilder나 StreamBuilder를 사용합니다. StreamBuilder는 데이터가 지속적으로 변하는 경우 사용하고, 이 예제에서는 페이지가 빌드될 때 데이터를 한번만 가져오면 되기 때문에 FutureBuilder를 사용하겠습니다.
@override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: getUserInfo(),
builder: (context, snapshot) {
return
}
)
);
}
FutureBuilder를 선언해주시고 ‘Future:’ 부분에는 데이터를 가져오는 메서드를 적어주세요. 이 때 메서드를 별도로 만들어서 불러오지 않고 직접 입력하게 되면, async방식으로 작동하지 않기 때문에 문제가 발생하게 됩니다.
그 다음으로 FutureBuilder의 리턴 값에 우리가 보여줄 Text 위젯을 넣어주겠습니다.
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text((snapshot.data as Map)['name']),
Text((snapshot.data as Map)['email']),
Text((snapshot.data as Map)['phoneNumber']),
],
),
);
우리가 가져온 데이터는 snapshot에 저장되어 있고 이 snapshot의 data는 object?타입이라서 바로 사용할 수가 없으므로, Map 타입으로 변환해줘야 합니다. 그러고 [] 안에 Map의 key값을 적어주면 key값에 해당되는 value가 표시됩니다.
이렇게 하면 데이터는 정상적으로 가져와져서 잘 표시가 되는데요, 문제는 데이터를 불러오는 시간보다 위젯이 빌드되는 속도가 더 빠르기 때문에 Text로 보여줄 변수에서 순간적으로 null check 에러가 발생하게 됩니다.
따라서 다음과 같이 ‘hasData’ 키워드를 조건식으로 사용해서 데이터가 들어오기 전 까지는 로딩화면이 먼저 보일 수 있도록 하겠습니다.
return
snapshot.hasData?
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text((snapshot.data as Map)['name']),
Text((snapshot.data as Map)['email']),
Text((snapshot.data as Map)['phoneNumber']),
],
),
)
: Center(child: CircularProgressIndicator());
이렇게 하면 페이지가 빌드될 때 null check 에러가 발생하지 않고, 아주 짧은 시간동안 로딩 화면이 보이다가 정상적으로 데이터가 표시됩니다.
전체 코드
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
class GetDataTest extends StatefulWidget {
const GetDataTest({Key? key}) : super(key: key);
@override
State<GetDataTest> createState() => GetDataTestState();
}
class GetDataTestState extends State<GetDataTest> {
String uid = FirebaseAuth.instance.currentUser!.uid;
getUserInfo() async {
var result = await FirebaseFirestore.instance.collection('userInfo').doc(uid).get();
return result.data();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: getUserInfo(),
builder: (context, snapshot) {
return
snapshot.hasData?
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text((snapshot.data as Map)['name']),
Text((snapshot.data as Map)['email']),
Text((snapshot.data as Map)['phoneNumber']),
],
),
)
: Center(child: CircularProgressIndicator());
}
)
);
}
}
'Flutter' 카테고리의 다른 글
[Flutter] 플러터 웹 배포 후 메인화면으로 넘어가지 않는 현상(Failed to load…404 error) (0) | 2023.07.01 |
---|---|
[Flutter] 웹 Mixed Content 에러 쉽고 간단하게 해결하는 방법 (0) | 2023.05.05 |
[Flutter] 심플한 팝업 메뉴 만들기 (0) | 2023.04.04 |
[Flutter] image_picker로 사진 업로드 화면 만들기 (0) | 2023.03.28 |
Flutter GridView(그리드뷰) 사용 방법 (0) | 2023.03.26 |