Skip to main content

Flet at PyCon US 2024

ยท 2 min read
Feodor Fitsner
Flet founder and developer

Last week we attended PyCon US in a beautiful city of Pittsburgh, PA!

I've been on many conferences, but at PyCon I was amazed by the spacious venue, flawless event organization, high-quality content and welcoming community ๐Ÿ˜Ž, ...and good food ๐Ÿ”!

We met a lot of great people and, especially, wonderful people from Beeware (hello Russell, Malcolm and Russell ๐Ÿ‘‹). They did a great job of popularizing Python on mobile and advocating the addition of iOS and Android to the list of supported platforms in the next release of Python 3.13 ๐ŸŽ‰!

We enjoyed good talks and inspirational key notes, learned new things, enjoyed the city. Lightning talks (short 5-minute presentations) were real fun!

Oh, I saw Guido van Rossum (The Creator of Python himself, in case you didn't know ๐Ÿ˜…) at Microsoft booth, but didn't have a chance to take a picture with him as there was a line up ๐Ÿ˜‰.

As first time attendees we did't do talks or presentations, but watched and learned instead. We plan to present next year ๐Ÿคž.

Next PyCon US is going to be at the same place. Will come again and hope to see more of you there!

Flet packaging update

ยท 7 min read
Feodor Fitsner
Flet founder and developer

The problemโ€‹

When you package your Flet program in Python to run on a mobile device (or desktop) the resulting bundle (.apk, .ipa, .exe, .app) contains your Python program, Python interpreter and Python Standard Library.

If your program uses only Python standard library then packaging process is relatively easy - Flet zips your code and combines Flutter app together with Python interpreter and standard library both compiled for the target platform: Android or iOS.

However, problems may arise when your Flet program uses third-party packages, with thousands of them published on PyPI or Conda.

There are two kinds of third-party packages:

Pure-Python packagesโ€‹

A "pure-Python" package is a package that only contains Python code, and doesn't include extensions written in C, C++, Rust or other languages. You only need a Python interpreter and the Python Standard Library to run a pure-Python package, and it doesn't matter what your OS or platform is.

Examples of such packages: httpx, click, rich, requests.

To verify if the package is pure, find that package on PyPI and navigate to its "Download files" page. If under "Built distribution" section there is only one wheel ending with -py3-none-any.whl then most probably it's a pure Python package that will work "as is" on any device with Python.

We say "probably" because that pure package could depend on a non-pure package which brings you to the next section. For example, pydantic is a pure package, but to work properly it requires pydantic-core non-pure package written in Rust.

Non-pure Python packagesโ€‹

A "non-pure Python" package is one that is fully or partially written in C, C++, Rust, or another language and must be compiled to machine code for the platform on which it will run.

Examples of such packages: cryptography, opencv-python, numpy, msgpack.

On "Download files" page of non-pure package you will find a bunch of wheels pre-built for various platforms: macOS, Windows, Linux.

When you run pip install <package> pip tries to find a wheel for your specific platform and Python version looking at wheel suffixes that include that information.

It's a courtesy of package developer to provide pre-compiled wheels for multiple platforms. There could be missing wheels for some platforms, or no wheels at all - just .tar.gz under "Source distribution" with package sources.

Building package from sources is hardโ€‹

To install a package with source distribution only, pip will attempt to build non-Python code on your machine using installed compilers, linkers, libraries, and SDKs. However, this process can be lengthy and error-prone. The compiled code base might be large, and your machine could lack the required libraries or toolchains.

No wheels for iOS and Android yetโ€‹

There are no pre-built wheels for iOS and Android on PyPI and PyPI's validation process won't allow package developers to upload them anyway as both iOS and Android are not officially supported platforms in Python.

There is a process (PEP 730 and PEP 738) to add official support for iOS and Android to Python 3.13, so, hopefully, the developer experience will improve.

Package dependenciesโ€‹

Pure-Python packages can import or depend on non-pure packages and you should keep that in mind while packaging your Flet app to run on a mobile device.

For example, supabase package, to access Supabase API, is a pure package which depends on pydantic package which is also pure Python package. In its turn pydantic package depends on pydantic-core which is a non-pure package written in Rust. Thus, to run your Flet app using Supabase API the packaging process should be able to find a pre-build wheel for your target platform. If PyPI doesn't have that wheel then it could be either Flet developers, building that wheel on their servers and hosting it somewhere, or you, building that wheel on your own machine.

To see a dependency graph for a package you can use pipgrip.

Run it with --tree option to get a tree view of dependencies:

$ pipgrip --tree fastapi

fastapi (0.110.3)
โ”œโ”€โ”€ pydantic!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0,>=1.7.4 (2.7.1)
โ”‚ โ”œโ”€โ”€ annotated-types>=0.4.0 (0.6.0)
โ”‚ โ”œโ”€โ”€ pydantic-core==2.18.2 (2.18.2)
โ”‚ โ”‚ โ””โ”€โ”€ typing-extensions!=4.7.0,>=4.6.0 (4.11.0)
โ”‚ โ””โ”€โ”€ typing-extensions>=4.6.1 (4.11.0)
โ”œโ”€โ”€ starlette<0.38.0,>=0.37.2 (0.37.2)
โ”‚ โ””โ”€โ”€ anyio<5,>=3.4.0 (4.3.0)
โ”‚ โ”œโ”€โ”€ idna>=2.8 (3.7)
โ”‚ โ””โ”€โ”€ sniffio>=1.1 (1.3.1)
โ””โ”€โ”€ typing-extensions>=4.8.0 (4.11.0)

Current approachโ€‹

We released the first version of packaging 4 months ago and since then, we have realized that the initial approach has multiple flaws and should be improved.

When you run flet build apk with the current Flet version it downloads Python runtime with standard library both pre-built for Android (or iOS if ran with flet build ipa).

For non-pure packages, like numpy, Flet is asking you to build those packages by yourself using "Python for Android" (p4a) tool from Kivy and then provide a path to "p4a" distributive where those pre-build packages could be found.

This is problem #1 - you are forced to struggle with a complicated process of installing "p4a" tool and compiling Python modules on your machine.

Problem #2 - all packages from p4a's dist directory will be included into a final application bundle - it could contain non-relevant packages and other junk.

Problem #3 - non-pure packages must be built before running flet build command. You have to analyze all dependencies of your app and separate what must be built with p4a.

Problem #4 - p4a "recipes" to build packages could be either very old or missing. You hope that older version of the package works with your app, try authoring a "recipe" and hope it works or submit a request for new recipe in Kivy repository.

When you're done with building non-pure packages using p4a, Flet requires you to specify only pure packages in requirements.txt which doesn't work if pure package directly or indirectly depends on non-pure (see example above) - this is problem #5. There is a recent example of this problem: flet build replaces flet with flet-embed in requirements.txt, but it's unable to know if there is a 3rd-party package depending on flet, thus both flet-embed and non-suitable-for-mobile flet are installed. This is not a solution, but a hack!

Packaging 2.0โ€‹

In the next iteration of Flet's packaging implementation, we are going to move away from Kivy and replace it with Mobile Forge. Mobile Forge has been created by Beeware team based and their experience with Briefcase and Chaquopy. Mobile Forge is a clean-room implementation of a packaging tool for binary Python packages which is relies on crossenv.

The main promise of Mobile Forge with crossenv is that most existing non-pure Python packages will be able to compile for iOS and/or Android by simply adding a recipe with meta.yaml file only, without requiring any hacks or patches.

We are going to use Mobile Forge to pre-build the most popular non-pure Python packages for iOS and Android and host them in our own public repository. You will be able to use that tool to build and contribute other packages, non present in our repository.

We've created a new "Packages" category in Flet discussions where you can post, vote and discuss requests for non-pure (native) Python packages that work with Flet (check rules before posting there). Flet's goal is to provide the most comprehensive catalog of pre-built Python packages and make the process of adding new packages as friendly and transparent as possible.

The new version of flet build will use a custom-made virtual pip index. This index will analyze dependencies, detect non-pure packages, and offer to pip mobile packages. For all other packages, it will fall back to PyPI.

The new packaging will be hopefully available in a few weeks. While we are working we encourage you to visit Packages and see if the package you need is there. Submitting a request or voting for existing package will help us to prioritize package "recipes".

Thank you!

Controls and theming enhancements

ยท 5 min read
Henri Ndonko
Flet Contributor and Maintainer

One month after the release of Flet 0.21.0, we are excited to announce the release of Flet 0.22.0.

This release comes with a lot of enhancements, bug fixes, and deprecations:

Enhancementsโ€‹

This was one of the main concerns while coming up with this release. Two types of enhancements were made:

Controls Enhancementโ€‹

We went through the long list of already-present controls and exposed, where possible, more properties - PR #2882. This will grant you more power/control over the Flet Controls you use in your awesome applications.

Below is the complete list:

  • AppBar: elevation_on_scroll, exclude_header_semantics, force_material_transparency, is_secondary, shadow_color, surface_tint_color, clip_behavior, title_spacing, toolbar_opacity, title_text_style, toolbar_text_style, shape
  • AlertDialog: action_button_padding, clip_behavior, icon_padding, shadow_color, surface_tint_color
  • Banner: content_text_style, margin, elevation, divider_color, shadow_color, surface_tint_color, on_visible
  • CupertinoListTile: leading_size, leading_to_title
  • CupertinoSegmentedButton: click_color
  • CupertinoSwitch:on_label_color, off_label_color
  • CupertinoTimerPicker: item_extent
  • Chip: surface_tint_color, color, click_elevation, clip_behavior, visual_density, border_side
  • Divider: leading_indent, trailing_indent
  • ExpansionTile: dense, enable_feedback, visual_density
  • Card: clip_behavior, is_semantic_container, show_border_on_foreground, variant
  • Checkbox: border_side, semantics_label, shape, splash_radius, is_error, visual_density, mouse_cursor
  • CircleAvatar: on_image_error
  • DataTable: clip_behavior
  • DatePicker: on_entry_mode_change
  • Draggable: on_drag_complete, on_drag_start
  • DragTarget: on_move
  • Dropdown: fill_color, hint_content, icon_content, elevation, item_height, max_menu_height, icon_size, enable_feedback, padding, icon_enabled_color, icon_disabled_color, on_click
  • ElevatedButton: clip_behavior
  • FloatingActionButton: clip_behavior, enable_feedback, focus_color, foreground_color, disabled_elevation, elevation, focus_elevation, highlight_elevation, hover_elevation, mouse_cursor
  • GridView: cache_extent, clip_behavior, semantic_child_count
  • IconButton: alignment, disabled_color, focus_color, enable_feedback, hover_color, padding, splash_color, splash_radius, focus_color, mouse_cursor, visual_density
  • Image: exclude_from_semantics, filter_quality
  • ListTile: enable_feedback, horizontal_spacing, min_leading_width, min_vertical_padding, selected_color, selected_tile_color, style, title_alignment, icon_color, text_color, shape, visual_density, mouse_cursor, title_text_style, subtitle_text_style, leading_and_trailing_text_style
  • ListView: cache_extent, clip_behavior, semantic_child_count
  • NavigationBar: animation_duration, overlay_color
  • NavigationDrawerDestination: bgcolor
  • NavigationBarDestination: bgcolor
  • NavigationRail: selected_label_text_style, unselected_label_text_style
  • NavigationRailDestination: indicator_color, indicator_shape
  • Option: alignment, on_click
  • OutlinedButton: clip_behavior
  • Page: locale_configuration
  • PopupMenuItem: height, padding, mouse_cursor
  • PopupMenuButton: bgcolor, clip_behavior, elevation, enable_feedback, icon_color, shadow_color, surface_tint_color, icon_size, padding, splash_radius, shape, on_open, on_cancel
  • ProgressBar: border_radius, semantics_label, semantics_value
  • ProgressRing: semantics_label, semantics_value, stroke_cap, stroke_align
  • Radio: focus_color, hover_color, overlay_color, splash_radius, toggleable, visual_density, mouse_cursor
  • SearchBar: keyboard_type, view_surface_tint_color, autofocus
  • SelectionArea: on_change
  • Slider: interaction, overlay_color, mouse_cursor, secondary_track_value, secondary_active_color
  • Stack: alignment, fit
  • SnackBar: clip_behavior, shape, on_visible, action_overflow_threshold
  • Switch: hover_color, splash_radius, overlay_color, track_outline_color, mouse_cursor
  • Tabs: divider_height, enable_feedback, indicator_thickness, is_secondary, mouse_cursor, clip_behavior
  • TextField: fill_color, hover_color
  • TimePicker: orientation, on_entry_mode_change
  • Tooltip: enable_tap_to_dismiss, exclude_from_semantics
  • VerticalDivider: leading_indent, trailing_indent

If you however feel that something lacks and should be added, don't hesitate to let us know.

Check out the article I wrote concerning Page.locale_configuration here.

Theme Enhancementsโ€‹

The Theme class which is used for application theming in light and dark mode has equally been further enhanced. Lots of new themes were introduced - PR #2955.

See the Theming Guide here.

Rive Animationsโ€‹

Rive is a very popular real-time interactive design and animation tool. The newly introduced Rive Control allows you to load and visualize any Rive animation in your applications.

The animation's source (Rive.src) can either be a local asset file or a URL - as usual, it all depends on your needs.

Parent Controlโ€‹

As requested in #952, the ability to access the parent of any control has been added: Control.parent.

Read more on it here.

Bug Fixesโ€‹

The below issues were successfully fixed:

  • #2560 - Dropdown.bgcolor was not visually respected
  • #2740 - CircleAvatar not working with local asset images
  • #2781 - 'FletSocketServer' Error raised on Linux
  • #2826 - PopupMenuItem.data not respected
  • #2839 - ExpansionTile.initially_expanded had no visual effect
  • #2867 - PopupMenuButton had an always-visible tooltip of "Show menu"
  • On some Python versions, you might have seen a RuntimeError('Event loop is closed') which usually shows up when closing the app's window. The Python-dev team fixed this asyncio-related issue recently, but this fix is only present in the versions released from the year 2024. So if you face this issue, please download one of the latest Python releases and replace the one used in your environment.

Special Thanks to the dynamic Flet community for reporting all the issues they encountered. We keep working hard on solving the remaining ones.

Deprecationsโ€‹

As previously mentioned in the announcement concerning Flet v0.21.0, all deprecations will be completely removed from the API in version 1.0 - so you have enough time to update your apps.

You must not completely memorize what has been deprecated as we've added DeprecationWarnings which will be shown directly in your console (without breaking your app).

Documentationโ€‹

The Flet documentation has been reorganized to ease navigation (especially for beginners/new users).

Upgrade to Flet 0.22.0, test your apps and let us know how you find the new features we added. If you have any questions, please join Flet Discord server or create a new thread on Flet GitHub discussions.

Happy Flet-ing!

Flet FastAPI and async API improvements

ยท 6 min read
Feodor Fitsner
Flet founder and developer

Flet makes writing dynamic, real-time web apps a real fun!

Flet 0.21.0 further improves web apps development experience as well as using asyncio APIs in your Flet apps.

Here's what's new in Flet 0.21.0:

FastAPI with Uvicorn replaces built-in web serverโ€‹

From very beginning of Flet life to serve web apps there was a built-in web server written in Go and called "Fletd". It's being started on the background when you run your app with flet run --web. Fletd was part of Flet Python wheel contributing a few megabytes to its size. Additionally, Python app was using WebSockets to talk to Fletd web server which was adding sometimes noticeable overhead.

Then, in Flet 0.10.0 we have added FastAPI support to build "serious" web apps using AsyncIO API.

Now, in Flet 0.21.0 built-in web server has been completely removed and replaced with FastAPI and Uvicorn. Fletd is not a part of Flet distribution anymore.

Using FastAPI means there is no more communication overhead as web server is a part of Flet app. Also, you don't need to do any additional steps to host your app in production with FastAPI - you just use the same ft.app(main) command to run your app.

Breaking change

flet_fastapi package has been deprecated and its contents moved to flet package as flet.fastapi module. If you were using FastAPI in your Flet app replace:

import flet_fastapi

with

import flet.fastapi as flet_fastapi

Use any ASGI web server for hosting

You can host your Flet web app with any ASGI-compatible server such as Uvicorn (used by default), Hypercorn or Daphne.

Just tell Flet to export ASGI app:

main.py
import flet as ft

def main(page: ft.Page):
page.add(ft.Text("Hello ASGI!"))

app = ft.app(main, export_asgi_app=True)

and then run with Hypercorn as:

hypercorn main:app --bind 0.0.0.0:8000

Web app environment variables

Every aspect of web app hosting can be controlled with environment variables:

  • FLET_FORCE_WEB_SERVER - true to force running app as a web app. Automatically set on headless Linux hosts.
  • FLET_SERVER_PORT - TCP port to run app on. 8000 if the program is running on a Linux server or FLET_FORCE_WEB_SERVER is set; otherwise random port.
  • FLET_SERVER_IP - IP address to listen web app on, e.g. 127.0.0.1. Default is 0.0.0.0 - bound to all server IPs.
  • FLET_ASSETS_DIR - absolute path to app "assets" directory.
  • FLET_UPLOAD_DIR - absolute path to app "upload" directory.
  • FLET_MAX_UPLOAD_SIZE - max allowed size of uploaded file, in bytes. Unlimited if not specified.
  • FLET_SECRET_KEY - a secret key to sign temporary upload URLs.
  • FLET_WEB_APP_PATH - a URL path after domain name to host web app under, e.g. /apps/myapp. Default is / - host app in the root.
  • FLET_SESSION_TIMEOUT - session lifetime, in seconds. Default is 3600.
  • FLET_OAUTH_STATE_TIMEOUT - max allowed time to complete OAuth web flow, in seconds. Default is 600.
  • FLET_WEB_RENDERER - Flutter rendering mode: canvaskit (default), html or auto.
  • FLET_WEB_USE_COLOR_EMOJI - true, or True or 1 to load web font with colorful emojis.
  • FLET_WEB_ROUTE_URL_STRATEGY - path (default) or hash.
  • FLET_WEBSOCKET_HANDLER_ENDPOINT - custom path for WebSocket handler. Default is /ws.
  • FLET_UPLOAD_HANDLER_ENDPOINT - custom path for upload handler. Default is /upload.
  • FLET_OAUTH_CALLBACK_HANDLER_ENDPOINT - custom path for OAuth handler. Default is /oauth_callback.

Async-first frameworkโ€‹

Flet is now async-first framework which means you don't have to decide whether your app is entirely sync or async, but you can mix both sync and async methods in the same app.

For example, in Flet 0.21.0 you can write an app like this:

import flet as ft
import time
import asyncio

def main(page: ft.Page):

def handler(e):
time.sleep(3)
page.add(ft.Text("Handler clicked"))

async def handler_async(e):
await asyncio.sleep(3)
page.add(ft.Text("Async handler clicked"))

page.add(
ft.ElevatedButton("Call handler", on_click=handler),
ft.ElevatedButton("Call async handler", on_click=handler_async)
)

ft.app(main)

In the example above a click on one button is handled by a "blocking" handler while a click on second button calls asynchronous handler. The first handler is run in a threading.Thread while second handler is run in asyncio.Task.

Also, notice in async def handler you are not required to use await page.add_async() anymore, but a regular page.add() works just fine.

API changes

Most of Page.<method>_async() and Control.<method>_async() methods have been deprecated and their Page.<method>() and Control.<method>() counterparts should be used instead.

The only exception here is methods returning results, like those ones in Audio control: you still have to use async methods in async event handlers.

Custom controls API normalizedโ€‹

In this Flet release we also re-visited API for writing custom controls in Python.

As a result UserControl class has been deprecated. You just inherit from a specific control with layout that works for your needs.

For example, Countdown custom control is just a Text and could be implemented as following:

import asyncio

import flet as ft

class Countdown(ft.Text):
def __init__(self, seconds):
super().__init__()
self.seconds = seconds

def did_mount(self):
self.running = True
self.page.run_task(self.update_timer)

def will_unmount(self):
self.running = False

async def update_timer(self):
while self.seconds and self.running:
mins, secs = divmod(self.seconds, 60)
self.value = "{:02d}:{:02d}".format(mins, secs)
self.update()
await asyncio.sleep(1)
self.seconds -= 1

def main(page: ft.Page):
page.add(Countdown(120), Countdown(60))

ft.app(main)

Notice the usage of self.page.run_task(self.update_timer) to start a new task. There is also self.page.run_thread() method that must be used by control developer to start a new background job in a thread.

If you want to spawn your own tasks or threads Flet provides the current event loop and thread executor via Page.loop and Page.executor properties respectively.

API changes

Control._before_build_command() replaced with Control.before_update()

Control.build() should not return any control now, but must update inherited control properties, for example:

def build():
self.controls.append(ft.Text("Something"))

Control.did_mount_async() and Control.will_unmount_async() are deprecated. Use Control.did_mount() and Control.will_unmount() instead.

New Cupertino controlsโ€‹

This Flet release adds more Cupertino controls to make your apps shine on iOS:

  • CupertinoActivityIndicator
  • CupertinoActionSheet
  • CupertinoSlidingSegmentedButton
  • CupertinoSegmentedButton
  • CupertinoTimerPicker
  • CupertinoPicker
  • CupertinoDatePicker
  • CupertinoContextMenu

Accessibility improvementsโ€‹

Now Flet has complete implementation of Semantics control and new SemanticsService control.

App lifecycle change eventโ€‹

There is a new Page.on_app_lifecycle_state_change event that allows listening for changes in the application lifecycle.

For example, you can now update UI with the latest information when the app becomes active (brought to the front). This event works on iOS, Android, all desktop platforms and web!

The following app lifecycle transitions are recognized:

  • SHOW
  • RESUME
  • HIDE
  • INACTIVE
  • PAUSE
  • DETACH
  • RESTART
note

Read more about each lifecycle state.

Here's a small example of how this event can be used:

import flet as ft

def main(page: ft.Page):

def app_lifecycle_change(e: ft.AppLifecycleStateChangeEvent):
if e.state == ft.AppLifecycleState.RESUME:
print("Update UI with fresh data!")

page.on_app_lifecycle_state_change = app_lifecycle_change
page.add(ft.Text("Hello World"))

ft.app(target=main)

Flet 0.21.0 release has some breaking changes. Upgrade to it, test your apps and let us know how it worked for you. Join Flet Discord server or create a new thread on Flet GitHub discussions.

Enjoy!

Flet adaptive UI and custom controls release

ยท 3 min read
Feodor Fitsner
Flet founder and developer

๐Ÿฅฐ Happy Valentine's Day lovely people! ๐Ÿฅฐ

We just released Flet 0.20.0 with the focus on:

  1. Adaptive UI.
  2. Extending Flet apps with 3rd-party Flutter packages.
  3. New controls: Video (yay!), AudioRecorder and a bunch of Cupertino-like controls. Flet now includes 97 built-in controls!
warning

Flet 0.20.0 includes a new Video control. While macOS and Windows already include all required media libraries to test Flet apps on Linux, the libmpv package must be installed. On Ubuntu/Debian in can be installed with:

sudo apt install libmpv-dev mpv

Adaptive UIโ€‹

Adaptive controls allow writing apps with a single code base which look and behave differently depending on the platform they are running on.

To the date Flet provides 11 adaptive controls. To make control adaptive you should set its adaptive property to True.

In Flet 0.20.0 we introduce adaptive property to all container-alike controls. Setting adaptive=True on a container propagates this property to all child adaptive controls.

Page adds design property which enables granular control over controls design language and can have the following values: ft.PageDesign.ADAPTIVE, ft.PageDesign.MATERIAL or ft.PageDesign.CUPERTINO.

By setting just page.design = ft.PageDesign.ADAPTIVE you can make you app looking awesome on both iOS and Android devices:

iPhone

Android

Integrating existing Flutter packagesโ€‹

Today Flet offers almost 100 controls, but, as you can imagine, not every Flutter library/widget could be added to the core Flet library and Flet team couldn't do that alone in the acceptable timeframe.

At the same time we do not want to put early adopters, who chose Flet to build their next commercial or corporate app, into a situation where their progress depends on Flet team availability and desire to implement a Flutter control they need.

In Flet 0.20.0 we re-factored Flutter core packages and identified the API that can be used by 3rd-party developers to add their own Flet controls written in Dart.

We are currently working on API docs, but you can learn now how custom Flutter packages are implemented by looking at Dart sources for Video, and Audio controls.

In short, you have to create a new Flutter package which implements and exports two methods:

void ensureInitialized();
Widget createControl(CreateControlArgs args);

See ensureInitialized() and createControl() implementations for Video control.

On Python side you create a new class inherited from Control (non-visual or overlay controls) or ConstrainedControl.

See Video class implementation in Python.

To integrate a custom Flutter package while building your Flet app with flet build command you can list extra packages with either --include-packages option or in pubspec.yaml file put into root of your Flet app.

Video controlโ€‹

Video control is implemented in a separate Flutter package.

To build your Flet app with Video control add --include-packages flet_video to your flet build command, for example:

flet build apk --include-packages flet_video

Flet 0.20.0 is a relatively "large" release and could break some things.

Upgrade to Flet 0.20.0, test your apps and let us know what you think by joining Flet Discord server or creating a new thread on Flet GitHub discussions.

Enjoy!