Skip to content

Instantly share code, notes, and snippets.

@muabe
Last active March 16, 2022 09:20
Show Gist options
  • Save muabe/2e1cfacf7dae603212f4307861547750 to your computer and use it in GitHub Desktop.
Save muabe/2e1cfacf7dae603212f4307861547750 to your computer and use it in GitHub Desktop.

Stream

๋จผ์ € Stream์„ ์™œ ์“ฐ๋Š”์ง€๋ถ€ํ„ฐ ์ƒ๊ฐํ•ด๋ณด์ž. Stream์€ ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ด์ง€๋ฉด ์ƒ์‚ฐ์ž์—์„œ ์†Œ๋น„์ž๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•œ๋‹ค. ์ด๋Š” ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•˜๋Š” ๋ฐ์ดํ„ฐ์˜ ์ ‘๊ทผ์„ ๋ฐฉ์ง€ํ•ด ์ค€๋‹ค.
๋‘๋ฒˆ์žฌ๋กœ ๋ณดํ†ต ๋ฐ์ดํ„ฐ๋ฅผ ์˜์—ญ๊ณผ UI์˜์—ญ์„ ๋ถ„๋ฆฌํ•˜๋Š”๋ฐ ์ด๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ UI์— ์ „๋‹ฌ ํ•ด์ฃผ๋Š” ๋งค๊ฐœ์ฒด๊ฐ€ ํ•„์š”ํ•  ๊ฒƒ์ด๋‹ค. Stream์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์›”ํ•˜๊ฒŒ ์ „๋‹ฌํ•˜๋Š” ๋งค๊ฐœ์ฒด ์—ญํ• ์„ ํ•œ๋‹ค. Stream์€ ์ƒ์‚ฐ์ž์™€ ์†Œ๋น„์ž๋กœ ์˜์—ญ์„ ๋ถ„๋ฆฌํ•˜์—ฌ ๋จผ ๊ณณ๊นŒ์ง€ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•ด ์ค„ ์ˆ˜ ์žˆ๋‹ค.

  • ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ ์ผ๋•Œ ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ํ•ด์ค€๋‹ค.
  • ๋ฐ์ดํ„ฐ ์˜์—ญ๊ณผ ์ฒ˜๋ฆฌ์˜์—ญ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ๋ถ„๋ฆฌ ํ• ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฅผ ์‰ฝ๊ฒŒ ์ „๋‹ฌํ•ด์ฃผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณต ํ•ด์ค€๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๊ณตํ•˜๊ณ  ์žฌ์ƒ์‚ฐ ํ• ์ˆ˜ ์žˆ์–ด ์›๋ณธ ๋ฐ์ดํ„ฐ์˜ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๊ทน๋Œ€ํ™” ํ•œ๋‹ค.

Iterable : sync*, yeild

๋ฐ์ดํ„ฐ ํ•˜๋‚˜ํ•˜๋‚˜๋ฅผ ์ˆ˜์‹ ํ•˜์—ฌ ์ฒ˜๋ฆฌํ•˜๋ ค ํ• ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์‹ ํ•˜๋ฉด์„œ ๋ณ€๊ฒฝ/๊ฐ€๊ณต ํ• ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋“ค์„ ์ œ๊ณตํ•ด์ฃผ๊ณ  ์žˆ๋‹ค. 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 ๋ฆฌํ„ด์„ ํ•œ์Œ์œผ๋กœ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ ๋‹ค.


Stream : async*, yeild

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๋Š” ๊ฐ’์„ ๋ฆฌํ„ดํ•˜๋Š” ๋ฐ˜๋ฉด 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*์€ ์žฌ๊ท€ ํ˜ธ์ถœ๋กœ ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ๋‹ค.


lazy generation

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์€ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋‚ด์ฃผ๋Š” ํ˜•ํƒœ๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

StreamSubscription

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 ๋˜์ง€ ์•Š๋Š”๋‹ค.

Stream์˜ ์ฝ”๋“œ ์‹คํ–‰ ์ˆœ์„œ

์œ„์˜ ๋‚ด์šฉ์œผ๋กœ 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
*/

StreamController

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

Stream ์ข…๋ฃŒ

streamController์˜ stream์€ ๋ฌดํ•œ์ • ๋Œ€๊ธฐํ•˜๋ฉฐ ์ด๋ฒคํŠธ๋ฅผ ๊ธฐ๋‹ค๋ฆฐ๋‹ค. ๋”ฐ๋ผ์„œ ํ•„์š”๊ฐ€ ์—†์„์‹œ ๋Œ€๊ธฐ๋ฅผ ์ข…๋ฃŒํ•ด์ฃผ๋Š” streamController.close๋ฅผ ๋ฐ˜๋“œ์‹œ ์ข…๋ฃŒ ํ•ด์ค˜์•ผ ํ•œ๋‹ค.
close๋กœ Stream์„ ๋‹ซ์•˜์„๋•Œ ๋”์ด์ƒ ์‚ฌ์šฉํ• ์ˆ˜ ์—†๊ณ  add๋ฅผ ํ•˜๊ฒŒ๋˜๋ฉด 'Cannot add new events after calling close' ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

Note : streamController.close๋Š” Future๋ฅผ ๋ฆฌํ„ดํ•˜๋Š”๋ฐ Stream์ด ์ฆ‰์‹œ ์ข…๋ฃŒ๋˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Œ์„ ์˜๋ฏธํ•œ๋‹ค. Stream์˜ ์ข…๋ฃŒ ์ดํ›„ ํ•„์š”ํ•œ ์ž‘์—…์€ Future์˜ then์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•  ๊ฒƒ์ด๋‹ค.


Broadcast

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
*/

StreamBulider

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๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.

  1. Stream ๊ฐ์ฒด ์ƒ์„ฑ
  2. Stream Listen
  3. ๋ฐ์ดํ„ฐ ์ˆ˜์‹ ์‹œ setState ์‚ฌ์šฉ

๋ฐ์ดํ„ฐ๋ฅผ ๋™์ ์œผ๋กœ ์ˆ˜์‹ ํ•˜๊ณ  UI๋ฅผ ๊ฐฑ์‹ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” StatefulWidget์„ ์จ์•ผํ•œ๋‹ค. ๋ฐ์ดํ„ฐ ์ˆ˜์‹ ์‹œ ์ƒํƒœ ๋ณ€๊ฒฝ์„ ์œ„ํ•ด setState๋ฅผ ํ•„์ˆ˜์ ์œผ๋กœ ํ˜ธ์ถœํ•  ๊ฒƒ์ด๋‹ค. setState๋Š” Stateํด๋ž˜์Šค์˜ ํ•จ์ˆ˜์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ ์ˆ˜์‹ ๋ถ€๋ฅผ ๋‹ค๋ฅธ ํด๋ž˜์Šค๋‚˜ ํŒŒ์ผ๋กœ ๋ถ„๋ฆฌํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด State๊ฐ์ฒด๋ฅผ ๋„˜๊ฒจ์ค˜์•ผ ํ• ๊ฒƒ์ด๋‹ค. ์ด๋Š” ์ „์ฒด์ ์ธ ๊ตฌ์กฐ๋ฅผ ์žก๋Š”๋ฐ ๋ถˆํŽธํ•  ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด ์ง€๊ณ  ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์—ฌ์ง€๊ฐ€ ์žˆ๋‹ค. ์ด ๋ฐ–์—๋„ Widget์ด dispose๋ ๋•Œ ์ทจ์†Œ๋„ ํ•ด์ค˜์•ผํ•œ๋‹ค. Flutter์—์„  ์ด๋Ÿฐ ๋ฐ˜๋ณต์ ์ธ ์ผ์„ ๋Œ€์‹  ์ฃผ๋Š” ํŒจํ„ด์ด ์žˆ๋Š”๋ฐ ๋ฐ”๋กœ Builder์ด๋‹ค. Flutter๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€ Builder๋“ค์„ ์ œ๊ณตํ•˜๋Š”๋ฐ ๊ทธ ์ค‘ ํ•˜๋‚˜๊ฐ€ StreamBuilder์ด๋‹ค.

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์„ ๊ฐฑ์‹ ํ•˜๋Š” ๋กœ์ง๋งŒ ํฌํ•จํ•˜๋Š”๊ฒƒ์ด ์•ˆ์ „ํ•˜๋‹ค.

StreamBuilder ConnectionState ์‹คํ–‰์ˆœ์„œ

  1. SteamController.onListen : SteamController๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ๋งŒ ํ•ด๋‹น. ์ดˆ๊ธฐํ™” ์ž‘์—…์„ ํ• ๋•Œ ์œ ์šฉํ•˜๋‹ค
  2. none : Stream๊ณผ ์—ฐ๊ฒฐ์ด ์ค€๋น„๋˜์ง€ ์•Š์€ ์ƒํƒœ
  3. waiting : Stream์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ƒํƒœ
  4. done : Stream์ด ์ข…๋ฃŒ๋œ ์ƒํƒœ

ConnectionState.done์˜ data

์œ„ ์˜ˆ์ œ ์ฝ”๋“œ์—๋Š” 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 ์‚ฌ์šฉ

์ƒํƒœ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์— ์žˆ์–ด 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 ํ˜ธ์ถœ ์‹œ์ 

๋‹จ์ผ 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


Stream์˜ ๋งŽ์€ ํ•จ์ˆ˜๋“ค take, drain ๋“ฑ๋“ฑ ์•Œ์•„๋ณด๊ธฐ

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment