It’s actually been quite some time since I’ve made a shift to using Python and in general languages and tools that offer a shorter way to test out ideas. But recently I’ve started doing that in a more structured and hopefully sustainable way, that not only is good for extremely fast prototyping, but also for building stable and trustworthy software applications that might (must?) run for a long time. So, to let you in on the trick, in this post, I am going to introduce you to Flython, a library that will help you create applications that use the best of the two worlds of Flutter (Dart) and Python.
How does it work?!
Flython creates a Process and then keeps it warm and running. This makes it easy and fast to keep sending commands to that process all the time, as opposed to re-run the process for every command. In this case, the created Process is supposed to be a Python instance, which essentially means you are sending commands to Python and receiving the results back.
Hence, all of the capabilities of Python are exposed to Dart and Flutter and vice versa.
How to use it?!
First things first, you can get it from the following link:
https://pub.dev/packages/flython
The same link also contains all necessary instructions on how to install and use it but to be clear on all of it, we’re going to take it step by step.
The way I add Flython to my projects is usually quite simple. I add the following entry to my dependencies list in pubspec.yaml
file, as seen below:
dependencies:
flython: ^0.0.6
Obviously, I’m pasting the current version here, which might (and probably will) change in the future. So simply use the latest to be on the safe side.
And in my Dart code, I add the necessary import as seen here:
import 'package:flython/flython.dart';
Now to the tricky part. We must first subclass Flython and use our own subclass, whatever it’s going to be called.
This snippet is from the official example, which shows a subclass of Flython, called OpenCV and it does a very simple task. Converting images from color to grayscale.
import 'package:flython/flython.dart';
class OpenCV extends Flython {
static const cmdToGray = 1;
Future<dynamic> toGray(
String inputFile,
String outputFile,
) async {
var command = {
"cmd": cmdToGray,
"input": inputFile,
"output": outputFile,
};
return await runCommand(command);
}
}
Now we need to create a Python script, which is essentially a command processor. Let’s assume it’s called main.py and the contents of it is as follows:
import argparse
import json
import sys
import cv2
CMD_SYS_VERSION = 0
CMD_TO_GRAY = 1
def run(command):
if command["cmd"] == CMD_SYS_VERSION:
return {
"sys.version": sys.version,
}
if command["cmd"] == CMD_TO_GRAY:
image = cv2.imread(command["input"])
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imwrite(command["output"], gray)
return {}
else:
return {"error": "Unknown command."}
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--uuid")
args = parser.parse_args()
stream_start = f"`S`T`R`E`A`M`{args.uuid}`S`T`A`R`T`"
stream_end = f"`S`T`R`E`A`M`{args.uuid}`E`N`D`"
while True:
cmd = input()
cmd = json.loads(cmd)
try:
result = run(cmd)
except Exception as e:
result = {"exception": e.__str__()}
result = json.dumps(result)
print(stream_start + result + stream_end)
The "__main__"
portion of the script is in fact more or less the same for all programs.
Finally, we can start using the OpenCV class we created. Here is an example:
final opencv = OpenCV();
await opencv.initialize("python", "./main.py", false);
await opencv.toGray("./image.png", "./image_gray.png");
opencv.finalize();
To be able to run this example, obviously you must have Python installed on your computer. Along with the OpenCV packages for Python.
Hopefully, this example will help you get started. If not, a complete example of a different use case for Flython can be found in the following repository:
https://github.com/amahta/clipboard_ocr
Clipboard OCR is a very lightweight and simple application that performs OCR on the image contents of the clipboard. It’s open source and uses Flython for the main OCR task it performs. The code is meant to be very short and easy to understand, but if you have any questions, you can pose them below.