Skip to main content

Integrating existing Flutter packages into your Flet app

Work in progress

The guide is being updated.

Introduction

While Flet controls leverage many built-in Flutter widgets, enabling the creation of even complex applications, not all Flutter widgets or third-party packages can be directly supported by the Flet team or included within the core Flet framework.

To address this, the Flet framework provides an extensibility mechanism. This allows you to incorporate widgets and APIs from your own custom Flutter packages or third-party libraries directly into your Flet application.

Prerequisites

To integrate custom Flutter package into Flet you need to have basic understanding of how to create Flutter apps and packages in Dart language and have Flutter development environment configured. See Flutter Getting Started for more information about Flutter and Dart.

Creating Flet extension

Flet extension that integrates 3rd-party Flutter package consists of the following parts:

  1. Flet Dart package.

  2. Flet Python control.

The Flet Dart package includes a mechanism to create Flutter widgets based on control names returned by the Control's _get_control_name() function. This mechanism iterates through all third-party packages and returns the first matching widget.

Flet Python control is a Python class that you will use in your Flet program.

For example, take a look at a basic Flet extension for flutter_spinkit package.

Flet Dart package

To create a new Dart package, run the following command:

flutter create --template=package <package_name>

You will see this folder structure:

├── CHANGELOG.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── lib
│   └── <package_name>.dart
├── pubspec.lock
├── pubspec.yaml
├── test
│   └── <package_name>_test.dart
└── <package_name>.iml

In the lib folder, you need to create src folder with two files: create_control.dart and <control_name>.dart:

└── <package_name>
├── lib
│ ├── <package_name>.dart
│ └── src
│ ├── create_control.dart
│ └── <control_name>.dart
└── pubspec.yaml

pubspec.yaml

A yaml file containing metadata that specifies the package's dependencies.

In your pubspec.yaml you should add dependency to flet and Flutter package for which you are creating your extension.

In the Flet Spinkit example, pubspec.yaml contains dependency to flutter_spinkit:

dependencies:
flet: ^0.22.0
flutter_spinkit: ^5.2.1

<package_name>.dart

Extension package should export two methods:

  • createControl - called to create a widget that corresponds to a control on Python side.
  • ensureInitialized - called once on Flet program start.
library <package_name>;

export "../src/create_control.dart" show createControl, ensureInitialized;

create_control.dart

Flet calls createControl for all controls and returns the first matching widget.

import 'package:flet/flet.dart';

import 'spinkit.dart';

CreateControlFactory createControl = (CreateControlArgs args) {
switch (args.control.type) {
case "spinkit":
return SpinkitControl(
parent: args.parent,
control: args.control,
);
default:
return null;
}
};

void ensureInitialized() {
// nothing to initialize
}

<control-name>.dart

Here you create Flutter "wrapper" widget that will build Flutter widget or API that you want to use in your Flet app.

Wrapper widget passes the state of Python control down to a Flutter widget, that will be displayed on a page, and provides an API to route events from Flutter widget back to Python control.

import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';

class SpinkitControl extends StatelessWidget {
const SpinkitControl({
super.key,
});

@override
Widget build(BuildContext context) {
return const SpinKitRotatingCircle(
color: Colors.red,
size: 100.0,
);
}
}

As a proof of concept, we would like to see the hardcoded SpinKitRotatingCircle in our Flet program, and later we will get to customizing its properties.

Flet Python control

Flet Python control is a Python class that you can create in your Flet app or as an external Python module. In the Flet Spinkit example, we created Spinkit class in /controls/spinkit.py file:


from flet_core.control import Control


class Spinkit(Control):
"""
Spinkit Control.
"""

def __init__(self):
Control.__init__(self)

def _get_control_name(self):
return "spinkit"

The minumal requirements for this class is that it has to be inherited from Flet Control and it has to have _get_control_name method that will return the control name. This name should be the same as args.control.type we check in the create_control.dart file.

Connect your Python app and Dart package

Once you have created Flet Dart package and Flet Python control, create a Python program in main.py that uses it:

import flet as ft
from controls.spinkit import Spinkit

def main(page: ft.Page):
page.vertical_alignment = ft.MainAxisAlignment.CENTER
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER

page.add(Spinkit())


ft.app(main)

Let's run this simple app with flet run command. We expect to see the hardcoded SpinKitRotatingCircle on the page but that's not happening yet. Instead, we see this message in place of the Spinkit control:

Our Flet app doesn't know yet about the new Flet Dart package that we created.

