Skip to main content

· 5 min read
Feodor Fitsner

Flet 0.7.1 enables developers changing scroll position and receiving scroll notifications from Page, View, Column, Row, ListView and GridView controls.

The release also introduces theming improvements:

Controlling scroll position

Scrollable controls (Page, View, Column, Row, ListView and GridView) introduce scroll_to() method to change their scroll position to either absolute offset, relative delta or jump to the control with specified key.

Moving to a key is particularly exciting as it allows simulating the navigation between page bookmarks, kind of HTML hrefs with #:

Check the source code of the example above.

See Column.scroll_to for more details about controlling scroll position.

Receiving scroll notifications

All scrollable controls now provide on_scroll event handler which fires when a scroll position is changed. From event object properties you can determine whether scroll operation has started, finished, changed direction or scroll position went behind scrolling extent (overscroll). You can also get updates of the current scroll position as well as dimensions of the scroll area, for example:

import flet as ft

def main(page: ft.Page):
def on_column_scroll(e: ft.OnScrollEvent):
f"Type: {e.event_type}, pixels: {e.pixels}, min_scroll_extent: {e.min_scroll_extent}, max_scroll_extent: {e.max_scroll_extent}"

cl = ft.Column(
for i in range(0, 50):
cl.controls.append(ft.Text(f"Text line {i}", key=str(i)))

ft.Container(cl, border=ft.border.all(1)),

See Column.on_scroll for more details about scroll notification.

Check infinite scroll example.

Color scheme customization

Until today the only way to control color scheme for your application was specifying color_scheme_seed when creating a new ft.Theme object.

This release enables you to fine tune all 30 colors based on the Material spec and used by various Flet controls.

You can even use Material Theme Builder and apply exported color palette to your app, for example:

page.theme = ft.Theme(
# ...

See ColorScheme class for more details.

Nested themes

Another awesome feature of this release is nested themes!

You can have a part of your app to use a different theme or override some theme styles for specific controls.

Remember page object having theme and theme_mode properties? Now Container has theme and theme_mode properties too!

Container.theme accepts the same ft.Theme object as a page. Specifying theme_mode in the container means you don't want to inherit parent theme, but want a completely new, unique scheme for all controls inside the container. However, if the container does not have theme_mode property set then the styles from its theme property will override the ones from the parent, inherited theme:

import flet as ft

def main(page: ft.Page):
# Yellow page theme with SYSTEM (default) mode
page.theme = ft.Theme(

# Page theme
content=ft.ElevatedButton("Page theme button"),

# Inherited theme with primary color overridden
content=ft.ElevatedButton("Inherited theme button"),

# Unique always DARK theme
content=ft.ElevatedButton("Unique theme button"),

Scrollbar theme

You can now customize the look and fill of scrollbars in your application (or a particular scroillbar with nested themes).

It could be done via page.theme.scrollbar_theme property, for example:

page.theme = ft.Theme(
ft.MaterialState.HOVERED: ft.colors.AMBER,
ft.MaterialState.DEFAULT: ft.colors.TRANSPARENT,
ft.MaterialState.HOVERED: ft.colors.RED,
ft.MaterialState.DEFAULT: ft.colors.GREY_300,

Text theming

Material 3 design defines 5 groups of text styles with 3 sizes in each group: "Display", "Headline", "Title", "Label" and "Body" which are used across Flet controls. You can now customize each of those styles with page.theme.text_theme, for example:

import flet as ft

def main(page: ft.Page):
page.theme = ft.Theme(

page.add(ft.Text("Hello, green world!"))

Apparently, Body Medium is used by Text control as a default style.

See TextTheme class for more details.

Tabs theming

You can now control the look and feel of Tabs control. In this release Tabs adds a bunch of new properties and there is a new page.theme.tabs_theme property to style all tabs in your app:

page.theme = ft.Theme(
ft.MaterialState.FOCUSED: ft.colors.with_opacity(0.2, ft.colors.GREEN),
ft.MaterialState.DEFAULT: ft.colors.with_opacity(0.2, ft.colors.PINK),

See TabsTheme class for more details.

Other changes

Flutter 3.10

This Flet release is based on Flutter 3.10 which brings new features, performance and size optimizations. As a result, most of Flet dependencies bumped their versions too, so if you notice any issues please let us know.

Color emoji in web apps

Color emoji support in web apps are back! In Flutter 3.7 color emoji were disabled in "CanvasKit" renderer (default in Flet) because of their font size (8 MB!) and returned back as an opt-in in Flutter 3.10. You can enable color emoji in server-driven app with use_color_emoji argument:, use_color_emoji=True)

and use --use-color-emoji switch when publishing app as a static side.

That's all for today!

Upgrade Flet module to the latest version (pip install flet --upgrade) and let us know what you think!

· 3 min read
Feodor Fitsner

Unleash your inner artist 🧑‍🎨 and boost your Flet creativity with brand-new Canvas control just released in Flet 0.6.0!

Canvas enables you to draw arbitrary graphics using a set of primitives, or "shapes", such as line, circle, arc, path and text. I bet you can even implement your own version of charts using Canvas control!

Combine Canvas with GestureDetector and you get a free-hand drawing app - Flet Brush 😀!

Example source

Canvas control is located in flet.canvas package. You need another import to use it:

import flet.canvas as cv

Here's a simple program drawing a smiley face with Circle and Arc shapes using filled and stroke Paint:

import math
import flet as ft
import flet.canvas as cv

def main(page: ft.Page):
stroke_paint = paint = ft.Paint(stroke_width=2, style=ft.PaintingStyle.STROKE)
fill_paint = paint = ft.Paint(style=ft.PaintingStyle.FILL)
cp = cv.Canvas(
cv.Circle(100, 100, 50, stroke_paint),
cv.Circle(80, 90, 10, stroke_paint),
cv.Circle(84, 87, 5, fill_paint),
cv.Circle(120, 90, 10, stroke_paint),
cv.Circle(124, 87, 5, fill_paint),
cv.Arc(70, 95, 60, 40, 0, math.pi, paint=stroke_paint),


Read more about Canvas in docs and explore Canvas examples!

Other changes

Rich text support

While working on drawing text on Canvas, as a bonus to this release, we implemented a new TextSpan control which can now be used with Text.spans to output rich text.

Check rich text examples: one, two and three.

url property for buttons

If you need to open a URL by clicking on a button or any other control with on_click event you can just provide that URL in url instead of doing that in the code with page.launch_url() method.

Instead of that:

ft.ElevatedButton("Go to Google", on_click=lambda e:""))

you can just do this:

ft.ElevatedButton("Go to Google", url="")

A new url property also solves blocked window on Safari issue.

As a continuation of url property Markdown control can now be enabled to auto-follow URLs in the document:

import flet as ft

md = """
[Go to Google](

def main(page: ft.Page):

Better web support

In this release we also did some improvements to web support like capturing user info in page.client_id and page.client_user_agent as well as fixing nasty #1333 and #1289 bugs related to routing.

That's all for today!

Upgrade Flet module to the latest version (pip install flet --upgrade), give canvas and rich text a try and let us know what you think!

· 2 min read
Feodor Fitsner

Last year we introduced support for Matplotlib and Plotly charts. Both libraries are able to export charts as SVG images which are then displayed in a Flet app. However, such charts, while serving the purpose of visualization, are lacking interactivity and animation.

Today we are releasing Flet 0.5.2 with built-in charts 📊 based on the awesome fl_chart library!

Three new chart controls have been introduced:


Docs · Examples


Docs · Examples


Docs · Examples


We spent a lot of time studying fl_chart library while trying to implement most of its features in a Flet way. However, if you see anything missing in Flet, but available in a library please submit a new feature request.

Other changes

Pyodide 0.23

Pyodide, which provides Python runtime in a browser and is used to run Flet app as a static website, was upgraded to version 0.23 which is based on Python 3.11.2 and giving some size and performance improvements.

Memory leak fixes

In this release we paid a lot of attention to memory leak issues in Flet apps. Now, when a user session is closed its memory is reliably released and garbage-collected. That makes Flet ready for production applications with a lot of users.

Upgrade Flet module to the latest version (pip install flet --upgrade), give charts a try and let us know what you think!

Hey, Flet project has reached ⭐️ 5K stars ⭐️ - thank you all for your continuing support!

· 4 min read
Feodor Fitsner

We've just released Flet 0.4.0 with a super exciting new feature - packaging Flet apps into a standalone static website that can be run entirely in the browser! The app can be published to any free hosting for static websites such as GitHub Pages or Cloudflare Pages. Thanks to Pyodide - a Python port to WebAssembly!

You can quickly build awesome single-page applications (SPA) entirely in Python and host them everywhere! No HTML, CSS or JavaScript required!

Quick Flet with Pyodide demo

Install the latest Flet package:

pip install flet --upgrade

Create a simple app:
import flet as ft

def main(page: ft.Page):
page.title = "Flet counter example"
page.vertical_alignment = ft.MainAxisAlignment.CENTER

txt_number = ft.TextField(value="0", text_align=ft.TextAlign.RIGHT, width=100)

def minus_click(e):
txt_number.value = str(int(txt_number.value) - 1)

def plus_click(e):
txt_number.value = str(int(txt_number.value) + 1)

ft.IconButton(ft.icons.REMOVE, on_click=minus_click),
ft.IconButton(ft.icons.ADD, on_click=plus_click),

Run a brand-new flet publish command to publish Flet app as a static website:

flet publish

The website will be published to dist directory next to Give website a try using built-in Python web server:

python -m http.server --directory dist

Open http://localhost:8000 in your browser to check the published app.

Here are a few live Flet apps hosted at Cloudflare Pages:

Check the guide for more information about publishing Flet apps as standalone websites.

Built-in Fletd server in Python

Flet 0.4.0 also implements a new Flet desktop architecture.

It replaces Fletd server written in Go with a light-weight shim written in Python with a number of pros:

  1. Only 2 system processes are needed to run Flet app: Python interpreter and Flutter client.
  2. Less communication overhead (minus two network hops between Python and Fletd) and lower latency (shim uses TCP on Windows and Unix domain sockets on macOS/Linux).
  3. Shim binds to on Windows by default which is more secure.
  4. The size of a standalone app bundle produced by flet pack reduced by ~8 MB.

The implementation was also required to support Pyodide (we can't run Go web server in the browser, right?) and paves the way to iOS and Android support.

Other changes

  • All controls loading resources from web URLs (Image.src, Audio.src, Page.fonts, Container.image_src) are now able to load them from local files too, by providing a full path in the file system, and from assets directory by providing relative path. For desktop apps a path in src property could be one of the following:
    • A path relative to assets directory, with or without starting slash, for example: /image.png or image.png. The name of artifact dir should not be included.
    • An absolute path within a computer file system, e.g. C:\projects\app\assets\image.png or /Users/john/images/picture.png.
    • A full URL, e.g.
    • Add page.on_error = lambda e: print("Page error:", to see failing images.
  • flet Python package has separated into two packages: flet-core and flet.
  • PDM replaced with Poetry.
  • beartype removed everywhere.

💥 Breaking changes

  • Default routing scheme changed from "hash" to "path" (no /#/ at the end of app URL). Use, route_url_strategy="hash") to get original behavior.
  • OAuth authentication is not supported anymore in standalone desktop Flet apps.

Async support

Flet apps can now be written as async apps and use asyncio with other Python async libraries. Calling coroutines is naturally supported in Flet, so you don't need to wrap them to run synchronously.

To start with an async Flet app you should make main() method async:

import flet as ft

async def main(page: ft.Page):
await page.add_async(ft.Text("Hello, async world!"))

Read the guide for more information about writing async Flet apps.


Flet 0.4.0 brings the following exciting features:

  • Standalone web apps with Pyodide running in the browser and hosted on a cheap hosting.
  • Faster and more secure architecture with a built-in Fletd server.
  • Async apps support.

Upgrade Flet module to the latest version (pip install flet --upgrade), give flet publish command a try and let us know what you think!

Hey, by the way, Flet project has reached ⭐️ 4.2K stars ⭐️ (+1K in just one month) - keep going!

· One min read
Feodor Fitsner

Happy New Year! Flet project has reached ⭐️ 3.3K stars ⭐️ on GitHub which is very exciting and encouraging! Thank you all for your support!

We are starting this year with the release of Flet 0.3.2 bringing a long-awaited feature: creating standalone desktop bundles with a custom icon!

flet command has been used for running Flet program with hot reload, but we recently re-worked Flet CLI to support multiple actions.

There is a new flet pack command that wraps PyInstaller API to package your Flet Python app into a standalone Windows executable or macOS app bundle which can be run by a user with no Python installed.

Command's --icon argument is now changing not only executable's icon, but Flet's app window icon and the icon shown in macOS dock, Windows taskbar, macOS "About" dialog, Task Manager and Activity Monitor:

Bundle name, version and copyright can be changed too:

Find all available options for packaging desktop apps in the updated guide.

Upgrade Flet module to the latest version (pip install flet --upgrade), give flet pack command a try and let us know what you think!

· 5 min read
Feodor Fitsner

This post is a continuation of Flet mobile strategy published a few months ago.

Our original approach to Flet running on a mobile device was Server-Driven UI. Though SDUI has its own benefits (like bypassing App Store for app updates) it doesn't work in all cases, requires web server to host Python part of the app and, as a result, adds latency which is not great for user actions requiring nearly instance UI response, like drawing apps.

I've been thinking on how to make Python runtime embedded into Flutter iOS or Android app to run user Python program. No doubt, it's technically possible as Kivy and BeeWare projects do that already.

Current Flet architecture

The current architecture of Flet desktop app is shown on the diagram below:

Running Flet program on a desktop involves three applications (processes) working together:

  • Python runtime (python3) - Python interpreter running your Python script. This is what you are starting from a command line. Python starts Fletd server and connects to it via WebSockets.
  • Fletd server (fletd)- Flet web server written in Go, listening on a TCP port. Fletd holds the state of all user sessions (for desktop app there is only one session) and dispatches page updates and user generated events between Python program and Flet client.
  • Flet client (flet) - desktop app written in Flutter and displaying UI in a native OS window. Flet client connects to Fletd server via WebSockets.

The architecture above works well for Flet web apps where web server is essential part, but for desktop it seems redundant:

  • If all three processes run on the same computer WebSockets could be replaced with sockets or named pipes with less overhead.
  • Fletd server has no much sense as there is only one user session and UI state is persistently stored in Flet desktop client which is never "reloaded".

Flet new desktop architecture

Flet desktop app architecture can be simplified by replacing Fletd with a "stub" written in Python and communicating with Flet desktop client via sockets (Windows) and named pipes (macOS and Linux):

Flet mobile architecture

Mobile applications are running in a very strict context with a number of limitations. For example, on iOS the app cannot spawn a new processes. Other words, Flet Flutter app cannot just start "python.exe" and pass your script as an argument.

Luckily for us, Python can be embedded into another app as a C library and Dart (the language in which Flutter apps are written) allows calling C libraries via FFI (Foreign Function Interface).

Additionally, while Android allows loading of dynamically linked libraries iOS requires all libraries statically linked into app executable. This article covers Dart FFI in more details, if you are curious.

Flet mobile architecture could look like this:

Python runtime will be statically or dynamically linked with Flutter client app and called via FFI and/or named pipes.

Running Python on mobile will have some limitations though. Most notable one is the requirement to use "pure" Python modules or modules with native code compiled specifically for mobile ARM64 architecture.

Asyncio support

Asyncio is part of Python 3 and we start seeing more and more libraries catching up with async/await programming model which is more effective for I/O-bound and UI logic.

Currently, Flet is spawning all UI event handlers in new threads and it's also a pain to see threading.sleep() calls hogging threads here and there just to do some UI animation. All that looks expensive.

Using of async libraries from a sync code is possible, but looks hacky and inefficient as it keeps CPU busy just to wait async method to finish. So, we want a first-class support of async code in Flet app.

Async/await model is a state machine switching between tasks in a single thread. By going async Flet will able to utilize streams for socket server and use async WebSockets library library. It will be possible to use both sync and async event handlers in a single Flet app without any compromises or hacks.

Even more exciting, async Flet will be able to run entirely in the browser within Pyodide - Python distribution based on WebAssembly (WASM). WebAssembly doesn't have multi-threading support yet, so running in a single thread is a must. Just imagine, Flet web app with a trully offline Flet PWA that does not require a web server to run a Python code!

Development plan

We are going to crunch the scope above in a few iterations:

  1. Async API support with async WebSockets library. Works with the same Fletd in Go.
  2. Fletd server ("stub") in Python to use with a desktop.
  3. Embedding Python with Fletd "stub" and user program into iOS.
  4. Embedding Python into Android.
  5. Packaging mobile apps for iOS and Android.

🙏 I'm looking for a help from the community with developing C/C++/native code integration part between Flutter and Python on iOS and Android. It could be either free help or a paid job - let me know if you are interested!

Hop to Discord to discuss the plan, offer help, ask questions!

· 2 min read
Feodor Fitsner

Flet is a fast-evolving framework with a new functionality and bug fixes being committed every other day.

The development model with one pull request per release didn't work well for the project as users waited for weeks to get hands on a new release and, honestly, from development perspective producing large "heroic" releases takes a lot of energy 🫠.

From now on we'll be breaking releases into multiple pull requests with one feature/bugfix per PR.

Every PR merged into main branch will be publishing pre-release (developmental release) package to having version format of X.Y.Z.devN.

Installing pre-releases

To install Flet pre-release package use the following command:

pip install flet --pre

We recommend installing pre-release builds into a virtual environment.

Flet versioning

Flet is switching to Semanting Versioning with a version number MAJOR.MINOR.PATCH:

  1. MAJOR will be incremented when there are "incompatible API changes". Right now it's 0 and we expect to make it 1 when we feel that Flet API is stable enough.
  2. MINOR will be incremented when a new functionality added in a backwards compatible manner.
  3. PATCH will be incremented when we make backward compatible bug fixes.

According to that rule, upcoming Flet release will have version 0.2.0. Bug fixes for that release will be labeled as 0.2.1, 0.2.2, etc. The release after that release will be 0.3.0 and so on.

Flet pre-releases will have a format of MAJOR.{LAST_MINOR + 1}{BUILD} where LAST_MINOR is MINOR version of the last release and {BUILD} is a build number set by CI. For example, if the last published release is 0.1.65 pre-releases will have versions{BUILD}. Pre-releases after 0.2.0 release will be labeled as{BUILD}.

· 3 min read
Feodor Fitsner

We just released Flet 0.1.65 which is adding a bunch of mobile-optimized controls, fixing some bugs and introducing a new layout control - ResponsiveRow.

ResponsiveRow control

ResponsiveRow borrows the idea of grid layout from Bootstrap web framework.

ResponsiveRow allows aligning child controls to virtual columns. By default, a virtual grid has 12 columns, but that can be customized with ResponsiveRow.columns property.

Similar to expand property every control now has col property which allows specifying how many columns a control should span. For examle, to make a layout consisting of two columns spanning 6 virtual columns each:

import flet as ft

ft.Column(col=6, controls=ft.Text("Column 1")),
ft.Column(col=6, controls=ft.Text("Column 2"))

ResponsiveRow is "responsive" because it can adapt the size of its children to a changing screen (page, window) size. col property in the example above is a constant number which means the child will span 6 columns for any screen size.

If ResponsiveRow's child doesn't have col property specified it spans the maximum number of columns.

col can be configured to have a different value for specific "breakpoints". Breakpoints are named dimension ranges:


For example, the following example collapses content into a single column on a mobile device and takes two columns on larger screens:

import flet as ft

ft.Column(col={"sm": 6}, controls=ft.Text("Column 1")),
ft.Column(col={"sm": 6}, controls=ft.Text("Column 2"))

Here is more elaborate example of responsive layout:

import flet as ft

def main(page: ft.Page):
def page_resize(e):
pw.value = f"{page.width} px"

page.on_resize = page_resize

pw = ft.Text(bottom=50, right=50, style="displaySmall")
ft.Text("Column 1"),
col={"sm": 6, "md": 4, "xl": 2},
ft.Text("Column 2"),
col={"sm": 6, "md": 4, "xl": 2},
ft.Text("Column 3"),
col={"sm": 6, "md": 4, "xl": 2},
ft.Text("Column 4"),
col={"sm": 6, "md": 4, "xl": 2},
ft.TextField(label="TextField 1", col={"md": 4}),
ft.TextField(label="TextField 2", col={"md": 4}),
ft.TextField(label="TextField 3", col={"md": 4}),
run_spacing={"xs": 10},

ResponsiveRow docs, example.

Other new controls

This release adds new visual and non-visual controls requested by Flet community and also required to build UI of the upcoming Flet Studio.


Shows a modal Material Design bottom sheet:

BottomSheet docs, example.

Bottom Navigation bar which offers a persistent and convenient way to switch between primary destinations in an app:

NavigationBar docs, example.


A tooltip control:

Tooltip docs, example.


Allows access to the haptic feedback (clicks and vibrates) interface on the device.

HapticFeedback docs.


A control to detect phone shakes. Based on shake widget.

ShakeDetector docs.

Other improvements

Markdown code syntax highlight

Sample code.

Variable fonts support

Flutter has finally supported variable fonts and we bring that into Flet too!

Sample code.

Upgrade Flet module to the latest version (pip install flet --upgrade) and let us know what you think!


· 2 min read
Feodor Fitsner

We are thrilled to introduce Matplotlib and Plotly charting controls in Flet 0.1.63!

Matplotlib and Plotly are the most recognized Python charting libraries with a ton of features. They are greatly compatible with other scientific Python libraries such as Numpy or Pandas.

No doubt, it would be nearly impossible to replicate their functionality as pure Flutter widgets. Fortunately, both Matplotlib and Plotly can export charts into various formats, such as SVG. On the other hand Flet can display SVG images and that gives a perfect combination - Flet charting controls for Matplotlib and Plotly!

The resulting solution works so great that it's possible to display almost any example from Matplotlib and Plotly galleries - your imagination is the only limit!

Plot a simple bar chart:

a nice scatter with legend:

or some multi-chart contour plot:

Check the docs for Matplotlib and Plotly charting controls:

Explore Flet chart examples.

Learn Python libraries by examples:

In the future releases, we may add an interactive "toolbar" for Matplotlib charts by implementing a custom backend. Or maybe it's a great excersize for Flet users? 😉

Also, when it's time for Flet to support other languages we would need to re-visit charting to make it language-agnostic as the current charting implementation relies on Python libraries.

Upgrade Flet module to the latest version (pip install flet --upgrade), integrate auth in your app and let us know what you think!


· 2 min read
Feodor Fitsner

We've just released Flet 0.1.62 with support of gestures processing!

There is a new control - GestureDetector which allows handling all sorts of gestures: single and double taps with a left (primary) and right (secondary) mouse (pointer) buttons, vertical, horizontal and bi-directional drags, zoom (pinch-in and pinch-out) gestures as well as hover events. Now, by wrapping it into GestureDetector, you can make any Flet control "clickable" and "draggable"!

Here is a simple example of an app which allows you to drag containers inside a Stack:

import flet as ft

def main(page: ft.Page):
def on_pan_update(e: ft.DragUpdateEvent): = max(0, + e.delta_y)
e.control.left = max(0, e.control.left + e.delta_x)

gd = ft.GestureDetector(
content=ft.Container(bgcolor=ft.colors.BLUE, width=50, height=50, border_radius=5),

page.add( ft.Stack([gd], expand=True))

Gesture detector is yet another great addition to a collection of Flet primitives that allows you to build apps limited only by your imagination. 2D drawing coming later this month is going to complete that ensemble!

That release wasn't about only gestures though - that was a "stabilization" release too. We fixed a number of bugs and added a bunch of other small features which you can see here.

Upgrade Flet module to the latest version (pip install flet --upgrade), integrate auth in your app and let us know what you think!