이번 글은 FireStore Database를 활용해서 숫자를 증감시키고, 변경된 값을 바로 확인해볼 수 있는 간단한 예제입니다. Future와 Stream의 비동기 처리에 대해 집중했고, FireStore Database의 연동 방법은 생략했습니다.
firebase_core: 1.10.3
cloud_firestore: ^3.1.7
채팅방의 이름, 숫자, document 값을 가진 데이터 모델입니다. factory형식으로 json을 파싱할 수 있도록 해놨고, 예제에서 사용하지는 않지만 toString과 equal operator를 override 했습니다.
class RoomModel {
String? name;
int? num;
String? doc;
RoomModel({required this.name, required this.num, required this.doc});
factory RoomModel.fromJson(Map<String, dynamic> json) {
return RoomModel(name: json["name"], num: json["num"], doc : json["doc"]);
}
String toString() {
return "RoomModel(name : $name, num : $num, doc : $doc)";
}
bool operator ==(Object other) {
if (other is RoomModel) {
return name == other.name && num == other.num && doc == other.doc;
} else {
return false;
}
}
int get hashCode => super.hashCode;
}
FD의 데이터를 호출하는 클래스입니다. readData 함수는 파이어베이스 클래스의 함수를 사용해서 Stream 타입을 리턴하고 있습니다.
class FSGet {
///파이어베이스의 room collection에 있는 모든 데이터 불러오기
static Stream<List<Map<String, dynamic>>> readData({required String collection}) =>
FirebaseFirestore.instance
.collection(collection)
.snapshots()
.map((snapshot) => snapshot.docs.map((doc) => doc.data()).toList());
}
FD의 데이터를 변경할 때 사용합니다.
class FSSet {
///FireStore Database의 room collection 업데이트
static Future set({
required String collection,
required String doc,
required Map<String, dynamic> json,
}) async {
await FirebaseFirestore.instance.collection(collection).doc(doc).set(json);
}
}
///Firebase와 Future, Stream을 활용한 채팅방 스크린
class RoomScreen extends StatefulWidget {
const RoomScreen({Key? key}) : super(key: key);
_RoomScreenState createState() => _RoomScreenState();
}
class _RoomScreenState extends State<RoomScreen> {
final String collectionName = "room";
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: const Text("Room")), body: _body());
}
Widget _body() {
return StreamBuilder<List<Map<String, dynamic>>>(
stream: FSGet.readData(collection: collectionName),
builder: (context, snapshot) {
if (snapshot.hasData) {
print(snapshot.data);
List<RoomModel> data = snapshot.data!
.map<RoomModel>((json) => RoomModel.fromJson(json))
.toList();
return _listView(data: data);
} else {
return const SizedBox();
}
},
);
}
///리스트 위젯
Widget _listView({required List<RoomModel> data}) {
return ListView.separated(
itemCount: data.length,
shrinkWrap: true,
itemBuilder: (_, index) {
var model = data[index];
return _listItem(
name: model.name!,
num: model.num!,
plus: () async => await _plusData(roomModel: model),
reset: () async => await _resetData(roomModel: model),
);
},
separatorBuilder: (_, __) => _divider(),
);
}
///구분선
Widget _divider() =>
Divider(height: 2, thickness: 2, color: Colors.grey.shade300);
///리스트 아이템 위젯
Widget _listItem({
required String name,
required int num,
required VoidCallback plus,
required VoidCallback reset,
}) {
return Padding(
padding: const EdgeInsets.all(10),
child: Row(
children: [
///숫자 증가 버튼
ElevatedButton(onPressed: plus, child: const Text("증가")),
const SizedBox(width: 10),
///숫자 초기화 버튼
ElevatedButton(onPressed: reset, child: const Text("초기화")),
///대화방 이름과 숫자
Expanded(
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Text("$name : $num", style: const TextStyle(fontSize: 20))
]),
)
],
),
);
}
///숫자 플러스 api 호출
Future _plusData({required RoomModel roomModel}) async {
int num = roomModel.num!;
num++;
await FSSet.set(
collection: "room",
doc: roomModel.doc!,
json: {"doc": roomModel.doc!, "name": roomModel.name!, "num": num});
}
///숫자 초기화 api 호출
Future _resetData({required RoomModel roomModel}) async {
await FSSet.set(
collection: collectionName,
doc: roomModel.doc!,
json: {"doc": roomModel.doc!, "name": roomModel.name!, "num": 0});
}
}