Listening for Data from Firebase using Streams
[Ko]
- firestore database의 messages collection에 저장된 text와 sender 값을 flutter app에서 불러와서 사용하자.
- getDocument method를 이용해서 firebase collection으로부터 얻어지는 messages는 future query snapshot 타입의 데이터임. Future<QuerySnapshot> 즉, QuerySnapshot은 firebase의 데이터 타입이며 이를 future 형태로 client에 전송함. —> messages.document를 이용해서 DocumentSnapshot의 List에 접근할 수 있으며, 이를 for loop를 돌릴 수 있음.
- for-in문에서 messages.document의 1개의 item인 message는 1개의 DocumentSnapshot이며, 여기서 실제 데이터를 사용하기 위해서는 message.data(text,sender 의 map 데이터 타입)를 이용.
void getMessages() async {
final messages = await _firestore.collection('messages').getDocuments();
for (var message in messages.documents) {
print(message.data);
}
}
- 실제로 app에서는 위와 같은 방법으로 메세지를 가지고 오면, 내가 작성하는 메세지가 상대방 app에 즉시 나타나게 하려면, getDocument method를 1초에도 여러번씩 계속 실행되도록 해야하는데 이는 매우 비효율적이며 resource 낭비가 심하게 됨. 따라서 작성되는 메세지를 즉시 보여주기 위해서는 stream을 이용해야 함.
- 새로운 메세지 데이터가 작성될 때마가 새 메세지 데이터가 우리의 앱으로 push 되도록 해야 함.
- Stream<QuerySnapshot>을 리턴하는 snapshots() method를 사용하자. Snapshots method가 실제로 제공하는 것은 여러개 future들의 list라고 보면 됨. For-in 문을 이용해서 stream 형태의 데이터를 차례로 순회할 수 있으며, for-in 문의 앞에 await를 사용하면 steam 형태로 제공되는 마지막 future(최신 변화 내용이 적용된 future)만을 사용할 수 있다. 이 마지막 future를 이용해서 다시 for문을 돌리면, 전체 대화 목록을 얻을 수 있음.
- snapshots method를 사용하면 messages collection에서 발생하는 모든 변화를 listening하게 되며, 변화된 최신의 snapshot을 반영하기 위해서는 await for-in 문을 사용하면 stream 형태로 제공되는 완성된 snapshot data를 snapshot 변수에 저장할 수 있음.
void messagesStream() async {
await for (var snapshot in _firestore.collection('messages').snapshots()) {
for (var message in snapshot.documents) {
print(message.data);
}
}
}
- 요약 : 비정기적으로 입력되는 데이터를 변화 발생시에만 push 받기 위해서는 해당 collection에 stream 형태의 데이터를 반환하는 method를 등록하며, 이 method에 의해 여러개 future list 들이 stream 방식으로 반환되는데, 최신의 future를 얻기 위해서는 stream 제너레이터의 결과물(future list)에 for-in 문을 적용하고, 그 중 마지막 future를 얻기 위해서 for-in 문의 앞에 await를 적용함.
- final code(chat_screen.dart)
...
final _firestore = Firestore.instance;
final _auth = FirebaseAuth.instance;
FirebaseUser loggedInUser;
String messageText;
@override
void initState() {
// TODO: implement initState
super.initState();
getCurrentUser();
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser();
if (user != null) {
loggedInUser = user;
print(loggedInUser.email);
}
} catch (e) {
print(e);
}
}
// void getMessages() async {
// final messages = await _firestore.collection('messages').getDocuments();
// for (var message in messages.documents) {
// print(message.data);
// }
// }
void messagesStream() async {
await for (var snapshot in _firestore.collection('messages').snapshots()) {
for (var message in snapshot.documents) {
print(message.data);
}
}
}
Turning Streams into Widgets Using the StreamBuilder
- flutter에서 제공하는 StreamBuilder를 이용하면 stream 형태로 제공되는 data의 snapshot을 중복된 data를 반복적으로 화면에 보여주는 비효율성을 제거하면서 widget으로 변환할 수 있음.
- StreamBuilder는 새로운 stream data가 들어올때마다 setState method를 실행시킴으로써 변경사항을 반영함.
- StreamBuilder의 builder에 사용되는 snapshot은 이전에 다룬 firebase의 snapshot이 아니라, flutter의 async snapshot임. (실제로 flutter의 async snapshot은 firebase의 snapshot을 포함하는 개념임.)
- flutter의 async snapshot에 .data를 이용해서 firebase의 snapshot에 접근할 수 있음. 이렇게 접근한 데이터에 .documents를 이용해서 firebase의 documents에 접근.
StreamBuilder(
stream: _firestore.collection('messages').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
),
);
}
final messages = snapshot.data.documents;
List<Text> messageWidgets = [];
for (var message in messages) {
final messageText = message.data['text'];
final messageSender = message.data['sender'];
final messageWidget =
Text('$messageText from $messageSender');
messageWidgets.add(messageWidget);
}
return Column(
children: messageWidgets,
);
},
),
[En]
- Import and use the text and sender values stored in the messages collection in the firestore database in the flutter app.
- The messages obtained from the firebase collection using the getDocument method are data of the future query snapshot type. Future That is, QuerySnapshot is a firebase data type and sends it to the client in the future. —> You can access the List of DocumentSnapshot using messages.document, and run a for loop.
- In the for-in statement, the message, which is one item of messages.document, is one DocumentSnapshot. To use the actual data, use message.data (the map data type of text and sender).
void getMessages() async {
final messages = await _firestore.collection('messages').getDocuments();
for (var message in messages.documents) {
print(message.data);
}
}
- In fact, if you bring a message in the same way as in the app above, in order for the message you write to appear immediately in the other party app, you must keep the getDocument method running several times a second, which is very inefficient and wastes resources. Therefore, stream must be used to immediately display the created message.
- Whenever new message data is created, we need to ensure that the new message data is pushed to our app.
- Use the snapshots () method that returns a Stream . What the Snapshots method actually provides is a list of multiple futures. By using the for-in statement, it is possible to iterate through data in the form of stream, and if await is used in front of the for-in statement, only the last future provided in the form of steam (the future with the latest changes) can be used. If you run the for statement again using this last future, you can get the full list of conversations.
- When using the snapshots method, all changes occurring in the messages collection are listened to. To reflect the latest snapshot, the await for-in statement can be used to store the completed snapshot data provided in the form of a stream in the snapshot variable. .
void messagesStream() async {
await for (var snapshot in _firestore.collection('messages').snapshots()) {
for (var message in snapshot.documents) {
print(message.data);
}
}
}
- Summary: In order to receive data that is entered irregularly only when a change occurs, register a method that returns data in the form of stream in the collection, and by this method, multiple future lists are returned in a stream manner. To do this, apply the for-in statement to the result of the stream generator (future list), and apply await before the for-in statement to get the last future.
- final code(chat_screen.dart)
...
final _firestore = Firestore.instance;
final _auth = FirebaseAuth.instance;
FirebaseUser loggedInUser;
String messageText;
@override
void initState() {
// TODO: implement initState
super.initState();
getCurrentUser();
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser();
if (user != null) {
loggedInUser = user;
print(loggedInUser.email);
}
} catch (e) {
print(e);
}
}
// void getMessages() async {
// final messages = await _firestore.collection('messages').getDocuments();
// for (var message in messages.documents) {
// print(message.data);
// }
// }
void messagesStream() async {
await for (var snapshot in _firestore.collection('messages').snapshots()) {
for (var message in snapshot.documents) {
print(message.data);
}
}
}
Turning Streams into Widgets Using the StreamBuilder
- Using the StreamBuilder provided by flutter, snapshots of data provided in the form of streams can be converted into widgets while removing the inefficiency of repeatedly displaying duplicate data on the screen.
- StreamBuilder reflects changes by executing setState method whenever new stream data comes in.
- The snapshot used in the builder of StreamBuilder is not a snapshot of the firebase discussed previous, but an async snapshot of flutter. (Actually, flutter's async snapshot is a concept that includes a snapshot of firebase.)
- You can access the snapshot of firebase using .data in flutter's async snapshot. Access the documents in the firebase using .documents on the accessed data.
StreamBuilder(
stream: _firestore.collection('messages').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
),
);
}
final messages = snapshot.data.documents;
List<Text> messageWidgets = [];
for (var message in messages) {
final messageText = message.data['text'];
final messageSender = message.data['sender'];
final messageWidget =
Text('$messageText from $messageSender');
messageWidgets.add(messageWidget);
}
return Column(
children: messageWidgets,
);
},
),
'플러터(Flutter) > 플러터 일반(Flutter General)' 카테고리의 다른 글
How to programmatically exit the app in flutter (0) | 2020.06.06 |
---|---|
stream data listening (0) | 2020.04.23 |
setup Firebase Project (0) | 2020.03.19 |
[Dart] Mixins (0) | 2020.03.19 |
Hero Animation (0) | 2020.03.19 |