website/blog/2025-06-26-introducing-flet-1-0-alpha.md
Flet has been in the making for over three years, steadily gaining traction and building a vibrant user community. As more developers adopt Flet for real-world projects, one thing has become clear: people are ready to commit — but they also want to see the same commitment from us.
Releasing Flet 1.0 isn’t just about a version number. It’s about signaling stability, maturity, and long-term vision. A stable API, comprehensive documentation, better testing and clearly communicated roadmap — these are the foundational pieces developers need to confidently build serious apps on Flet.
But Flet 1.0 isn’t just the next incremental release. It’s a complete re-architecture.
The first versions of Flet inherited design decisions from Pglet — a web-based framework with a focus on multi-language support. While that served as a useful starting point, Flet has since evolved into a Python-centric framework for building cross-platform apps — web, desktop, and mobile.
With that evolution came technical debt, architectural misfits, and increasing complexity. Rather than patch over the cracks, we made a bold decision: to rewrite Flet from the ground up. It’s always risky to rewrite, but there’s no better time than now — before 1.0 — while the user base is still manageable and we can afford to break things in the name of long-term simplicity and maintainability.
After nearly five months of work, today we’re releasing the Flet 1.0 Alpha — a technical preview of what’s coming.
Flet 1.0 introduces major changes that simplify how you build, run, and scale apps. Some are improvements, some are breaking — all are focused on giving you a faster, more flexible developer experience.
Audio, FilePicker, Clipboard were re-written as services.Flet 1.0 introduces a declarative/reactive approach to building UIs in Flet. Developers can now mix imperative and declarative patterns in the same app, enabling more flexible and functional UI code.
from dataclasses import dataclass
import flet as ft
@dataclass
class AppState:
count: int
def increment(self):
self.count += 1
def main(page: ft.Page):
state = AppState(count=0)
page.floating_action_button = ft.FloatingActionButton(
icon=ft.Icons.ADD, on_click=state.increment
)
page.add(
ft.ControlBuilder(
state,
lambda state: ft.SafeArea(
ft.Container(
ft.Text(value=f"{state.count}", size=50),
alignment=ft.Alignment.center(),
),
expand=True,
),
expand=True,
)
)
ft.run(main)
More declarative examples:
🚧 Documentation is in progress 🚧
Control.update() is now automatically called once its event handler method is finished.
Most of Flet apps will now work without explicit update() calls.
Use yield inside long-running event handlers to refresh UI, for example:
async def button_click():
progress.value = "Something started"
yield
await asyncio.sleep(3)
progress.value = "Something finished"
🚧 Documentation is in progress 🚧
"Service" is a persistent, non-visual control that can "survive" page updates and navigation transitions.
Some of the existing controls were re-implemented as services (breaking change):
Audio (extension)AudioRecorder (extension)FilePickerFlashlight (extension)Geolocator (extension)HapticFeedbackInterstitialAd (extension)PermissionHandler (extension)SemanticsServiceShakeDetectorService instances must be added to page.services list to work.
Flet 1.0 web apps use WebAssembly (WASM) by default on selected browsers.
Built-in Flet web client and Flet apps built with flet build web are now include both Dart2JS (with CanvasKit as a renderer) and WebAssembly (with SKWASM renderer) targets.
🚧 Documentation is in progress 🚧
Flet 1.0 has "no-CDN" mode which allows bundling the following resources along with your app instead of loading them from external CDNs:
To enable no-CDN during runtime either add no_cdn=True to ft.run() (it's a new ft.run()) call:
ft.run(main, no_cdn=True)
or set FLET_WEB_NO_CDN environment variable to 1, true or yes.
To enable no-CDN for flet build add --no-cdn argument.
🚧 Documentation is in progress 🚧
You can now embed a Flet web app into any HTML element within an existing web page.
It's also possible to render multiple views of the same app in different HTML elements.
🚧 Documentation is in progress 🚧
The new extensions API allows exporting from your Flutter package of both control and service widgets. Technically, an extension is a class now rather than a method which will allow us to add more hooks into it like custom splash and loading screens.
For example, flet-ads extension before has just one createControl method:
import 'package:flet/flet.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'banner.dart';
import 'interstitial.dart';
CreateControlFactory createControl = (CreateControlArgs args) {
switch (args.control.type) {
case "banner_ad":
return BannerAdControl(
parent: args.parent, control: args.control, backend: args.backend);
case "interstitial_ad":
return InterstitialAdControl(
parent: args.parent, control: args.control, backend: args.backend);
default:
return null;
}
};
void ensureInitialized() {
if (isMobilePlatform()) {
MobileAds.instance.initialize();
}
}
and for Flet v1 it has two methods createWidget and createService:
import 'package:flet/flet.dart';
import 'package:flutter/cupertino.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'banner.dart';
import 'interstitial.dart';
class Extension extends FletExtension {
@override
void ensureInitialized() {
if (isMobilePlatform()) {
MobileAds.instance.initialize();
}
}
@override
FletService? createService(Control control) {
switch (control.type) {
case "InterstitialAd":
return InterstitialAdService(control: control);
default:
return null;
}
}
@override
Widget? createWidget(Key? key, Control control) {
switch (control.type) {
case "BannerAd":
return BannerAdControl(control: control);
default:
return null;
}
}
}
This change is breaking.
🚧 Documentation is in progress 🚧
ft.run with before_mainA new before_main arg added to ft.run() (replaces ft.run()). before_main is a hook that allows to reliable configure page-level event handlers before Flutter client starts sending events. before_main is a function that accepts one parameter: page: ft.Page
Example usage:
def config(page: ft.Page):
page.on_resize = lambda e: print("Page resized!")
def main(page: ft.Page):
page.add(ft.Text("Hello!"))
ft.run(main, before_main=config)
There is a new page.storage_paths multi-platform API based on path_provider for finding commonly used locations on the filesystem:
get_application_cache_directory_async(self) -> str
get_application_documents_directory_async(self) -> str
get_application_support_directory_async(self) -> str
get_downloads_directory_async(self) -> Optional[str]
get_external_cache_directories_async(self) -> Optional[List[str]]
get_external_storage_directories_async(self) -> Optional[List[str]]
get_library_directory_async(self) -> str
get_external_cache_directory_async(self) -> Optional[str]
get_temporary_directory_async(self) -> str
get_console_log_filename_async(self) -> str
🚧 Documentation is in progress 🚧
eEvent handlers can now omit e parameter, for example both of these work:
button_1.on_click = lambda: print("Clicked!")
button_2.on_click = lambda e: print("Clicked!", e)
or
def increment():
print("Increment clicked")
inc_btn.on_click = increment
Control.before_event(e) hookThe method is called before calling any event handler.
It receives an instance of ControlEvent as parameter and should return either True or False. Returning False cancels event handler. Example implementation in Page class:
def before_event(self, e: ControlEvent):
if isinstance(e, RouteChangeEvent):
if self.route == e.route:
return False
self.query()
return super().before_event(e)
ft.context.page works everywhereIt's now possible to get a reference to a current Page instance in any part of Flet program.
Flet 1.0 is not just a feature release — it's a ground-up rewrite designed to address technical debt, improve maintainability, and unlock long-term performance and flexibility. Here are some of the most impactful architectural changes:
Controls are now implemented as Python dataclasses, bringing:
str or JSONThis significantly reduces boilerplate and makes adding new controls trivial — often zero-maintenance.
A new binary serialization protocol (MessagePack) replaces the JSON-based message format:
Control property names in Dart now exactly match their Python counterparts, making it easier to debug and extend across both runtimes.
Flet 1.0 is a major release and includes breaking changes — for good reason!
The Flet team maintains a list of known breaking changes in this issue.
If you discover something else that’s broken or incorrect, please submit a new issue or discussion. Once confirmed, we’ll update the list.
Below is a summary of the most significant and impactful breaking changes:
Flet 1.0 adopts a single-threaded async UI model, similar to JavaScript or Flutter. This design makes concurrency more predictable and better suited for the browser and mobile platforms.
time.sleep() will freeze the UI. Instead of time.sleep() use async def event handlers and using await asyncio.sleep() for delays.asyncio.to_thread(...).🚧 Example with CPU-bound method updating progress bar 🚧
All controls' get- and set- methods are async now.
Methods that do not return any results have fire-and-forget sync wrappers.
🚧 Documentation is in progress 🚧
ft.run() replaces ft.run()target argument renamed to main and the rest of method arguments stays the same. A new before_main argument added (see above).
Page splitPage split into Page and PageView.
To support Flet embedding with multi-views.
It's not a breaking-change per-se if you just continue to use page instance methods or properties.
🚧 Documentation is in progress 🚧
To display a dialog, banner, snack bar, drawer, or any similar popup control, use page.show_dialog(dialog_control) instead of page.open().
To close the topmost popup, use page.pop_dialog().
page.drawer and page.end_drawer were removed.
:::note We might re-introduce this in the future, to fix displaying of the top menu icon button as in this example. :::
Use NavigationDrawer.position property and then page.show_dialog() to display drawer instead of page.drawer and page.end_drawer.
FilePicker is now a service and must be added to page.services list to work.
API re-worked to provide async methods immediately returning dialog results without using "result" event handlers.
files: list[FilePickerFile] = await file_picker.pick_files_async(allow_multiple=True)
file_name: str = await file_picker.save_file_async()
dir_name: str = await file_picker.get_directory_path_async()
Full examples:
Instead of page.set_clipboard() use page.clipboard.set_async().
Instead of page.get_clipboard() use page.clipboard.get_async().
"Client storage" is now "Shared Preferences".
page.client_storage property renamed to page.shared_preferences.
In scrollable controls on_scroll_interval property renamed to scroll_interval.
All buttons: no text property, use content instead.
Chart controls have been moved to a separate package flet-charts.
e.target is a number now, not a string.
We are releasing Flet 1.0 Alpha as 0.70.0.devXXXX.
:::info
Going forward Flet 1.0 will be called v1 and Flet 0.x will be called v0.
main branch of Flet repository will have Flet 1.0 and v0 branch will have Flet 0.x.
:::
:::caution Make sure you are installing Flet pre-release to a new virtual environment. :::
To install Flet v1 Alpha with pip:
pip install --pre 'flet[all]>=0.70.0.dev0'
or install with uv:
uv add 'flet[all]>=0.70.0.dev0' --prerelease=allow
or add flet >=0.70.0.dev0 to dependencies of your Python project.
flet buildTo make flet build work with Flet 1.0 Alpha specify exact version of flet and all extension packages in dependencies section of your pyproject.toml:
dependencies=[
"flet >=0.70.0.dev0",
"flet-audio >=0.2.0.dev0",
"flet-video >=0.2.0.dev0",
...
]
Extensions for Flet v1 will have version 0.2.x and above and Flet v0 extensions will have version 0.1.x.
Flet 1.0 Alpha marks the beginning of a new chapter for the framework — one focused on performance, maintainability, and developer experience. It introduces a streamlined architecture, a powerful declarative programming model, and a reimagined extension system — all while laying the groundwork for a stable and scalable 1.0 release.
This is a technical preview, and while it's not production-ready yet, we invite you to try it out, share your feedback, and help us shape the future of Flet.
We’re incredibly excited about what’s coming next — and we’re just getting started.