Dart was originally a single-threaded programming language. However, many have heard of Isolate. They are often asked about in Flutter developer interviews. For this reason alone, it is worth studying this topic in a little more detail.
Today we will figure out what they are and how they can help us create responsive interfaces that work with 60 fps.
Any Dart code is executed in Isolate. Isolate is an isolated environment, inside which there is memory allocated to it and its EventLoop. Each Isolate is single-threaded and can only manage the memory and EventLoop allocated for it. You cannot control the memory of another Isolate.
Communication between Isolates occurs through the transfer of messages between ports.
So, to use this powerful tool, we need to include the Dart library: isolates
If we look at the documentation, we will see a rather small API that gives us the following main classes to work with:
So there are two surefire cases when you should resort to Isolate.
import 'dart:isolate';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Isolate isolate;
ReceivePort receivePort;
@override
void initState() {
super.initState();
spawnNewIsolate();
}
void spawnNewIsolate() async {
receivePort = ReceivePort();
try {
isolate = await Isolate.spawn(sayHello, receivePort.sendPort);
print("Isolate: $isolate");
receivePort.listen((dynamic message) {
print('New message from Isolate: $message');
});
} catch (e) {
print("Error: $e");
}
}
//spawn accepts only static methods or top-level functions
static void sayHello(SendPort sendPort) {
sendPort.send("Hello from Isolate");
}
@override
void dispose() {
super.dispose();
receivePort.close();
isolate.kill();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Isolate Demo"),
),
body: Center(),
);
}
}
So we figured out how to spawn a new isolate. Now let's look at more convenient ways of working with isolates, and the first of them will be the built-in wrapper over Isolate Api in Flutter - Compute.
Compute slightly expands the capabilities of working with isolates in Flutter and takes on the following responsibilities:
Thus, all you need to use the compute function is to pass the first argument to the function that you want to execute in the isolate, and the second argument to pass those arguments that should go to the executable function.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
void initState() {
super.initState();
createComputeFunction();
}
void createComputeFunction() async {
String answer;
answer = await compute(sayHelloFromCompute, 'Hello');
print("Answer from compute: $answer");
}
static String sayHelloFromCompute(String string) {
return string;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Isolate Demo"),
),
body: Center(),
);
}
}
We have already mastered the skills of working with isolates and know how to use the compute function.
Today there are two third-party libraries for working with isolates.
computer - https://pub.dev/packages/computer
worker_manager - https://pub.dev/packages/worker_manager
You can already familiarize yourself with them in more detail and see examples of work in the documentation.
However, we have not yet looked at the most illustrative example showing the importance of performing complex operations in separate isolates.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
AnimationController rotationController;
Animation animation;
List<int> results = [];
@override
void initState() {
super.initState();
rotationController = AnimationController(
duration: const Duration(seconds: 5),
vsync: this,
)..addListener(() => setState(() {}));
animation = Tween(begin: 0.0, end: 1.0).animate(rotationController);
rotationController.forward(from: 0.0);
//loop the animation for clarity
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
rotationController.repeat();
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Isolate Demo"),
),
body: Column(
children: [
SizedBox(
height: 100,
),
Center(
child: RotationTransition(
turns: animation,
child: Container(
width: 200,
height: 200,
color: Colors.orange,
),
),
),
SizedBox(
height: 100,
),
RaisedButton(
onPressed: () {
setState(() {
final result = fib(40);
print(result);
results.add(result);
});
},
child: Text("fib(40) in main thread"),
),
RaisedButton(
onPressed: () async {
final result = await compute(fib, 40);
setState(() {
results.add(result);
});
},
child: Text("fib(40) in isolate"),
),
Text("Number of results: ${results.length.toString()}")
],
),
);
}
}
int fib(int n) {
if (n < 2) {
return n;
}
return fib(n - 2) + fib(n - 1);
}
Sergey Vedmediev
Flutter Developer at GBKSOFT
Having started my career as a front-end developer; at some point, I got to start learning Flutter. Despite my moderate experience with it, I was able to switch to building beautiful cross-platform applications quickly. I think this technology, and in particular, the Dart language is much more promising and exciting.
What is Isolate?
Isolate - an isolated environment within which Dart code is executed with its own allocated memory (heap) and its own (separate) EventLoop.
What are they needed for?
They are needed to perform complex operations in separate threads and not affect performance in the main thread (main/ui-thread).
How do I use them?
The dart:isolate library in Dart or the compute function in Flutter or third-party Flutter libraries.