To connect your Python app and new Flet Dart package, you need create to pubspec.yaml file on the same level as main.py. It should have the following contents:

dependencies:
flet_spinkit:
path: {absolute-path-to-flet-dart-package-folder}
Work in progress

This approach is subject to change and the guide is being updated.

Now you need to build the app for the platform of your choice by running flet build command, for example:

flet build macos

Finally, we open the built app:

open build/macos/flet_spinkit_app.app

You can find source code for this example here.

info

Every time you need to make changes to Python or Dart part of your extension, you need to re-run build command.

Customize properties

In the example above, Spinkit control creates a hardcoded Flutter widget. Now let's customize its properties.

Flet Control properties

When we created Spinkit class in Python, it inherited from Flet Control class that has properties common for all controls such as visible, opacity and tooltip, to name a few. See reference for the common Control properties here.

To be able to use these properties for your new control you need to add the Control properties you want to use in the constructor for your new Python control:

from typing import Any, Optional

from flet_core.control import Control, OptionalNumber

class Spinkit(Control):
"""
Spinkit Control.
"""

def __init__(
self,
#
# Control
#
opacity: OptionalNumber = None,
tooltip: Optional[str] = None,
visible: Optional[bool] = None,
data: Any = None,
):
Control.__init__(
self,
tooltip=tooltip,
opacity=opacity,
visible=visible,
data=data,
)

def _get_control_name(self):
return "spinkit"

In <control-name>.dart file, wrap your widget into baseControl() to magically implement all Python's Control properties:

import 'package:flet/flet.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';

class SpinkitControl extends StatelessWidget {
final Control? parent;
final Control control;

const SpinkitControl({
super.key,
required this.parent,
required this.control,
});

@override
Widget build(BuildContext context) {
return baseControl(
context,
const SpinKitRotatingCircle(
color: Colors.green,
size: 100.0,
),
parent,
control);
}
}

Finally, use Control properties in your app:

import flet as ft
from controls.spinkit import Spinkit


def main(page: ft.Page):
page.vertical_alignment = ft.MainAxisAlignment.CENTER
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER

page.add(Spinkit(opacity=0.5, tooltip="Spinkit tooltip"))


ft.app(main)

You can find source code for this example here.

Flet ConstrainedControl properties

Generally, there are three types of controls in Flet:

  1. Visual Controls that are added to the app/page surface, such as Spinkit.
  2. Popup Controls (dialogs, pickers, panels etc.).
  3. Non-visual Controls or services that are added to overlay, such as Video or Audio.

In the most cases, Visual Controls could inherit from ConstrainedControl that has many additional properties such as top and left for its position within Stack and a bunch of animation properties.

To use those properties, inherit your control from CostrainedControl and add those properties to the constructor of your Python control:

from typing import Any, Optional

from flet_core.constrained_control import ConstrainedControl
from flet_core.control import OptionalNumber


class Spinkit(ConstrainedControl):
"""
Spinkit Control.
"""

def __init__(
self,
#
# Control
#
opacity: OptionalNumber = None,
tooltip: Optional[str] = None,
visible: Optional[bool] = None,
data: Any = None,
#
# ConstrainedControl
#
left: OptionalNumber = None,
top: OptionalNumber = None,
right: OptionalNumber = None,
bottom: OptionalNumber = None,
):
ConstrainedControl.__init__(
self,
tooltip=tooltip,
opacity=opacity,
visible=visible,
data=data,
left=left,
top=top,
right=right,
bottom=bottom,
)

def _get_control_name(self):
return "spinkit"

In <control-name>.dart file, use constrainedControl method to wrap Flutter widget:

import 'package:flet/flet.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';

class SpinkitControl extends StatelessWidget {
final Control? parent;
final Control control;

const SpinkitControl({
super.key,
required this.parent,
required this.control,
});

@override
Widget build(BuildContext context) {
return constrainedControl(
context,
const SpinKitRotatingCircle(
color: Colors.green,
size: 100.0,
),
parent,
control);
}
}

Use ConstrainedControl properties in your app:

import flet as ft
from controls.spinkit import Spinkit


def main(page: ft.Page):
page.vertical_alignment = ft.MainAxisAlignment.CENTER
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER

page.add(
ft.Stack(
[
ft.Container(height=200, width=200, bgcolor=ft.Colors.BLUE_100),
Spinkit(opacity=0.5, tooltip="Spinkit tooltip", top=0, left=0),
]
)
)


ft.app(main)

You can find source code for this example here.

Control-specific properties

