A Flutter package which wraps a python module.
This example is intended to show how to use the Dart Python FFI in a publishable Flutter package. Package clients can use the wrapped Python module without knowledge of the Python FFI. They simply install and import the package like any other Dart / Flutter package.
- Usage (the final product)
- Prerequisites
- Including the Python module source
- Adding the Python module to the Dart project
- Next step
See flutter_package_import
for an example of how to consume
this Flutter package.
- The dartpip cli should be installed (either globally or as dev-dependency in this Dart project).
The Python module source is a single file basic_adder.py
in the python-modules
directory. It
could be located in any other directory, even outside the project root. This will result in a module
named basic_adder
that we will be importing from Dart.
# python-modules/basic_adder.py
num = int | float
def add(x: num, y: num) -> num:
"""Adds x and y together"""
return x + y
# pubspec.yaml
python_ffi:
modules:
basic_adder:
path: python-modules/basic_adder.py
The following command should be run from the root of your Dart project. It will bundle the Python module into a Dart package.
$ dartpip install
Each Python module needs its corresponding Dart Module-definition.
The install
command will automatically generate a Module-definition in Dart for the Python module.
The generated file will be located at lib/python_modules/basic_adder.g.dart
.
Click to see the generated file
// lib/python_modules/basic_adder.g.dart
// ignore_for_file: camel_case_types, non_constant_identifier_names, prefer_void_to_null
library basic_adder;
import "package:python_ffi/python_ffi.dart";
/// ## num
typedef $num = Object?;
/// ## basic_adder
///
/// ### python source
/// ```py
/// num = int | float
///
/// def add(x: num, y: num) -> num:
/// """Adds x and y together"""
/// return x + y
/// ```
final class basic_adder extends PythonModule {
basic_adder.from(super.pythonModule) : super.from();
static basic_adder import() =>
PythonFfi.instance.importModule(
"basic_adder",
basic_adder.from,
);
/// ## add
///
/// ### python docstring
///
/// Adds x and y together
///
/// ### python source
/// ```py
/// def add(x: num, y: num) -> num:
/// """Adds x and y together"""
/// return x + y
/// ```
Object? add({
required Object? x,
required Object? y,
}) =>
getFunction("add").call(
<Object?>[
x,
y,
],
kwargs: <String, Object?>{},
);
/// ## num (getter)
Object? get $num => getAttribute("num");
/// ## num (setter)
set $num(Object? $num) => setAttribute("num", $num);
}
To make the python module available to other Dart / Flutter projects, we need to export the
generated files. To do this, we modify lib/flutter_package_export.dart
:
// lib/flutter_package_export.dart
library flutter_package_export;
import "package:flutter/material.dart";
import "package:flutter_package_export/python_modules/basic_adder.g.dart";
import "package:python_ffi/python_ffi.dart";
Future<void> initialize() async {
WidgetsFlutterBinding.ensureInitialized();
await PythonFfi.instance.initialize(package: "flutter_package_export");
}
num add(num x, num y) => basic_adder.import().add(x: x, y: y)! as num;
We export an abstract initialization function initialize
which will be called by the package
client. The initialization function will correctly initialize the Python FFI. Due to this
abstraction, the package client does not need to know about the Python FFI.
We also a custom wrapper function add
which will be used by the package client to call the Python
module. The wrapper function will automatically import the Python module and cast the return value
to a Dart type. Again, due to this abstraction, the package client does not need to know about the
Python FFI.
Note: Since we used a typing.Union
type annotation in the Python module (int | float
), the
generated Dart Module-definition will use Object?
as the corresponding Dart type. This is because
Dart does not have a Union
type. The Object?
type is used as a catch-all type for any type
annotation that cannot be converted to a Dart type. To make the API more user-friendly, we can
manually cast the return value to a corresponding Dart type (in this case num
).
The package is now ready to be published and consumed by other Dart / Flutter projects.
See flutter_package_import
for an example of how to consume
this Flutter package.
Converting all supported types between Dart and Python. See
the python_ffi_dart
example.
Importing multiple Python modules in a Flutter app. See
the python_ffi
example.