๋จผ์ Stream์ ์ ์ฐ๋์ง๋ถํฐ ์๊ฐํด๋ณด์.
Stream์ ๋ฐ์ดํฐ๊ฐ ์ฌ์ฉ ๊ฐ๋ฅํด์ง๋ฉด ์์ฐ์์์ ์๋น์๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ค. ์ด๋ ์ฌ์ฉํ์ง ๋ชปํ๋ ๋ฐ์ดํฐ์ ์ ๊ทผ์ ๋ฐฉ์งํด ์ค๋ค.
๋๋ฒ์ฌ๋ก ๋ณดํต ๋ฐ์ดํฐ๋ฅผ ์์ญ๊ณผ UI์์ญ์ ๋ถ๋ฆฌํ๋๋ฐ ์ด๋ ๋ฐ์ดํฐ๋ฅผ UI์ ์ ๋ฌ ํด์ฃผ๋ ๋งค๊ฐ์ฒด๊ฐ ํ์ํ ๊ฒ์ด๋ค. Stream์ ๋ฐ์ดํฐ๋ฅผ ์์ํ๊ฒ ์ ๋ฌํ๋ ๋งค๊ฐ์ฒด ์ญํ ์ ํ๋ค. Stream์ ์์ฐ์์ ์๋น์๋ก ์์ญ์ ๋ถ๋ฆฌํ์ฌ ๋จผ ๊ณณ๊น์ง ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํด ์ค ์ ์๋ค.
- ๋ฐ์ดํฐ๊ฐ ์ฌ์ฉ ๊ฐ๋ฅํ ์ํ ์ผ๋ ๊ธฐ๋ค๋ ธ๋ค๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๊ฒ ํด์ค๋ค.
- ๋ฐ์ดํฐ ์์ญ๊ณผ ์ฒ๋ฆฌ์์ญ์ผ๋ก ์ฝ๋๋ฅผ ๋ถ๋ฆฌ ํ ์ ์๋ค.
- ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ฒ ์ ๋ฌํด์ฃผ๋ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณต ํด์ค๋ค.
- ๋ฐ์ดํฐ๋ฅผ ๊ฐ๊ณตํ๊ณ ์ฌ์์ฐ ํ ์ ์์ด ์๋ณธ ๋ฐ์ดํฐ์ ์ฌ์ฌ์ฉ์ฑ์ ๊ทน๋ํ ํ๋ค.
๋ฐ์ดํฐ ํ๋ํ๋๋ฅผ ์์ ํ์ฌ ์ฒ๋ฆฌํ๋ ค ํ ๋ ์ฌ์ฉํ๋ค. ๋ฐ์ดํฐ๋ฅผ ์์ ํ๋ฉด์ ๋ณ๊ฒฝ/๊ฐ๊ณต ํ ์ ์๋ ํจ์๋ค์ ์ ๊ณตํด์ฃผ๊ณ ์๋ค. Iterable์ ๋ฆฌํดํด์ผํ๊ณ ํจ์์ sync* ํค์๋๋ฅผ ๋ถ์ธ๋ค. yield๊ฐ ํธ์ถ๋ ๋๋ง๋ค Iterable์ ๊ฐ์ ๋ฃ์ด์ฃผ๊ณ ์์ธ ๊ฐ์ ๋ฆฌํดํ๋ค.
Iterable<String> stm() sync*{
for (int i = 1; i <= 10; i++) {
String sendMsg = "message:$i";
print(sendMsg);
yield sendMsg;
}
}
stm().forEach((receive) => print('receive = $receive'));
/* ์ถ๋ ฅ
message:1
receive = message:1
...
receive = message:10
/*
์ถ๋ ฅ ๋ด์ฉ์ ๋ณด๋ฉด ํ๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋๊ฒ์ด ์๋ yield๊ฐ ํธ์ถ๋ ๋๋ง๋ค ์์ ํ๋ค. ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๋ ๋ถ๋ถ๊ณผ(Iterable) ์ฒ๋ฆฌ๋ถ๊ฐ(forEach) ๋๋ ์ง๋ ๊ฒ์ ๋ณผ์ ์๋ค. ์ด๋ฌํ ์ฅ์ ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ๊ณตํ ๋ ์๋ณธ ๋ฐ์ดํฐ๋ฅผ ์์ /๋ณ๊ฒฝ ํ์ง ์๊ณ ๋ฐ์ดํฐ๋ฅผ ๊ฐ๊ณตํ์ฌ ๋ง๋ค์ ์๋ค.
Note : sync*์ Iterable ๋ฆฌํด์ ํ์์ผ๋ก ํจ์๋ฅผ ๋ง๋ ๋ค.
async*, yeild๋ ๋ฐ์ดํฐ๋ฅผ ๋น๋๊ธฐ๋ก ์ฃผ๊ธฐ์ ์ผ๋ก ๋ฐ์์ผ ํ ๋ ์ฌ์ฉํ๋ค. Iterable๊ณผ ๊ฐ์ด ๋ณ๊ฒฝ/๊ฐ๊ณต ํ ์ ์๋ ๊ธฐ๋ฅ๋ค์ ์ ๊ณตํด ์ค๋ค.
Stream์ ๋ฆฌํดํด์ผํ๊ณ ํจ์์ async* ํค์๋๋ฅผ ๋ถ์ธ๋ค. yield๋ ๊ตฌ๋
ํ๋ ๊ณณ์ ๋ฆฌํดํ ๋ฐ์ดํฐ์ด๋ค. ๊ทธ๋ฆฌ๊ณ Stream.listen์ผ๋ก ์์ฐ๋ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ ๋ฐ์ ์ ์๋ค.
Note : async/yeild ๋ก๋ง๋ Stream์ ๋ฐ์ดํฐ ์์ฐ์, Stream.listen์ ์๋น์ ์ด๋ค.
Stream<String> count() async* {
for (int i = 1; i <= 10; i++) {
await Future.delayed(Duration(seconds: 1)); // ์์ด๋๋จ. ๋น๋๊ธฐ ์ฝ๋๊ฐ ๋ค์ด๊ฐ๋ ๋๋์ง ํ์ธํ๋ ค ๋ฃ์ ์ฝ๋
yield 'message:$i';
}
}
print("read");
stream.listen((receive) { print('receive = $receive');});
print("end");
//์ถ๋ ฅ
//read
//end
//receive = message:1
//receive = message:2
//....
Note : async*์ Stream ๋ฆฌํด์ ํ์์ผ๋ก ํจ์๋ฅผ ๋ง๋ ๋ค.
๋น๋๊ธฐ๋ก ์ถ๋ ฅํ๋ ๊ฒ์ ๋ณผ์ ์๋ค. ์์ ์ด ์๋ฃ๋๊ณ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ๊ณ ์ถ๊ฑฐ๋ Future์ฒ๋ผ await๊ฑธ์ด ์ต์ ๋ํ๊ฒ ๋๊ธฐํ๋ฅผ ๊ฑธ๊ณ ์ถ์ ์ ์๋ค. ํ์ง๋ง stream์ Future๊ฐ ์๋๊ธฐ ๋๋ฌธ์ await๋ฅผ ๊ฑธ์ ์๋ค. ๋์ Future๋ฅผ returnํ๋ ํจ์๋ฅผ ์ ๊ณตํจ์ผ๋ก์จ await ๋ฐ Future์ ๊ธฐ๋ฅ์ ํ์ฉํ ์ ์๊ฒ ํด์ค๋ค. ๋ํ์ ์ธ ๊ฒ์ด forEach ํจ์์ด๋ค.
await stream.forEach((receive) {print('receive = $receive'); });
//๋๋ for in ๋ ๊ฐ์ ์ญํ ์ํจ
// await for(var receive in stream){print('receive = $receive');}
//์ถ๋ ฅ
//read
//receive = message:1
//receive = message:2
//....
//end
yield๋ ๊ฐ์ ๋ฆฌํดํ๋ ๋ฐ๋ฉด yield* Stream์ ๋ฆฌํดํ๋ค. yield*์ ๋ค๋ฅธ ์คํธ๋ฆผ๊ณผ ์ฐ๊ฒฐํ๋๋ฐ ์ฌ์ฉํ๋ค๊ณ ๋ณผ์ ์๋ค.
Stream<int> stm(int count) async*{
yield* stm2(count);
}
Stream<int> stm2(int count) async*{
yield count*10;
}
stm(3).listen(print);
/* ์ถ๋ ฅ
30
*/
Note : yield*์ ์ฌ๊ท ํธ์ถ๋ก ์ฌ์ฉํ ์ ์๋ค.
Stream์ lazy๋ก ์คํ๋๋ค. ์ฆ ์๊ตฌ๊ฐ ์์๋ ๊ทธ๋ ์ฝ๋๋ฅผ ์คํํ๊ฒ ๋๋ค. Stream์ ๊ฒฝ์ฐ listen์ด ์์๋ ํด๋น ์ฝ๋๋ฅผ lazyํ๊ฒ ์คํํ๊ฒ ๋๋ค.
Stream<int> stm(int count) async*{
print('3์ด ํ ์คํ');
yield count;
}
Stream<int> stream = stm(5);
Future.delayed(Duration(seconds: 3), (){
stream.listen((event) {print('listen:$event');});
});
/* ์ถ๋ ฅ
3์ด ํ ์คํ
listen:5
*/
์ถ๋ ฅ ๋ด์ฉ๊ณผ ๊ฐ์ด listen์ด ์๊ตฌ๋ ๋ stm์ ์คํํ๋๊ฒ์ ๋ณผ์ ์๋ค. lazy๋ก๋ฉ์ ๋๋ฌธ์ Stream ๊ฐ์ฒด๋ฅผ ๋ง๋ค๋๊ณ ํ์ํ ๊ณณ์์ listen์ ํ ์ ์๋ค. ์ด๊ฒ์ ์ค์ ๋ฐ์ดํฐ ๋ฑ์ ๋ง๋๋ ๊ณณ๊ณผ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ ์์ญ์ ๋ถ๋ฆฌํ ์ ์์์ ์๋ฏธํ๋ค.
์ผ๋ฐ์ ์ผ๋ก lazy๋ก๋ฉ์ ์์
์ ๋ถ์ฐ์์ผ ์ฑ๋ฅ์ ํฅ์์ ๋์์ด ๋๋ค. ํ์ง๋ง ๋ก๋ฉ(์ฑ์ ์ธํธ๋ก ๊ฐ์๊ณณ) ํ์์ ์ค๋๊ฑธ๋ฆฌ๋ ๋ฐ์ดํฐ ์ฐ์ฐ์ ํ์ง ์๊ณ lazyํ๊ฒ ํ๋ค๋ฉด ๋ฐ์ดํฐ๊ฐ ํ์ํ ์์ ์ ์ฐ์ฐ์ ํ๊ฒ๋์ด ์์น ์๋ delay๋ฅผ ์ค ์ ์์ด ์ด์ ์ ์ฃผ์ํด์ผ ํ๋ค. ์ฐ์ฐ์๊ฐ์ ํ ์ ๋ฐ๋๊ณณ์ด ์๋ค๋ฉด ์ฌ๊ธฐ์ ์ฐ์ฐ์ ํ๊ณ Stream์ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ด์ฃผ๋ ํํ๋ก ๋ง๋๋ ๊ฒ์ด ์ข๋ค.
Stream์ ์ด๋ฒคํธ๋ ํน์ ์์ ์ ๋ฐํ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ธฐ์ ์ผ๋ก ๋ฐ๋ ๊ตฌ๋ ์ด๋ ๊ฐ๋ ์ ๊ฐ์ง๊ณ ์๋ค. ๋ฐ๋ผ์ ๊ตฌ๋ ์ ํด์ ํ๋ค๋์ง ๊ตฌ๋ ์ค ์ค๋ฅ๋ ๊ตฌ๋ ์ธ ๋ง๋ฃ ๋์์๋ ์ฒ๋ฆฌ๊ฐ ํ์ํ๋ค. ์ด๋ ์ฐ์ด๋ ๊ฒ์ด StreamSubscription ์ด๋ค. stream.listen ํจ์๋ StreamSubscription์ ๋ฆฌํดํ๊ณ ์๋ค. StreamSubscription์ผ๋ก Stream ๊ตฌ๋ ์ cancel ํ ์ ์๊ณ ์๋ฃ์ ์๋ฌ์ฒ๋ฆฌ๋ ์ง์ํ๋ค.
StreamSubscription subscription = stream.listen((event) => print(event));
subscription.cancel(); // ๊ตฌ๋
์ทจ์
subscription.onError(()=>print('error')); // ์๋ฌ. ์๋ฌ๋ฅผ ๊ตฌํํ ์ถ๊ฐ์ ์ธ ์ฝ๋๊ฐ ํ์ํ๋ค.
subscription.onDone(()=>print('done')); // ์๋ฃ
subscription.onData((data)=>print('data=$data')); //onData๋ฅผ ์ฌ์ฉํ๋ฉด listen์์ ํจ์๋ฅผ ์คํํ์ง ์๊ณ onData ์์ ํจ์๋ฅผ ์ธํฐ์
ํฐํ์ฌ ์คํํ๋ค.
//๋๋ค๋ฅธ ๋ฐฉ๋ฒ์ด ์๋๋ฐ Stream listenํจ์ ์์ onError ๋ฑ์ ๋งค๊ฐ๋ณ์๋ก ์ง์ํ๋ค.
//StreamSubscription ๋์์ ํ๋ค.
stream.listen((data)=>print('data=$data'),
onError: ()=>print('error'),
onDone: ()=>print('done'),
);
์ฃผ์ ํด์ผํ ์ ์ stream.cancel์ ์ ์ฝ์ด ์๋ค๋ ๊ฒ์ด๋ค.
Stream<int> stm() async*{
int count = 5;
for (int c = 1; c <= count; c++) {
for (int i = 0; i < 100000; i++) {
for (int j = 0; j < 15000; j++) {
100000 * 100;
}
}
yield c;
}
}
void main() async {
print('ready');
StreamSubscription? subscription = null;
Future.delayed(Duration(seconds: 1), (){
subscription!.cancel();
print('cancel');
});
subscription = stm().listen((event) => print('listen:$event'));
print('end');
}
/* ์ถ๋ ฅ
ready
end
listen:1
...
listen:5
cancel
*/
delay๊ฐ 1์ด๊ฐ ์ง๋๋ฉด cancel๋๊ฒ ํ๋ค. ๊ทธ๋ฆฌ๊ณ listen๋ณด๋ค ๋จผ์ ์คํํ๋ค. 1์ด๊ฐ ์ถฉ๋ถํ ์ง๋ฌ์์๋ ๋ถ๊ตฌํ๊ณ ์ถ๋ ฅ๋ด์ฉ์ ๋ณด๋ฉด cancel์ด ๋งจ ๋ง์ง๋ง์ ๋์จ๋ค.
Stream์ ์ปดํจํ
์์
์ ํ๊ฒ๋๋ฉด Block๊ฐ ๊ฑธ๋ฆฌ๊ฒ ๋๋ค. ์ดํ cancel ์ฝ๋๋ Stream ์์
์ดํ ๋ถ๋ฆฌ๊ฒ ๋๋ค. ๋ฐ๋ผ์ cancel์ Future๋ก ํธ์ถํ๋ค ํ๋๋ผ๋ ๋งจ๋ง์ง๋ง์ ํธ์ถ๋์ด cancel ๋์ง ์๋๋ค.
์์ ๋ด์ฉ์ผ๋ก 2๊ฐ์ง๋ฅผ ์์ ์๋ค.
- Stream ์์ ์ฝ๋๊ฐ ๋ค์คํ ๋ ๋๊น์ง Block์ด ๊ฑธ๋ฆฐ๋ค.
- Stream์ ๊ด๋ จ ์ฝ๋๋ ํด๋น ์ฝ๋๋ธ๋ญ์ ๋งจ ๋ง์ง๋ง์ ์คํ๋๋ค.
Stream์ ์ฌ์ค ์์ ํ๊ฒ ๋น๋๊ธฐ๋ก ๋์ํ์ง ์๋ ๊ฒ์ ๋ณผ์ ์๋ค. ๊ฐ์ ๋ ๋ฒจ์ ์ฝ๋ ๋ธ๋ญ์ ๋งจ ๋์ค์ ์คํ๋๊ธฐ ๋๋ฌธ์ ๋น๋๊ธฐ ๊ฐ์ ๋๋์ด ๋๋ ๊ฒ์ด๋ค.
์๋๋ Stream์ listen์ด ๋น๋๊ธฐ๊ฐ ์๋๋ผ ๋งจ ์ฝ๋๋ธ๋ญ์ด ๋๋๊ณ ํธ์ถ๋๋ ์์ ์ด๋ค.
Stream<int> stm() async* {
int count = 5;
for (int c = 1; c <= count; c++) {
for (int i = 0; i < 100000; i++) {
for (int j = 0; j < 15000; j++) {
100000 * 100;
}
}
yield c;
}
}
void main() async {
print('ready');
StreamSubscription subscription = stm().listen((event) => print('listen:$event'));
for (int c = 1; c <= 3; c++) {
for (int i = 0; i < 100000; i++) {
for (int j = 0; j < 15000; j++) {
100000 * 100;
}
}
print("code:$c");
}
print('end');
/* ์ถ๋ ฅ
ready
code:1
code:2
code:3
end
listen:1
...
listen:t
/*
}
๊ฒฐ๋ก ์ ์ผ๋ก Stream์ Future์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ฐ์ฐ์ฝ๋๋ ๋น๋๊ธฐ๋ก ์คํ๋์ง ์๋๋ค. Stream์ ์ฐ์ฐ์ฝ๋๊ฐ ๋ง๋ค๋ฉด ํ๋ก๊ทธ๋จ์ด block์ด ๊ฑธ๋ ค ์คํ์ด ๋๋ ค์ง์ ์๋ค. ์๊ฐ delay๋ ์ฌ์ฉ์ ์ ๋ ฅ ๋ฆฌ์ค๋ ๊ฐ์ ์ฐ์ฐ์์ด ์ฌ์ฉํด์ผ ์ข๋ค. ์ฐ์ฐ์ ๋ํ ๋น๋๊ธฐ๊ฐ ํ์ํ๋ค๋ฉด ๋ฆฌ์ค๋ ํ isolate๋ฅผ ์ด์ฉํ์ฌ ๋น๋๊ธฐ๋ฅผ ์คํํ์ฌ์ผ ํ ๊ฒ์ด๋ค.
Note : Stream์ ๊ฐ์ ๋ ๋ฒจ์ ์ฝ๋ค ๋ธ๋ญ์ ์ปดํจํ ๋งํผ block์ด ๊ฑธ๋ฆฐ๋ค.
์๋๋ ์ฐ์ฐ์์ด time delay์ด๋ก ๋๊ธฐํ ์๋ฆผ์ ์ฃผ๋ ๊ตฌ๋ ํํ์ ์ฌ๋ฐ ์คํธ๋ฆผ ์์ ์ด๋ค.
Stream<int> stm() async*{
for(int i=1;i<=10;i++){
await Future.delayed(Duration(seconds:1));
yield i;
}
}
void main() async{
print('ready');
StreamSubscription subscription= stm().listen((event){print('$event์ด');});
//3์ดํ ๊ตฌ๋
์ ์ทจ์ํ๋ค.
Future.delayed(Duration(seconds:3), ()=>subscription.cancel());
print('end');
}
/* ์ถ๋ ฅ
ready
1์ด
2์ด
end
*/
Stream ์์ฒด๋ฅผ ์ต์ ๋ฒ ํํ๋ก ์ฌ์ฉํ๊ธฐ์๋ ์ข ์ด๋ ค์์ด ์๋ค. UI๋ ํน์ ์ด๋ฒคํธ๋ฅผ ์ต์ ๋น ํ๋ ค ํ ๋๋ ์ฌ์ฉํ๊ธฐ ํ๋ค๋ค. ์๋ ์ฝ๋์ฒ๋ผ ์ฌ์ฉ์์ input๊ฐ์ ๋ฐ์ ์ฒ๋ฆฌํ๋ค๊ณ ๊ฐ์ ํ์.
Stream<int> stm(int count) async*{
yield count;
}
int input = 1;
stm(input).listen((event) => print('listen:$event'));
input = 2;
stm(input).listen((event) => print('listen:$event'));
input = 3;
stm(input).listen((event) => print('listen:$event'));
์ฌ๊ธฐ์๋ ๋๊ฐ์ง ๋ฌธ์ ์ ์ด ์๋ค.
- ์ฌ์ฉ์ input์ ๋ฏธ๋ฆฌ ์ ํด์ ธ ์์ง ์๊ธฐ ๋๋ฌธ์ ์ฐ์ฐ์์ญ(Stream ๊ฐ์ฒด)์ ์ฒ๋ฆฌ์์ญ(listen)์ ๋ถ๋ฆฌ ํ ์ ์๋ค.
- input์ด ๋ค์ด ์ฌ๋๋ง๋ค listen์ ํด์ค์ผํ๋ค.
๋ฐ์ดํฐ ์์ญ๊ณผ UI์์ญ์ด ๋ถ๋ฆฌ๋ ์ํ์์ ํน์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๊ณ ์ถ์ ๋๊ฐ ์๋ค. ๋ค์ ๋งํด ๋จ์ํ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ ๋ฐ๋ ์ต์ ๋ฒ ํํ์ Stream์ด ํ์ํ ๋๊ฐ ๋ง๋ค. ์ต์ ๋ฒ์ ์กฐ๊ฑด์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ๋ ๊น์ง ๊ธฐ๋ค๋ฆฌ๋ '๋๊ธฐ'์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ๋ ๋ง๋ค ์๋ ค์ฃผ๋ '์๋ฆผ(๊ตฌ๋
)' ํ๋ค๋ ์กฐ๊ฑด์ด ์๋ค.
์ ์ฝ๋๋ฅผ ๋ณด๋ฉด ์ฌ์ฉ์์ ์
๋ ฅ์ ๋ฐ๋ฅธ ๋๊ธฐ๋ ํ ์ ์์ผ๋ฉฐ ํ๋์ Stream์ listen ํ๋ฒ๋ง ํ์ฉ๋๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ์ ์
๋ ฅ์ด ๋ฐ์ํ ๋ ๋ง๋ค ์๋ฆผ์ ์๋ ์๋ค.
StreamController๋ ๊ตฌ๋ ์ ๋ฐ๋ฅธ ๋๊ธฐ์ ์์์ ์ธ ์๋ฆผ์ ๋ฐ์์ ์๋ ์ต์ ๋ฒ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
StreamController<int> streamController = StreamController<int>();
streamController.stream.listen((event) => print('listen:$event'));
streamController.sink.add(1);
streamController.sink.add(2);
streamController.sink.add(3);
//์ฌ์ฉ์ด ๋ค๋๋๋ฉด ๋ฐ๋์ ์ข
๋ฃ ํด์ค์ผํจ
streamController.close();
/* ์ถ๋ ฅ
listen:1
listen:2
listen:3
*/
StreamControllerํด๋์ค ๋ด๋ถ stream์ด ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค. stream ๊ฐ์ฒด์ listen์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ตฌ๋ ํ ์ ์๋ค. ์ดํ ํ์ํ ๋ ๋ง๋คstreamController.sink.add ํธ์ถํ์ฌ listenํ๋ ๊ณณ์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ ์์๋ค. sink.add(data)์ ๋งค๊ฐ๋ณ์(data)๊ฐ listen((event)..)์ event๋ก ์ ๋ฌ๋๋ค.
Note : StreamController.add์ sink.add์ ์ฐจ์์
streamController.add์ Sink.add๋ ๋์ผํ ๊ธฐ๋ฅ์ ํ๋๋ฐ ๋๊ฐ์ง์ ๊ธฐ๋ฅ์ ์ฐจ์ด์ ์ ์๋ค.
๋ค๋ง streamController.sink๋ StreamSink์ ์ธํฐํ์ด์ค๋ก ๊ฐ์ฒด๋ก sink์ ๊ธฐ๋ฅ๋ค๋ง ์ฌ์ฉํ ์ ์๊ฒ ๋๋ค.
์ฐธ๊ณ ์๋ฃ : https://api.dart.dev/stable/2.5.0/dart-async/StreamController/sink.html
streamController์ stream์ ๋ฌดํ์ ๋๊ธฐํ๋ฉฐ ์ด๋ฒคํธ๋ฅผ ๊ธฐ๋ค๋ฆฐ๋ค. ๋ฐ๋ผ์ ํ์๊ฐ ์์์ ๋๊ธฐ๋ฅผ ์ข
๋ฃํด์ฃผ๋ streamController.close๋ฅผ ๋ฐ๋์ ์ข
๋ฃ ํด์ค์ผ ํ๋ค.
close๋ก Stream์ ๋ซ์์๋ ๋์ด์ ์ฌ์ฉํ ์ ์๊ณ add๋ฅผ ํ๊ฒ๋๋ฉด 'Cannot add new events after calling close' ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
Note : streamController.close๋ Future๋ฅผ ๋ฆฌํดํ๋๋ฐ Stream์ด ์ฆ์ ์ข ๋ฃ๋์ง ์์ ์ ์์์ ์๋ฏธํ๋ค. Stream์ ์ข ๋ฃ ์ดํ ํ์ํ ์์ ์ Future์ then์ ์ฌ์ฉํ๋ ๊ฒ์ด ์์ ํ ๊ฒ์ด๋ค.
Stream์ ํ๊ณณ์์๋ง ๊ตฌ๋
์(listen) ํ ์ ์๋ค. Broadcast๋ก ์ฌ๋ฌ๊ณณ์์ ๊ตฌ๋
์ ํ ์ ์๋ค.
StreamController.broadcast()๋ก StreamController๊ฐ์ฒด๋ฅผ ์์ฑํ๋ฉด ์ฌ๋ฌ๊ณณ์์ listen์ ํ ์ ์๋ค.
StreamController<int> streamController = StreamController.broadcast();
streamController.stream.listen((event) => print('listen1:$event'));
streamController.stream.listen((event) => print('listen2:$event'));
streamController.sink.add(1);
streamController.sink.add(2);
streamController.close();
/* ์ถ๋ ฅ
listen1:1
listen2:1
listen1:2
listen2:2
*/
UIํ๋ฉด์ StatelessWidget๊ณผ StatefulWidget์ผ๋ก ์ํ๊ด๋ฆฌ์ ๋ฐ๋ผ widget์ ๋๋๊ฒ ๋๋ค.
์ฃผ๊ธฐ์ ์ผ๋ก ์ํ์ ๋ฐ๋ผ UIํ๋ฉด์ ๊ฐฑ์ ํ๊ธฐ ์ํด Stream์ ์ฌ์ฉํ ์ ์๋ค.
์๋๋ Stream์ ๊ฐ์ ๋ฐ๋ผ ์์์ด ๋ณํ๋ ์์ ์ด๋ค.
class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _MyApp();
}
}
class _MyApp extends State<MyApp> {
Color color = Colors.red;
Stream<Color> chageColor() async* {
await Future.delayed(Duration(seconds: 3));
yield Colors.blue;
await Future.delayed(Duration(seconds: 3));
yield Colors.yellow;
}
@override
void initState() {
super.initState();
chageColor().listen((newColor) {
setState(() {
color = newColor;
});
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Container(
width: 300,
height: 300,
color: color,
),
),
),
);
}
}
์ ์ฝ๋์ ์คํ ๊ธฐ์ค์ผ๋ก ๋จ๊ณ๋ฅผ ๋ณด๋ฉด 3๊ฐ์ง๊ฐ ์๋ค.
- Stream ๊ฐ์ฒด ์์ฑ
- Stream Listen
- ๋ฐ์ดํฐ ์์ ์ setState ์ฌ์ฉ
๋ฐ์ดํฐ๋ฅผ ๋์ ์ผ๋ก ์์ ํ๊ณ UI๋ฅผ ๊ฐฑ์ ํ๊ธฐ ์ํด์๋ StatefulWidget์ ์จ์ผํ๋ค. ๋ฐ์ดํฐ ์์ ์ ์ํ ๋ณ๊ฒฝ์ ์ํด setState๋ฅผ ํ์์ ์ผ๋ก ํธ์ถํ ๊ฒ์ด๋ค. setState๋ Stateํด๋์ค์ ํจ์์ด๊ธฐ ๋๋ฌธ์ ๋ฐ์ดํฐ ์์ ๋ถ๋ฅผ ๋ค๋ฅธ ํด๋์ค๋ ํ์ผ๋ก ๋ถ๋ฆฌํ๊ฒ ๋๋ค๋ฉด State๊ฐ์ฒด๋ฅผ ๋๊ฒจ์ค์ผ ํ ๊ฒ์ด๋ค. ์ด๋ ์ ์ฒด์ ์ธ ๊ตฌ์กฐ๋ฅผ ์ก๋๋ฐ ๋ถํธํ ๋ฟ๋ง ์๋๋ผ ์ฝ๋๊ฐ ๋ณต์กํด ์ง๊ณ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ฌ์ง๊ฐ ์๋ค. ์ด ๋ฐ์๋ Widget์ด dispose๋ ๋ ์ทจ์๋ ํด์ค์ผํ๋ค.
Flutter์์ ์ด๋ฐ ๋ฐ๋ณต์ ์ธ ์ผ์ ๋์ ์ฃผ๋ ํจํด์ด ์๋๋ฐ ๋ฐ๋ก Builder์ด๋ค. Flutter๋ ์ฌ๋ฌ๊ฐ์ง Builder๋ค์ ์ ๊ณตํ๋๋ฐ ๊ทธ ์ค ํ๋๊ฐ StreamBuilder์ด๋ค.
Stream์ ์ฌ์ฉํ๋ฉด ๋งค๋ฒ initState์ listen์ฝ๋๋ฅผ ๋ฃ๊ณ dispose์ cancel์ ๋ฃ์ด์ผ ํ๋ค. ๊ทธ๋ฆฌ๊ณ ํ๋ฉด์ ๊ฐฑ์ ํ๊ธฐ ์ํด setState๋ ํธ์ถ ํด์ค์ผํ๋ค. ๊ฐ์ ์ฝ๋๋ฅผ ๋ฐ๋ณตํ๋ค ๋ณด๋ฉด ์ฌ์ฌ์ฉํ๊ธฐ ์ํ ๊ณตํตํด๋์ค๋ฅผ ๋ง๋ค๊ธฐ ๋ง๋ จ์ด๋ค. Flutter์์๋ ์ด๋ฏธ StreamBuilder๋ก ๋ง๋ค์ด ๋จ๋ค.
StreamBuilderํด๋์ค๋ StatefulWidget์ ์์ ํ์ผ๋ฉฐ ๋ฐ๋ผ์ Widget๊ณ Widget์ฒ๋ผ ์ฌ์ฉํ๋ฉด ๋๋ค. StatefulWidget์ ์ํ์ ๊ด๋ จ๋ ๋ผ์ดํ ์ธ์ดํด๋ ๊ฐ์ง๊ณ ์๋ค๋ ๋ป๋ ๋๋ค. ๋ด๋ถ์ ์ผ๋ก initState์ dispose์ ๊ตฌ๋
,์ทจ์๋ฅผ ๊ตฌํํด ๋จ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ฐ์ดํฐ ์์ ๋ถ์ setState๋ฅผ ํธ์ถํด์ค๋ค.
์๋๋ StreamBuilder๋ฅผ ์ฌ์ฉํ ์์ด๋ค
class MyApp extends StatelessWidget {
Color? color = Colors.red;
Stream<Color> chageColor() async* {
await Future.delayed(Duration(seconds: 3));
yield Colors.blue;
await Future.delayed(Duration(seconds: 3));
yield Colors.yellow;
await Future.delayed(Duration(seconds: 3));
}
late Stream<Color> stm;
MyApp() {
stm = chageColor();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: StreamBuilder<Color>(
stream: stm,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.none) {
print('none');
} else if (snapshot.connectionState == ConnectionState.waiting) {
print('waiting');
} else if (snapshot.connectionState == ConnectionState.active) {
print('active:${snapshot.data}');
color = snapshot.data;
} else if (snapshot.connectionState == ConnectionState.done) {
print('done');
}
return Container(
width: 300,
height: 300,
color: color,
);
},
),
),
),
);
}
}
StreamBuilder์ builder์ ๊ตฌํ๋ ๋ด์ฉ์ ConnectionState ์ํ์ ๋ฐ๋ผ ์ฌ๋ฌ๋ฒ ํธ์ถ๋๋ค. ๋ฆฌํด๊ฐ์ผ๋ก๋ Widget์ ๋ฆฌํด ํด์ผํ๋ค.
์ํ์ ๋ฐ๋ผ ์์ ฏ์ ๊ฐฑ์ ํ๋ ํจํด์ด๋ผ ๋ณผ์ ์๋ค.
์ฃผ์ ํด์ผํ ์ ์ builder๊ฐ ์ฌ๋ฌ๋ฒ ํธ์ถ๋ ์ ์๋ค๋ ๊ฒ์ด๋ค. ๊ทธ๋์ ์ฐ์ฐ์ด๋ ๊ฐ์ ์ฐธ์กฐ์ ๊ฐ์ ๋ณ๊ฒฝํ๋ ์์
์ ํ๋ค๋ณด๋ฉด ์์์น ๋ชปํ ์ผ์ด ๋ฐ์ํ ์ ์๋ค.
๋ฐ๋ผ์ Stream์์๋ Widget์ ๊ฒฐ์ ํ๋ ๋จ์ํ ์ํ๊ฐ๋ง ๋ณด๋ด๊ณ builder์์๋ ์ํ์ ๋ฐ๋ผ Widget์ ๊ฐฑ์ ํ๋ ๋ก์ง๋ง ํฌํจํ๋๊ฒ์ด ์์ ํ๋ค.
- SteamController.onListen : SteamController๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ๋ง ํด๋น. ์ด๊ธฐํ ์์ ์ ํ ๋ ์ ์ฉํ๋ค
- none : Stream๊ณผ ์ฐ๊ฒฐ์ด ์ค๋น๋์ง ์์ ์ํ
- waiting : Stream์ด ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ์ํ
- done : Stream์ด ์ข ๋ฃ๋ ์ํ
์ ์์ ์ฝ๋์๋ ConnectionState๊ฐ active์ผ๋๋ง ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ ๋ฐ์๋ค. ํ์ง๋ง ์๋์ฒ๋ผ ๋ง์ง๋ง์ delay ์ฝ๋๋ฅผ ์ญ์ ํ๋ค๋ฉด ์ด์ผ๊ธฐ๊ฐ ๋ค๋ฅด๋ค.
Stream<Color> chageColor() async* {
await Future.delayed(Duration(seconds: 3));
yield Colors.blue;
await Future.delayed(Duration(seconds: 3));
yield Colors.yellow;
// await Future.delayed(Duration(seconds: 3)); //delay ์ฝ๋ ์ญ์
}
...
StreamBuilder<Color>(
stream: stm,
builder: (context, snapshot) {
...
if (snapshot.connectionState == ConnectionState.done) {
// done์ ์๋ก์ด ๋ฐ์ดํฐ๊ฐ ๋ณด๋ด์ง
print('done:${snapshot.data}');
}
...
Stream์ด ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๊ณ ๋ฐ๋ก ๋น ๋ฅด๊ฒ ์ข
๋ฃ ๋๋ค๋ฉด active๊ฐ ์๋ done์ ๋ฐ์ดํฐ๊ฐ ์ค๋ ค ๋ณด๋ด ์ง๋ค. ๋ฐ๋ผ์ Stream์ด ๋น ๋ฅด๊ฒ ์ข
๋ฃ๊ฐ ๋๋์ง ์๋์ง๋ฅผ ํ์ธํ๊ณ data์ ๋ํ ๋์์ ํด์ผํ๋ค.
์ด๋ฐ์ผ์ ์๋นํ ๋จธ๋ฆฌ๋ฅผ ์ํ๊ฒ ํ๋ค. ๋ฐ๋ผ์ ๋๋์ฑ์ด builder์๋ ๋จ์ํ ์ํ์ ๋ฐ๋ฅธ Widget๋ง ๋๊ธฐ๋ ๋ก์ง๋ง ๋จ๊ธฐ๊ณ done ์ํ์๋ data์ ํฌ๊ฒ ์ฐ๊ด์ฑ ์๊ฒ ๊ตฌ์ฑํ๋ ๊ฒ์ด ์ข๋ค. ์๋๋ฉด active, done๊ณผ ์๊ด์์ด data์๋ง ๋ฐ์ํ๋๋ก ๊ตฌ์ฑํ๋๊ฒ์ด ์ข๋ค.
์ ์์ ๋ ConnectionState์ ์๊ด์๊ณ data์๋ง ์ฐ๊ด์ด ์๊ธฐ ๋๋ฌธ์ ์๋ ์ฝ๋์ฒ๋ผ ๋ฐ๊พธ๋๊ฒ์ด ์ข๋ค.
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: StreamBuilder<Color>(
stream: stm,
builder: (context, snapshot) {
if(snapshot.hasData){
color = snapshot.data;
}
return Container(
width: 300,
height: 300,
color: color,
);
},
),
),
),
);
}
์ํ๋ฅผ ์ ๋ฌํ๋ ๊ฒ์ ์์ด StreamController๋ฅผ ์ฌ์ฉํ๋ฉด ์ข๋ ์ง๊ด์ ์ผ๋ก StreamBuilder์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ ์์๋ค. StreamController๋ฅผ.sink.add ํตํด ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๋ก ๋ณด๋ผ ์ ์๋ค.
class MyApp extends StatelessWidget {
Color? color = Colors.red;
StreamController<Color> controller = StreamController();
MyApp(){
controller.onListen = () async{
print('onListen');
await Future.delayed(Duration(seconds: 3));
controller.sink.add(Colors.cyan);
await Future.delayed(Duration(seconds: 3));
controller.sink.add(Colors.amber);
controller.close();
};
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: StreamBuilder<Color>(
stream: controller.stream,
builder: (context, snapshot) {
if(snapshot.hasData){
color = snapshot.data;
}
return Container(
width: 300,
height: 300,
color: color,
);
},
),
),
),
);
}
}
StreamController๋ฅผ ์ข ๋ฃํ์ง ์์ผ๋ฉด ํ์ํ ๋ ์ํ๋ฅผ ์ ์กํ ์ ์์ด ์ ์ฉํ๋ค. UI์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์๋ ๋ณ๊ฒฝ์ด ํ์ํ Widget์ ์ํ๋ฅผ ๋ฐ๊ฟ์ค ์ ์๋ค.
class MyApp extends StatelessWidget {
Color? color = Colors.red;
StreamController<Color> controller = StreamController();
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: StreamBuilder<Color>(
stream: controller.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
color = snapshot.data;
}
return InkWell(
child: Container(
width: 300,
height: 300,
color: color,
),
onTap: (){
//ํด๋ฆญ ํ์๋ ์์์ ๋ฐ๊ฟ์ค
if(color == Colors.red){
controller.sink.add(Colors.blue);
}else{
controller.sink.add(Colors.red);
}
},
);
},
),
),
),
);
}
}
์์ ์ฝ๋๋ ์ ๋์ํ์ง๋ง ํ๊ฐ์ง ๋ฌธ์ ๊ฐ ์๋ค. StreamController์ฌ์ฉํ๋ฉด ๋ฐ๋์ close๋ฅผ ํด์ค์ผํ๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋์ ๋ณดํต ํ๋ฉด์ด dispose ์์ ์ closeํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ StreamController๋ฅผ ์ฌ์ฉํ ๋์๋ StatelessWidget์ ์จ์ผํ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
์๋์ฒ๋ผ StreamController๋ฅผ ๋ฐ๋์ ์ข
๋ฃํด ์ค์ผ ํ๋ค.
Note: ๋ณดํต StatelessWidget dispose ์์ ์ StreamController๋ฅผ ์ฌ์ฉํ์ง ์์์ผ๋ก dispose์ close๋ฅผ ํ๋ค.
class MyApp extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return _MyApp();
}
}
class _MyApp extends State<MyApp> {
Color? color = Colors.red;
StreamController<Color> controller = StreamController();
StreamBuilder<int> b = StreamBuilder(builder: (context, snapshot) => Text(''));
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: StreamBuilder<Color>(
stream: controller.stream,
builder: (context, snapshot) {
if (snapshot.hasData) {
color = snapshot.data;
}
return InkWell(
child: Container(
width: 300,
height: 300,
color: color,
),
onTap: (){
//ํด๋ฆญ ํ์๋ ์์์ ๋ฐ๊ฟ์ค
if(color == Colors.red){
controller.sink.add(Colors.blue);
}else{
controller.sink.add(Colors.red);
}
},
);
},
),
),
),
);
}
@override
void dispose() {
//๋ฐ๋์ close๋ฅผ ํด์ผํ๋ค.
controller.close();
super.dispose();
}
}
๋จ์ผ StatefulWidget๋ง๋ค๊ณ ์ฑ์ ์ข
๋ฃํ๋ฉด dispose๊ฐ ํธ์ถ๋์ง ์๋ ๊ฒ์ ๋ณผ์ ์๋ค.
dispose ํธ์ถ์ ํ๋ฉด์ด ์ฌ๋ผ์ง๋๋ผ๊ณ ์๊ฐํ๊ธฐ ์ฝ์ง๋ง ์ฌ์ค Navigator์ pop์ด ๋์์๋ ํธ์ถ๋๋ค.
๋ฌธ์ ๋ ์ฒซํ์ด์ง์ ๊ฐ์ ๊ฒฝ์ฐ dispose๊ฐ ํธ์ถ๋์ง ์๋ ๊ฒ์ด๋ค. Android Activity๊ฐ ์ข
๋ฃ๋๋ ์์ ์ ์์์ผ ๋๊ธฐ ๋๋ฌธ์ Activity ๋ผ์ดํ ์ฌ์ดํด์ ๊ฐ์ ธ์ค๋ ์ถ๊ฐ์ ์ธ ์ฝ๋๊ฐ ํ์ํ๋ค.
์ฐธ๊ณ ๋ฌธ์ : https://docs.flutter.dev/get-started/flutter-for/android-devs#how-do-i-listen-to-android-activity-lifecycle-events