Now that you have taken full advantage of the properties Flet Control and ConstrainedControl offer, let's define the properties that are specific to the new Control you are building.

In the Spinkit example, let's define its color and size.

In Python class, define new color and size properties:

from typing import Any, Optional

from flet_core.constrained_control import ConstrainedControl
from flet_core.control import OptionalNumber


class Spinkit(ConstrainedControl):
"""
Spinkit Control.
"""

def __init__(
self,
#
# Control
#
opacity: OptionalNumber = None,
tooltip: Optional[str] = None,
visible: Optional[bool] = None,
data: Any = None,
#
# ConstrainedControl
#
left: OptionalNumber = None,
top: OptionalNumber = None,
right: OptionalNumber = None,
bottom: OptionalNumber = None,
#
# Spinkit specific
#
color: Optional[str] = None,
size: OptionalNumber = None,
):
ConstrainedControl.__init__(
self,
tooltip=tooltip,
opacity=opacity,
visible=visible,
data=data,
left=left,
top=top,
right=right,
bottom=bottom,
)

self.color = color
self.size = size

def _get_control_name(self):
return "spinkit"

# color
@property
def color(self):
return self._get_attr("color")

@color.setter
def color(self, value):
self._set_attr("color", value)

# size
@property
def size(self):
return self._get_attr("size")

@size.setter
def size(self, value):
self._set_attr("size", value)

In <control-name>.dart file, use helper methods attrColor and attrDouble to access color and size values:

import 'package:flet/flet.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';

class SpinkitControl extends StatelessWidget {
final Control? parent;
final Control control;

const SpinkitControl({
super.key,
required this.parent,
required this.control,
});

@override
Widget build(BuildContext context) {
var color = control.attrColor("color", context);
var size = control.attrDouble("size");

return constrainedControl(
context,
SpinKitRotatingCircle(
color: color,
size: size ?? 50,
),
parent,
control);
}
}

Use color and size properties in your app:

import flet as ft
from controls.spinkit import Spinkit


def main(page: ft.Page):
page.vertical_alignment = ft.MainAxisAlignment.CENTER
page.horizontal_alignment = ft.CrossAxisAlignment.CENTER

page.add(
ft.Stack(
[
ft.Container(height=200, width=200, bgcolor=ft.Colors.BLUE_100),
Spinkit(
opacity=0.5,
tooltip="Spinkit tooltip",
top=0,
left=0,
color=ft.Colors.PURPLE,
size=150,
),
]
)
)

ft.app(main)

You can find source code for this example here.

Examples for different types of properties and events

Enum properties

For example, clip_behaviour for AppBar.

In Python:

# clip_behavior
@property
def clip_behavior(self) -> Optional[ClipBehavior]:
return self._get_attr("clipBehavior")

@clip_behavior.setter
def clip_behavior(self, value: Optional[ClipBehavior]):
self._set_attr(
"clipBehavior",
value.value if isinstance(value, ClipBehavior) else value,
)

In Dart:

var clipBehavior = Clip.values.firstWhere(
(e) =>
e.name.toLowerCase() ==
widget.control.attrString("clipBehavior", "")!.toLowerCase(),
orElse: () => Clip.none);
Json properties

For example, shape property for Card.

In Python:

def before_update(self):
super().before_update()
self._set_attr_json("shape", self.__shape)

# shape
@property
def shape(self) -> Optional[OutlinedBorder]:
return self.__shape

@shape.setter
def shape(self, value: Optional[OutlinedBorder]):
self.__shape = value

In Dart:

var shape = parseOutlinedBorder(control, "shape")
Children

For example, content for AlertDialog:

In Python:

    def _get_children(self):
children = []
if self.__content:
self.__content._set_attr_internal("n", "content")
children.append(self.__content)
return children

In Dart:

    var contentCtrls =
widget.children.where((c) => c.name == "content" && c.isVisible);
Events

For example, on_click event for ElevatedButton.

In Python:

# on_click
@property
def on_click(self):
return self._get_event_handler("click")

@on_click.setter
def on_click(self, handler):
self._add_event_handler("click", handler)

In Dart:

Function()? onPressed = !disabled
? () {
debugPrint("Button ${widget.control.id} clicked!");
if (url != "") {
openWebBrowser(url,
webWindowName: widget.control.attrString("urlTarget"));
}
widget.backend.triggerControlEvent(widget.control.id, "click");
}
: null;

Examples

A few Flet controls are implemented as in external packages and could serve as a starting point for your own controls: