안녕하세요? 이번주 키워드는 mounted, streambuilder, isar, stream 였는데 그 중에서도 streambuilder를 알아보겠습니다.
streambuilder와 stream은 떼어놓을 수 없는 관계이기 때문에 함께 정리해볼게요👀
스트림 개념정리
비동기 데이터 전송을 할 때 쓰이는 클래스입니다. 이벤트들을 순차적으로 제공하는 방법을 제공하며 각 이벤트들은 스트림의 요소에 해당하는 데이터 이벤트이거나 오류가 발생했음을 알려주는 오류 이벤트입니다. 스트림이 모든 이벤트들을 내보냈을 때 done이벤트로 리스너에게 끝에 도달했음을 알립니다.
async 함수를 호출해서 스트림을 생성하고 스트림을 반환합니다. stream을 모두 소비하면 종료되고 스트림이 닫힐 때까지 이벤트를 내보내는 기능이 수행됩니다. 스트림을 소비하려면 async나 async* 와 await을 사용하거나 async 함수 내에서 yield*를 사용해서 직접 이벤트를 전달합니다.
예시코드
Stream<T> optionalMap<T>(
Stream<T> source , [T Function(T)? convert]) async* {
if (convert == null) {
yield* source;
} else {
await for (var event in source) {
yield convert(event);
}
}
}
함수가 호출되면 스트림이 즉시 Stream 객체가 반환됩니다. 그런다음 누군가가 그 스트림을 소비하려고 할 때까지는 아무 일도 일어나지 않습니다. 이 시점에서 비동기 함수의 본문이 실행되기 시작합니다. convert 함수가 생략된 경우 stream을 수신하고 yield는 모든 이벤트(date, error)를 반환된 스트림으로 전달합니다. 스트림이 닫히면 yield*도 완료되고 optionalMap 본문도 종료됩니다.
반환된 스트림이 닫힙니다. convert가 제공되면 함수가 대신 stream을 수신하고 다음 데이터 이벤트를 반복적으로 기다리는 await 대기 루프에 들어갑니다. 오류 이벤트가 발생하지 않으면 스트림이 발생할 때 루프가 종료되고 optionalMap 함수가 완료되면 반환된 스트림이 닫힙니다. 스트림의 오류 이벤트가 발생할 경우 해당 오류를 재투입하여 루프를 차단합니다. 그런 다음 오류가 감지되지 않기 때문에 optionalMap 함수 끝에 도달하고 그러면 반환된 스트림에서 오류가 발생한 후 닫힙니다.
스트림의 종류
두 가지 유형 single-subscription, broadcast이 있습니다.
single-subscription은 스트림의 전체 수명 동안 single listener만 허용합니다. listener가 있기 전까지는 이벤트생성을 시작하지 않고 listener가 unsubscribed 되면 제공할 이벤트가 더 많더라도 전송을 중지합니다. stream은 *async 함수에 의해 생성되고 함수에 호출될 때마다 이러한 스트림이 새로 생성됩니다. 첫번째 subscription이 취소되고 나면 두번 재생할 수 없습니다. 일반적으로 파일 입출력같은 큰 연속 데이터에 사용됩니다.
braodcast는 listener 수에 관계없이 임의의 수를 listener로 허용하며 listener가 있는지 여부에 관계없이 이벤트를 발생시킵니다.
두 종류의 스트림에서 where 및 skip 같은 스트림 변환은 별도로 언급하지 않는 한 메서드가 호출된 스트림과 동일한 유형의 스트림을 변환합니다.
이벤트가 발생하면 listener가 이벤트를 수신합니다. 이벤트가 발생하는 동안 listener가 취소되면 해당 listener는 현재 발생 중인 이벤트를 수신하지 않습니다. 완료 이벤트가 발생하면 이벤트를 수신하기 전에 subscribers가 등록을 취소합니다. 이벤트가 전송된 후 스트림에는 subscriber가 없습니다.
isBroadcast의 기본 구현은 false를 반환하고 스트림에서 상속되는 브로드캐스트 스트림은 동작한다는 신호를 보내려면 isBroadcast를 재정의해야 합니다.
StreamBuilder 클래스
위젯 재구성은 State.setState를 사용하여 각 상호 작용데 의해 스케줄링되지만 그렇지 않으면 스트림의 타이밍과 분리됩니다. builder는 flutter 파이프라인에 따라 호출되므로 스트림과의 상호 작용을 나타내는 snapshot의 타이밍 종속 하위 시퀀스를 수신합니다.
예시코드
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
final Stream<int> _bids = (() {
late final StreamController<int> controller;
controller = StreamController<int>(
onListen: () async {
await Future<void>.delayed(const Duration(seconds: 1));
controller.add(1);
await Future<void>.delayed(const Duration(seconds: 1));
await controller.close();
},
);
return controller.stream;
})();
@override
Widget build(BuildContext context) {
return DefaultTextStyle(
style: Theme.of(context).textTheme.displayMedium!,
textAlign: TextAlign.center,
child: Container(
alignment: FractionalOffset.center,
color: Colors.white,
child: StreamBuilder<int>(
stream: _bids,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
List<Widget> children;
if (snapshot.hasError) {
children = <Widget>[
const Icon(
Icons.error_outline,
color: Colors.red,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('Error: ${snapshot.error}'),
),
Padding(
padding: const EdgeInsets.only(top: 8),
child: Text('Stack trace: ${snapshot.stackTrace}'),
),
];
} else {
switch (snapshot.connectionState) {
case ConnectionState.none:
children = const <Widget>[
Icon(
Icons.info,
color: Colors.blue,
size: 60,
),
Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Select a lot'),
),
];
break;
case ConnectionState.waiting:
children = const <Widget>[
SizedBox(
width: 60,
height: 60,
child: CircularProgressIndicator(),
),
Padding(
padding: EdgeInsets.only(top: 16),
child: Text('Awaiting bids...'),
),
];
break;
case ConnectionState.active:
children = <Widget>[
const Icon(
Icons.check_circle_outline,
color: Colors.green,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('\$${snapshot.data}'),
),
];
break;
case ConnectionState.done:
children = <Widget>[
const Icon(
Icons.info,
color: Colors.blue,
size: 60,
),
Padding(
padding: const EdgeInsets.only(top: 16),
child: Text('\$${snapshot.data} (closed)'),
),
];
break;
}
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: children,
);
},
),
),
);
}
}
참고했습니다.
https://api.flutter.dev/flutter/dart-async/Stream-class.html
아직은 설명이 더 어렵게 느껴지네요 실제로 더 사용하면서 문서를 봐야 감이 잡힐 것 같습니다.🤔