예시 코드
시나리오: 아래의 4개 버튼 클릭 시, progress bar 보여주고 2초후에 text 변경과 함께 progress bar 가 사라진다.
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Builder(builder: (_) {
if (loadedData == null) {
return const SizedBox();
}
if (loadedData!.isEmpty) {
return const CircularProgressIndicator();
}
return Text(loadedData!);
}),
const SizedBox(
height: 10,
),
ElevatedButton(onPressed: onTapMethodCall, child: const Text('Call Method')),
const SizedBox(
height: 10,
),
ElevatedButton(
onPressed: onTapMethodCallWithFutureDelay, child: const Text('Call Method With Future Delay')),
const SizedBox(
height: 10,
),
ElevatedButton(onPressed: onTapMethodCallWIthIsolate, child: const Text('Call Method With Isolate')),
const SizedBox(
height: 10,
),
ElevatedButton(onPressed: onTapCancelIsolate, child: const Text('Cancel Method')),
],
)
아래 버튼 종류
1) Call Method :
Future<void> onTapMethodCall() async {
setState(() {
loadedData = '';
});
sleep(const Duration(milliseconds: 2000));
setState(() {
loadedData = 'just sleep over';
});
}
2) Call Method With Future Delay :
Future<void> onTapMethodCallWithFutureDelay() async {
setState(() {
loadedData = '';
});
await Future.delayed(const Duration(milliseconds: 2000));
setState(() {
loadedData = 'just sleep over';
});
}
3) Call Method With Isolate :
Future<void> onTapMethodCallWIthIsolate() async {
setState(() {
loadedData = '';
});
_isolate = await Isolate.spawn<SendPort>(sleepApp, _receivePort.sendPort);
}
4) Cancel Method :
void onTapCancelIsolate() {
_isolate.kill(priority: Isolate.immediate);
setState(() {
loadedData = 'Task canceled';
});
}
과연 결과는?
1) Call Method : 2초동안 UI block 되고, 2초후에 text 변경 성공
2) Call Method With Future Delay : UI block 없이 text 변경 성공
3) Call Method With Isolate : UI block 없이 text 변경 성공
4) Cancel Method : 3번 실행도중 호출 시, 3번 작업 cancel 성공
⚠️ 주의사항
Isolate 함수 에러가 발생할 수 있다.
Isolate 함수 안에 사용되는 첫번째 인자는 별도의 메모리 공간에서 실행된다는 것을 명심해야 한다.
즉, 메모리가 공유되지 않기 때문에 최상위 함수거나 static 함수로 독립적으로 존재해야 한다.
Isolate.spawn<SendPort>(sleepApp, _receivePort.sendPort);
에러가 났을 때, Chat GPT 답변 =>
Dart에서 Isolate를 사용할 때, Isolate로 전달되는 함수는 정적(static) 함수이거나 최상위 함수여야 합니다. 이 규칙을 지키지 않으면 오류가 발생합니다. 그 이유는 다음과 같습니다:
1. Isolate의 독립성: Isolate는 별도의 메모리 공간에서 실행됩니다. 따라서, Isolate로 전달되는 함수는 클래스의 인스턴스 상태나 컨텍스트에 의존할 수 없습니다. 이는 Isolate가 다른 Isolate 또는 메인 프로그램의 상태를 직접 접근할 수 없도록 보장하기 위함입니다.
2. 함수 시리얼라이제이션: Isolate는 함수 및 데이터를 시리얼라이즈하여 전달합니다. 클래스의 인스턴스 메서드는 인스턴스와 연결된 상태를 포함하고 있어서 시리얼라이즈하기 어렵습니다. 반면, 정적 함수나 최상위 함수는 이런 상태가 없기 때문에 쉽게 시리얼라이즈할 수 있습니다.
3. 성능 및 안정성: 독립된 메모리 공간에서 실행되는 Isolate는 상태 공유로 인한 부작용을 방지하여 성능과 안정성을 향상시킵니다.
따라서, sleepApp 함수를 static으로 정의하면 Isolate가 이를 독립적으로 실행할 수 있기 때문에 오류가 발생하지 않습니다.
전체코드 참고
class _MyHomePageState extends State<MyHomePage> {
String? loadedData;
late Isolate _isolate;
final _receivePort = ReceivePort();
@override
void initState() {
super.initState();
_receivePort.listen((message) {
setState(() {
loadedData = message.toString();
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Builder(builder: (_) {
if (loadedData == null) {
return const SizedBox();
}
if (loadedData!.isEmpty) {
return const CircularProgressIndicator();
}
return Text(loadedData!);
}),
const SizedBox(
height: 10,
),
ElevatedButton(onPressed: onTapMethodCall, child: const Text('Call Method')),
const SizedBox(
height: 10,
),
ElevatedButton(
onPressed: onTapMethodCallWithFutureDelay, child: const Text('Call Method With Future Delay')),
const SizedBox(
height: 10,
),
ElevatedButton(onPressed: onTapMethodCallWIthIsolate, child: const Text('Call Method With Isolate')),
const SizedBox(
height: 10,
),
ElevatedButton(onPressed: onTapCancelIsolate, child: const Text('Cancel Method')),
],
),
));
}
Future<void> onTapMethodCall() async {
setState(() {
loadedData = '';
});
sleep(const Duration(milliseconds: 2000));
setState(() {
loadedData = 'just sleep over';
});
}
Future<void> onTapMethodCallWithFutureDelay() async {
setState(() {
loadedData = '';
});
await Future.delayed(const Duration(milliseconds: 2000));
setState(() {
loadedData = 'just sleep over';
});
}
Future<void> onTapMethodCallWIthIsolate() async {
setState(() {
loadedData = '';
});
_isolate = await Isolate.spawn<SendPort>(sleepApp, _receivePort.sendPort);
}
void onTapCancelIsolate() {
_isolate.kill(priority: Isolate.immediate);
setState(() {
loadedData = 'Task canceled';
});
}
static void sleepApp(SendPort sendPort) {
sleep(const Duration(milliseconds: 2000));
sendPort.send("Completion");
}
}
'flutter' 카테고리의 다른 글
Flutter Mvvm Clean Architecture Guide (0) | 2024.09.13 |
---|---|
flutter go_router vs auto_route (0) | 2024.09.11 |
Provider 라이브러리 사용 (0) | 2020.02.23 |
How Flutter renders Widget (0) | 2019.11.23 |
Mac에 Fultter로 Android 개발하기 - 2 (0) | 2019.10.18 |