1. 네트워크 프로그래밍
1) JSON 파싱하기
(1) JSON 데이터 디코딩과 인코딩하기
- dart:convert 패키지를 이용하여 JSON → Map 형식으로 변경
- Map 형식으로 변환
Map<String, dynamic> map = jsonDecode(jsonStr);
setState(() {
result = "decode : id: ${map['id']}, title: ${map['title']}, completed: ${map['completed']}"
});
- List 형식으로 변환([{}, {}]형식일 시)
List list = jsonDecode(jsonStr);
- Json인코딩
result = "encode : ${jsonEncode(map)}"
result = "encode : ${jsonEncode(list)}"
(2) 모델 클래스로 JSON 데이터 이용하기
- 클래스에 변수명과 각 타입이 지정된 채로 받음
class Todo {
int id;
String title;
bool completed;
// 생성자
Todo(this.id, this.title, this.completed);
// 생성자2
Todo.fromJson(Map<String, dynamic> json)
: id = json['id'], title = josn['title'], completed = json['completed'];
Map<string, dynamic> toJson() => {
'id': id,
'title': title,
'completed': completed
};
}
Map<String, dynamic> map = jsonDecode(jsonStr);
Todo todo = Todo.fromJson(map);
✨코드 예시
import 'dart:convert';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class Todo {
int id;
String title;
bool completed;
Todo(this.id, this.title, this.completed);
Todo.fromJson(Map<String, dynamic> json)
: id = json['id'],
title = json['title'],
completed = json['completed'];
Map<String, dynamic> toJson() =>
{'id': id, 'title': title, 'completed': completed};
}
class MyAppState extends State<MyApp> {
String jsonStr = '{"id": 1, "title": "HELLO", "completed": false}';
Todo? todo;
String result = '';
onPressDecode() {
Map<String, dynamic> map = jsonDecode(jsonStr);
todo = Todo.fromJson(map);
setState(() {
result =
"decode : id: ${todo?.id}, title: ${todo?.title}, completed: ${todo?.completed}";
});
}
onPressEncode() {
setState(() {
result = "encode : ${jsonEncode(todo)}";
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Test')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$result'),
ElevatedButton(
onPressed: onPressDecode, child: Text('Decode')),
ElevatedButton(
onPressed: onPressEncode, child: Text('Encode')),
],
),
)));
}
}
(3) JSON 데이터 자동 매핑하기 - json_serializable
- pubspec.yaml
dependencies:
json_annotation: ^4.5.0
dev_dependencies:
build_runner: ^2.1.11
json_serializable: ^6.2.0
- 패키지 임포트
import 'package:json_annotation/json_annotation.dart';
import 'test_json_serializable.g.dart';
- 모델 클래스 파일
@JsonSerializable()
class Todo {
@JsonKey(name: "id")
int todoId;
String title;
bool completed;
Todo(this.todoId, this.title, this.completed);
factory Todo.fromJson(Map<String, dynamic> json) => _$TodoFromJson(json);
Map<String, dynamic> toJson() => _$TodoToJson(json);
}
- terminal 입력
flutter pub run build_runner build
- 중첩 클래스 매핑(Location 클래스)
@JsonSerializable()
class Location {
String latitude;
String longitude;
Location(this.latitude, this.longitude);
factory Location.fromJson(Map<String, dynamic> json) => _$LocationFromJson(json);
Map<String, dynamic> tojson() => _$LocationToJson(this);
}
- 중첩 클래스 매핑(Todo 클래스)
@JsonSerializable
class Todo {
@JsonKey(name: "id")
int todoId;
String title;
bool completed;
Location location; // 중첩 클래스
Todo(this.todoId, this.title, this.completed, this.location);
factory Todo.fromJson(Map<String, dynamic> json) => _$TodoFromJson(json);
Map<String, dynamic> toJson() => _$TodoToJson(this);
}
- Todo 객체 출력하기
Map<String, dynamic> map = jsonDecode(jsonStr);
todo = Todo.fromJson(map);
print(todo?.toJson());
- Location 객체에 담긴 값 출력하기
@JsonSerializable(explicitToJson: true)
class Todo{
Location location; // 중첩 클래스
}
✨코드 예시
- pubspec.yaml
dependencies:
json_annotation: ^4.5.0
dev_dependencies:
build_runner: ^2.1.11
json_serializable: ^6.2.0
- test.dart
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:json_annotation/json_annotation.dart';
part 'test.g.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return MyAppState();
}
}
@JsonSerializable()
class Location {
String latitude;
String longitude;
Location(this.latitude, this.longitude);
factory Location.fromJson(Map<String, dynamic> json) =>
_$LocationFromJson(json);
Map<String, dynamic> toJson() => _$LocationToJson(this);
}
@JsonSerializable(explicitToJson: true)
class Todo {
@JsonKey(name: "id")
int todoId;
String title;
bool completed;
Location location;
Todo(this.todoId, this.title, this.completed, this.location);
factory Todo.fromJson(Map<String, dynamic> json) => _$TodoFromJson(json);
Map<String, dynamic> toJson() => _$TodoToJson(this);
}
class MyAppState extends State<MyApp> {
String jsonStr = '{"id" : 1, "title" : "HELLO", "completed" : false, "location" : {"latitude":"37.5", "longitude":"127.1"}}';
Todo? todo;
String result = '';
onPressDecode() {
Map<String, dynamic> map = jsonDecode(jsonStr);
todo = Todo.fromJson(map);
print(todo?.toJson());
setState(() {
result = "decode : ${todo?.toJson()}";
});
}
onPressEncode() {
setState(() {
result = "encode : ${jsonEncode(todo)}";
});
}
@override
Widget build(BuildContext context) {
return Materia력
```bash
flutter pub run build_runner build
- test.g.dart(생성된 파일)
part of 'test.dart';
Location _$LocationFromJson(Map<String, dynamic> json) => Location(
json['latitude'] as String,
json['longitude'] as String,
);
Map<String, dynamic> _$LocationToJson(Location instance) => <String, dynamic>{
'latitude': instance.latitude,
'longitude': instance.longitude,
};
Todo _$TodoFromJson(Map<String, dynamic> json) => Todo(
json['id'] as int,
json['title'] as String,
json['completed'] as bool,
Location.fromJson(json['location'] as Map<String, dynamic>),
);
Map<String, dynamic> _$TodoToJson(Todo instance) => <String, dynamic>{
'id': instance.todoId,
'title': instance.title,
'completed': instance.completed,
'location': instance.location.toJson(),
};
2) http 패키지 이용하기
- pubspec.yaml
dependencies:
http: ^0.13.4
- 패키지 임포트
import 'package:http/http.dart' as http;
- 서버에 요청하기
http.Response response =
await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));
- 서버에서 전달한 데이터 얻기
if (response.statusCode == 200) {
String result = response.body;
}
- 헤더 이용하기
Map<String, String> headers = {
"content-type" : "application/json",
"accept" : "application/json"
};
http.Response response =
await http.get(Uri.parse('https://jsonplaceholder.typicode.com/post/1'),
headers: headers);
- POST 방식으로 요청
http.Response response =
await http.post(Uri.parse('https://jsonplaceholder.typicode.com/posts'),
body : {'title' : 'hello', 'body' : 'world', 'userId' : 1});
- 반복해서 요청하기
var client = http.Client();
try {
http.Response response =
await client.post(Uri.parse('https://jsonplaceholder.typicode.com/posts'),
body: {'title' : 'hello', 'body' : 'world', 'userId' : '1'});
if (response.statusCode == 200 || response.statusCode == 201) {
response = await client.get(
Uri.parse('https://jsonplaceholder.typicode.com/posts/1')
);
print('response: ${response.body}');
} else {
print('error......');
}
} finally {
client.close();
}
✨코드 예시
- pubspec.yaml
dependencies:
http: ^0.13.4
- test.dart
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
String result = '';
onPressGet() async {
Map<String, String> headers = {
"content-type": "application/json",
"accept": "application/json"
};
http.Response response = await http.get(
Uri.parse('https://jsonplaceholder.typicode.com/posts/1'),
headers: headers);
if (response.statusCode == 200) {
setState(() {
result = response.body;
});
} else {
print('error......');
}
}
onPressPost() async {
try {
http.Response response = await http.post(
Uri.parse('https://jsonplaceholder.typicode.com/posts'),
body: {'title': 'hello', 'body': 'world', 'userId': '1'});
print('statusCode : ${response.statusCode}');
if (response.statusCode == 200 || response.statusCode == 201) {
setState(() {
result = response.body;
});
} else {
print('error......');
}
} catch (e) {
print('error ... $e');
}
}
onPressClient() async {
var client = http.Client();
try {
http.Response response = await client.post(
Uri.parse('https://jsonplaceholder.typicode.com/posts'),
body: {'title': 'hello', 'body': 'world', 'userId': '1'});
if (response.statusCode == 200 || response.statusCode == 201) {
response = await client
.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));
setState(() {
result = response.body;
});
} else {
print('error......');
}
} finally {
client.close();
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Test'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$result'),
ElevatedButton(onPressed: onPressGet, child: Text('GET')),
ElevatedButton(onPressed: onPressPost, child: Text('POST')),
ElevatedButton(
onPressed: onPressClient, child: Text('Client')),
],
),
)));
}
}
3) dio 패키지 이용하기
- pubspec.yaml
dependencies:
dio: ^4.0.6
- GET 방식으로 요청
try {
var response = await Dio().get('https://reqres.in/api/users?page=2');
if (response.statusCode == 200) {
String result = response.data.toString();
print("result... $result");
}
} catch (e) {
print(e);
}
- queryParameters 매개변수로 데이터 전달
var response = await.Dio().get('https://reqres.in/api/users', queryParameters: {'page':2});
- POST방식으로 요청
var response = await Dio().post(
'https://reqres.in/api/users',
data: {
'name': 'kkang',
'job': 'instructor'
}
);
(1) request() 함수로 요청
- request() 함수로 요청
var response = await Dio().request(
'https://reqres.in/api/users',
data: {
'name': 'kkang',
'job': 'instructor'
},
options: Options(method: 'POST')
)
(2) BaseOptions로 Dio 속성 지정하기
- BaseOptions로 Dio 속성 지정하기
var dio = Dio(BaseOptions(
baseUrl: 'https://reqres.in/api/',
connectTimeout: 5000,
receiveTimeout: 5000,
headers: {
HttpHeaders.contentTypeHeader: 'application/json',
HttpHeaders.acceptHeader: 'application/json'
}
))
(3) 동시 요청하기
- 동시 요청하기
List<Response<dynamic>> response =
await Future.wait([dio.get('https://reqres.in/api/users?page=1'),
dio.get('https://reqres.in/api/users?page=2')]);
response.forEach((element) {
if (element.statusCode == 200) {
String result = element.data.toString();
print('result... $result');
}});
(4) 파일 전송하기 - MultifileUpload
- 파일 전송하기
MultipartFile.fromFile('./test.txt', filename:'upload.txt')
- 파일 데이터를 지정해서 전송하기
MultipartFile multipartFile = new MultipartFile.fromBytes(
imageData, // 파일 데이터
filename: 'load_image',
contentType: MediaType('image', 'jpg')
);
- FormData 객체로 파일 전송하기
var formData = FormData.fromMap({
'name':'kkang',
'file': await MultipartFile.fromFile('./test.txt', filename:'upload.txt')
});
var response = await dio.post('/info', data: formData);
(5) 요청이나 응답 가로채기 - Interceptor
- 인터셉터 작성하기
class MyInterceptor extends Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
print('request... ${options.method}, ${options.path}');
print('request data : ${options.data}');
super.onRequest(options, handler); // 서버 요청
}
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
print('response... ${response.statusCode}, ${response.requestOptions.path}');
print('response data : ${response.data}');
super.onResponse(response, handler); // 결괏값 반환
}
@override
void onError(DioError err, ErrorInterceptorHandler handler) {
super.onError(err, handler);
print('error... ${err.response?.statusCode}, ${err.requestOptions.path}');
}
}
- dio에 인터셉터 추가하기
var dio = Dio();
dio.interceptors.add(MyInterceptor());
await dio.post(
'https://reqres.in/api/users',
data: {
'name': 'kkang',
'job': 'instructor'
}
)
- InterceptiorsWrapper 이용하기
dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
print('request... ${options.method}, ${options.path}');
print('request data : ${options.data}');
handler.next(options); // 서버 요청
},
onResponse: (response, handler) {
print('response... ${response.statusCode}, ${response.requestOptions.path}');
print('request data : ${response.data}');
handler.next(response); // 결괏값 반환
}
));
- 서버 대신 응답하기
onRequest: (options, handler) {
print('request... ${options.method}, ${options.path}');
print('request data : ${options.data}');
// handler.next(options); // 서버 요청
handler.resolve(Response(requestOptions: options, data: {'hello':'world'}));
}
- 3초 후 요청하기
onRequest: (options, handler) {
dio.lock();
handler.next(options);
Timer(Duration(seconds: 3), () {
dio.unlock();
});
}
✨코드 예시
- pubspec.yaml
dependencies:
dio: ^4.0.6
- test.dart
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:flutter_lab/ch13/customscrollview_sliverappbar.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
String result = '';
dioTest() async {
try {
var dio = Dio(BaseOptions(
baseUrl: 'https://reqres.in/api/',
connectTimeout: 5000,
receiveTimeout: 5000,
headers: {
HttpHeaders.contentTypeHeader: 'application/json',
HttpHeaders.acceptHeader: 'application/json'
}));
List<Response<dynamic>> response = await Future.wait([
dio.get('https://reqres.in/api/users?page=1'),
dio.get('https://reqres.in/api/users?page=2')
]);
response.forEach((element) {
if (element.statusCode == 200) {
setState(() {
result = element.data.toString();
});
}
});
} catch (e) {
print(e);
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Test'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$result'),
ElevatedButton(
onPressed: dioTest, child: Text('Get Server Data'))
],
),
)));
}
}
2. 퓨처와 스트림으로 비동기 프로그래밍
1) 퓨처와 퓨처 빌더
(1) 퓨처 - Future
- 비동기 프로그래밍을 지원
- 비동그 프로그래밍 : 시간이 오래 걸리는 작업을 실행한 후 끝날 때까지 기다리지 않고 다음 작업을 실행
- 동기 프로그래밍
void sum() {
var sum = 0;
Stopatch stopwatch = Stopwatch();
stopwatch.start();
for (int i = 0; i < 500000000; i++) {
sum += i;
}
stopwatch.stop();
print('${stopwatch.elapsed}, result: $sum')
}
void onPress() {
print('onPress top...');
sum();
print('onPress bottom...');
}
- 비동기 처리
Future<int> sum() {
return Future<int>(() { // 미래의 데이터를 담을 상자 반
var sum = 0;
Stopwatch stopwatch = Stopwatch();
stopwatch.start();
for (int i = 0; i < 500000000; i++) {
sum += i;
}
stopwatch.stop();
print('${stopwatch.elapsed}, result: $sum');
return sum; // 실제 데이터를 상자에 담기
})
}
void onPress() {
print('onPress top...');
sum();
print('onPress bottom...')
}
(2) 퓨처 빌더 - FutureBuilder
- FutureBuilder의 생성자 : 결과 대기 후 화면 출력(자체 화면 보유X)
const Future Builder<t>(
{ Key? key,
Future<T>? future,
T? initialData,
required AsyncWidgetBuilder<T> builder
}
)
- AsyncWidgetBuilder 정의
AsyncWidgetBuilder<T> = Widget Function(
BuildContext context,
AsyncSnapshot<T> snapshot
)
- 퓨처 데이터 출력
body: Center(
child : FutureBuilder(
future : calFun(),
builder : (context, snapshot) {
if (snapshot.hasData) {
return Text('${snapshot.data}');
}
return CircularProgressIndicator();
}))
✨코드 예시
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Future<int> sum() {
return Future<int>(() {
var sum = 0;
for (int i = 0; i < 500000000; i++) {
sum += i;
}
return sum;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Test')),
body: FutureBuilder(
future: sum(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Center(
child: Text('${snapshot.data}',
style: TextStyle(color: Colors.black, fontSize: 30)),
);
}
return Center(
child: Text('waiting',
style: TextStyle(color: Colors.black, fontSize: 30)));
})));
}
}
2) await와 async
- Future에 담은 데이터 가져오기
void onPress() {
print('onPress top...');
Future<int> future = sum();
print('onPress future: $future');
print('onPress bottom...');
}
(1) then() 함수 사용하기
- then() 함수로 Future에 담은 데이터 가져오기
print('onPress top...');
Future<int> future = sum();
future.then((value) => print('onPress then... $value'));
future.catchError((error) => print('onPress catchError... $error'));
print('onPress bottom...');
- 시간이 오래 걸리는 함수 2개
Future<int> funA() {
return Future.delayed(Duration(seconds: 3), () {
return 10;
});
}
Future<int> funB(int arg) {
return Future.delayed(Duration(second: 2), () {
return arg * arg
});
}
- then() 함수 중첩
Future<int> calFun() {
return funA().then((aResult) {
return funB(aResult);
}).then((bResult) {
return bResult;
});
}
(2) await와 async 사용하기
- await, async로 처리
- async : 비동기식 메써드 설정
- await는 Future의 객체가 완료될 때까지 기다림, 해당 객체의 값을 반환
- ✨코드 예시
Future<int> calFun() async {
int aResult = await funA();
int bResult = await funB(aResult);
return bResult;
}
✨코드 예시
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Future<int> funA() {
return Future.delayed(Duration(seconds: 3), () {
return 10;
});
}
Future<int> funB(int arg) {
return Future.delayed(Duration(seconds: 2), () {
return arg * arg;
});
}
Future<int> calFun() async {
int aResult = await funA();
int bResult = await funB(aResult);
return bResult;
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Test')),
body: Center(
child: FutureBuilder(
future: calFun(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Center(
child: Text('${snapshot.data}',
style: TextStyle(color: Colors.black, fontSize: 30)));
}
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 100,
height: 100,
child: CircularProgressIndicator(),
),
Text('waiting...',
style: TextStyle(color: Colors.black, fontSize: 20))
],
));
},
))));
}
}
3) 스트림과 스트림 빌더
(1) 스트림 - Stream
- Stream : (미래에) 반복해서 발생하는 데이터
- 데이터 한 번 반환
Future<int> futureFun() async {
return await Future.delayed(Duration(seconds: 1), () {
return 10;
});
}
void onPress() async {
await futureFun().then((value) => print("result: $value"));
}
- 데이터 5번 반환
Stream<int> streamFun() async* {
for (int i = 1; i <= 5; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
void onPress() async {
await for (int value in streamFun()) {
print("value : $value");
}
}
- listen() 함수로 반환값 여러 번 받기
void onPress() {
streamFun().listen((value) {
print('value : $value');
});
}
(2) 스트림을 만드는 여러 가지 방법
- Iterable 타입 데이터 만들기 - fromIterable()
var stream = Stream.fromIterable([1, 2, 3]);
stream.listen((value) {
print("value : $value");
})
- Future 타입 데이터 만들기 - fromFuture()
Future<int> futureFun() {
return Future.delayed(Duration(seconds: 3), () {
return 10;
});
}
test4() {
var stream = Stream.fromFuture(futureFun());
stream.listen((value) {
print("value : $value");
});
}
- 주기 지정하기 - periodic()
int calFun(int x) {
return x*x;
}
test1() async {
Duration duration = Duration(seconds: 2);
Stream<int> stream = Stream<int>.periodic(duration, calFun);
await for (int value in stream) {
print('value : $value');
}
}
- 횟수 지정하기 - take()
int calFun(int x) {
return x*x;
}
test1() async {
Duration duration = Duration(seconds: 2);
Stream<int> stream = Stream<int>.periodic(duration, calFun); // 2초에 한 번씩 데이터 발생
stream = stream.take(3);
await for (int value in stream) {
print('value : $value');
}
}
- 조건 지정하기 - takeWhile()
int calFun(int x) {
return x * x;
}
test1() async {
Duration duration = Duration(seconds: 2);
Stream<int> stream = Stream<int>.periodic(duration, calFun);
// 조건 설정
stream = stream.takeWhile((value) {
return value < 20;
});
await for (int value in stream) {
print('value : $value');
}
}
- 생략 지정하기 - skip()
int calFun(int x) {
return x * x;
}
test1() async {
Duration duration = Duration(seconds: 2);
Stream<int> stream = Stream<int>.periodic(duration, calFun);
// stream = stream.take(3);
stream = stream.takeWhile((value) {
return value < 20;
});
// 생략 지정 하기
stream = stream.skip(2);
await for (int value in stream) {
print('value : $value');
}
}
- 생략 조건 지정하기 - skipWhile()
int calFun(int x) {
return x * x;
}
test1() async {
Duration duration = Duration(seconds: 2);
Stream<int> stream = Stream<int>.periodic(duration, calFun);
stream = stream.skipWhile((value) {
return value < 50;
});
await for (int value in stream) {
print('value : $value');
}
}
- List 타입으로 만들기 - toList()
int calFun(int x) {
return x * x;
}
test2() async {
Duration duration = Duration(seconds: 2);
Stream<int> stream = Stream<int>.periodic(duration, calFun);
stream = stream.take(3);
Future<List<int>> future = stream.toList(); // list타입으로 만들기
future.then((list) {
list.forEach((value) {
print('value : $value');
});
});
}
(3) 스트림 빌더 - StreamBuilder
- 스트림 빌더 사용하기
body: Center(
child: StreamBuilder(
stream: test(),
builder: (BuildContext context, AsyncSnapshot<int> snapshot {
if (snapshot.hasData) {
return Text('data : ${snapshot.data}');
}
return CircularProgressIndicator();
})
)
)
- AsyncSnapshot의 connectionState 속성
- ConnectionState.waiting : 데이터 발생을 기다리는 상태
- ConnectionState.active : 데이터가 발생하고 있으며 아직 끝나지 않은 상태
- ConnectionState.done : 데이터 발생이 끝난 상태
- 연결 상태 파악하기
Center(
child: StreamBuilder(
stream: test(),
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Text(
'Completed',
style: TextStyle(
fontSize: 30.0
),
);
} else if (snapshot.connectionState == ConnectionState.waiting) {
return Text(
'Waiting For Stream',
style : TextStyle(
fontSize: 30.0
)
);
}
return Text(
'data : ${snapshot.data}',
style : TextStyle(
fontSize: 30.0
)
);
}
)
);
✨코드 예시
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
int calFun(int x) {
return x * x;
}
Stream<int> test() {
Duration duration = Duration(seconds: 3);
Stream<int> stream = Stream<int>.periodic(duration, calFun);
return stream.take(5);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Test')),
body: Center(
child: StreamBuilder(
stream: test(),
builder:
(BuildContext context, AsyncSnapshot<int> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Center(
child: Text('Completed',
style: TextStyle(fontSize: 30.0)));
} else if (snapshot.connectionState ==
ConnectionState.waiting) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 100,
height: 100,
child: CircularProgressIndicator()),
Text('waiting...',
style: TextStyle(fontSize: 20))
]),
);
}
return Center(
child: Text(
'data : ${snapshot.data}',
style: TextStyle(fontSize: 30.0),
));
}))));
}
}
4) 스트림 구독, 제어기, 변환기
(1) 스트림 구독자 - StreamSubscription
- 스트림 데이터 얻기
var stream = Stream.fromIterable([1, 2, 3]);
stream.listen((value) {
print("value : $value");
});
- onError와 onDone 함수
var stream = Stream.fromIterable([1,2,3]);
stream.listen((value) {
print('value : $value');
},
onError: (error) {
print('error : $error');
},
onDone: () {
print('stream done...');
});
- onError와 onDone 따로 정의하기
var stream = Stream.fromIterable([1, 2, 3]);
StreamSubscription subscription = stream.listen(null);
subscription.onData((data) {
print('value : $data');
});
subscription.onError((error) {
print('value : $data');
});
subscription.onDone(() {
print('stream done...');
});
(2) 스트림 제어기 - StreamController
- 스트림 제어기
var controller = StreamController();
var stream1 = Stream.fromIterable([1,2,3]);
var stream2 = Stream.fromIterable(['A','B','C']);
stream1.listen((value) {
contoller.add(value);
});
stream2.listen((value) {
contoller.add(value);
});
controller.stream.listen((value) {
print('$value');
});
- 스트림에 다른 데이터 추가
controller.stream.listen((value) {
print('$value');
});
controller.add(100);
controller.add(200);
- 두 번째 가져오기 실패
var stream1 = Stream.fromIterable([1, 2, 3]);
stream1.listen((value) {print('listen1 : $value');}); // 정상
stream1.listen((value) {print('listen2 : $value');}); // 오류
- 방송용 스트림 제어기 만들기
var controller = StreamController.broadcast();
controller.stream.listen((value) {print('listen1 : $value');});
controller.stream.listen((value) {print('listen2 : $value');});
controller.add(100);
controller.add(200);
- 스트림 변환기
var stream = Stream.fromIterable([1, 2, 3]);
StreamTransformer<int, dynamic> transformer =
StreamTransformer.fromHandlers(handleData: (value, sink) {
print('in transformer... $value');
});
- sink 매개변수 이용하기
var stream = Stream.fromIterable([1, 2, 3]);
StreamTransformer<int, dynamic> transformer = StreamTransformer.fromHandlers(handleData: (value, sink) {
print('in transformer... $value');
});
stream.transform(transformer).listen((value) {
print('in listen... $value');
});
✨코드 예시
import 'dart:async';
import 'dart:html';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
subscriptionTest() {
var stream = Stream.fromIterable([1, 2, 3]);
StreamSubscription subscription = stream.listen(null);
subscription.onData((data) {
print('value : $data');
});
subscription.onError((error) {
print('error : $error');
});
subscription.onDone(() {
print('stream done...');
});
}
controllerTest() {
var controller = StreamController();
var stream1 = Stream.fromIterable([1, 2, 3]);
var stream2 = Stream.fromIterable(['A', 'B', 'C']);
stream1.listen((value) {
controller.add(value);
});
stream2.listen((value) {
controller.add(value);
});
controller.stream.listen((value) {
print('$value');
});
}
transformerTest() {
var stream = Stream.fromIterable([1, 2, 3]);
StreamTransformer<int, dynamic> transformer =
StreamTransformer.fromHandlers(handleData: (value, sink) {
print('in transformer... $value');
sink.add(value * value);
});
stream.transform(transformer).listen((value) {
print('in listen... $value');
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Test'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: Text('subscription'), onPressed: subscriptionTest),
ElevatedButton(
child: Text('controller'), onPressed: controllerTest),
ElevatedButton(
child: Text('transformer'), onPressed: transformerTest),
],
))));
}
}
댓글남기기