Asynchronous and parallelism are two different things. Let's take an example. Our brain can do only one thing at a time. It can't handle two different things at the same instant. If you want to talk about multitasking here, it is actually asynchronous in nature. In multitasking, if a job is taking more time, then we move to another work and when the first job is done, we come back to process the output from the first job.
It is simple, right. Till now, it looks like asynchronous processing is the bast way to boost our productivity, right ? Yeah, but not 100% right.
Nowadays, multicore CPU are default in our machines, it can do a lot of different things parallelly. So to use this architecture, we have to make our software in a way to use multicore or multiple thread.
But the thing is that flutter use only one thread, and it does all of its work on a single thread. In this thread, it has to pump 60 Frames Per Second to give a fluid experience to the end user. If we do some heavy work or long-running task in this thread, Then flutter thread will be busy in the heavy work and as a result frame rates of our app will drop and the app looks sluggish, or stutters.
So what to do ? How to do heavy lifting work in out flutter app ? There are many ways to this in flutter. Like writing asynchronous code or using compute function.
But the best way I have found that to use an isolated. If you haven't heard this name before, don't worry. I am here to help.
What is an Isolate in flutter
Isolate is a container which is completely separate from flutter thread and don't share any memory with the app. Isolate means a CPU thread which run in its own sandbox. We can create an isolate from a flutter app and communicate with an isolate by passing messages to and fro with the isolate.
So enough of introduction, let's see how to create and use isolates in flutter.
What we are going to make
Here we will use the basic flutter counter app and implement isolate to generate random numbers. Just like the vanilla flutter counter app, but with isolate. Excited ? Let's dive in.
How to create an Isolate in flutter
To create Isolate in flutter, we need to import dart:isolate
package. We can start an isolate from our main thread with Isolate.spawn
method. This is the entry point to our isolate, and we have to pass a ReceivePort
in Isolate.spawn
method to receive message from the isolate and listen the messages as a stream in the main thread.
Let us create a stateful widget named MyApp
in flutter.
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Counter"),
),
body: Center(child: Text("1")),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
);
}
}
Now, I define the Isolate _isolate
variable in the top of the stateful widget. As we need to listen, the incoming values from the isolate inside the build
function as a stream.
Then in the initState
function in our widget, we start our isolate and pass a ReceivePort
.
@override
void initState() {
spawnIsolate();
super.initState();
}
Future spawnIsolate() async {
ReceivePort _receivePort = ReceivePort();
_isolate = await Isolate.spawn(remoteIsolate, _receivePort.sendPort,
debugName: "remoteIsolate");
}
In the above code, remoteIsolate
is the entry point of the isolate.
We have defined a ReceivePort _receivePort
and send the _receivePort.sendPort
to the created isolate. This sendPort
is the address of the main thread of the application. We have given our isolate a name called remoteIsolate
. This will be helpful to debug our code later.
This remoteIsolate
can use sendPort
to send data to the main thread.
Now inside the remoteIsolate
function, which is the entry point of our isolate, we define the working or functionality of our isolate.
static void remoteIsolate(SendPort sendPort) {
sendPort.send("Hi i am from remote Isolate");
}
We can listen to this message in our main thread using the ReceivePort
.
_receivePort.listen(
(message) {
print(message);
},
);
Till now, I think you have understood the basic working of an Isolate
and how to use ReceivePort
and SendPort
to communicate between isolate and the main thread.
Bidirectional communication between isolate and the main-thread
Bidirectional communication between isolate and the main thread is very useful if we have to use the same isolate repeatedly.
To send a message from the main-thread to our isolate, we need the sendPort
of the isolate. To get that sendPort
, we the isolate is initialize for the first time, we create a ReceivePort
inside the isolate and send the sendPort
to the main thread as a message.
Then we can use this sendPort
of the isolate to send a message from our main thread.
Let's modify our remoteIsolate
function from above.
static void remoteIsolate(SendPort sendPort) {
ReceivePort _isolateReceivePort = ReceivePort();
sendPort.send(_isolateReceivePort.sendPort);
}
Now we have to identify the sendPort
from the rest of the message in our main-thread.
_receivePort.listen(
(message) {
if (message is SendPort) {
message.send("SendPort from Isolate received");
}
},
);
Now we can use this sendPort
to send any message to the isolate. So we can now use bidirectional messaging in flutter isolate and can reuse the isolate over and over again.
Flutter counter app using Isolate
As I have promised before in this article, This is the complete source-code of isolate implementation in the flutter counter app.
import 'dart:isolate';
import 'dart:math';
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: MyApp(),
),
);
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ReceivePort? _receivePort;
Isolate? _isolate;
SendPort? _isolateSendPort;
static void remoteIsolate(SendPort sendPort) {
ReceivePort _isolateReceivePort = ReceivePort();
sendPort.send(_isolateReceivePort.sendPort);
_isolateReceivePort.listen((message) {
if (message == "+") {
sendPort.send(Random().nextInt(100));
}
});
}
Future spawnIsolate() async {
_receivePort = ReceivePort();
_isolate = await Isolate.spawn(remoteIsolate, _receivePort!.sendPort,
debugName: "remoteIsolate");
}
@override
void initState() {
spawnIsolate();
super.initState();
}
@override
void dispose() {
if (_isolate != null) {
_isolate!.kill();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Counter"),
),
body: Center(
child: StreamBuilder(
stream: _receivePort,
initialData: "NoData",
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data is SendPort) {
_isolateSendPort = snapshot.data;
}
return Container(
child: Text(
snapshot.data.toString(),
),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_isolateSendPort!.send("+");
},
child: Icon(Icons.add),
),
);
}
}
If you have like this article, leave a comment. If you want to use a file picker in flutter, you can read this article. Follow me on Twitter to discuss cool new tech.
Top comments (2)
I'm MD. Shahadat Hossain from Bangladesh. I'm a fresher flutter developer. I can fetch data from the Rest API and Firebase as well. And I have MVVM architecture knowledge and I can make eye-catching UI designs. I'm committed and dedicated to my work and always learning something new.
Now I'd like to do a job or internship to increase my work ability and financial support.
Would you mind, if I got help from you about getting the opportunity to work on Flutter?
thank you for the working example code, very little information for intermediate level tutorial.