From 58cca4e3623b6d109d7fa6e9107fc7902f752a11 Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Fri, 20 Feb 2026 00:05:57 +0100 Subject: [PATCH 1/9] initial commit --- packages/flet/lib/src/utils/device_info.dart | 24 ++++++++ .../flet/src/flet/controls/device_info.py | 60 +++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/packages/flet/lib/src/utils/device_info.dart b/packages/flet/lib/src/utils/device_info.dart index 6cb6a9793c..66714ba022 100644 --- a/packages/flet/lib/src/utils/device_info.dart +++ b/packages/flet/lib/src/utils/device_info.dart @@ -1,3 +1,5 @@ +import 'dart:ui'; + import 'package:collection/collection.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/services.dart'; @@ -10,6 +12,17 @@ Future> getDeviceInfo() async { return deviceInfo.asMap(); } +Map getDeviceLocales() { + final locales = PlatformDispatcher.instance.locales + .map((locale) => locale.toLanguageTag()) + .where((localeTag) => localeTag.isNotEmpty) + .toList(growable: false); + return { + "language": locales.firstOrNull, + "languages": locales.isNotEmpty ? locales : null, + }; +} + extension DeviceInfoExtension on BaseDeviceInfo { Map asMap() { var deviceInfo = this; @@ -34,6 +47,7 @@ extension DeviceInfoExtension on BaseDeviceInfo { "hardware_concurrency": deviceInfo.hardwareConcurrency, }; } else { + final deviceLocales = getDeviceLocales(); if (isAndroidMobile()) { deviceInfo = (deviceInfo as AndroidDeviceInfo); return { @@ -71,6 +85,8 @@ extension DeviceInfoExtension on BaseDeviceInfo { 'preview_sdk': deviceInfo.version.previewSdkInt, 'security_patch': deviceInfo.version.securityPatch, }, + "language": deviceLocales["language"], + "languages": deviceLocales["languages"], }; } else if (isIOSMobile()) { deviceInfo = (deviceInfo as IosDeviceInfo); @@ -95,6 +111,8 @@ extension DeviceInfoExtension on BaseDeviceInfo { "version": deviceInfo.utsname.version, }, "identifier_for_vendor": deviceInfo.identifierForVendor, + "language": deviceLocales["language"], + "languages": deviceLocales["languages"], }; } else if (isLinuxDesktop()) { deviceInfo = (deviceInfo as LinuxDeviceInfo); @@ -110,6 +128,8 @@ extension DeviceInfoExtension on BaseDeviceInfo { "variant": deviceInfo.variant, "variant_id": deviceInfo.variantId, "machine_id": deviceInfo.machineId, + "language": deviceLocales["language"], + "languages": deviceLocales["languages"], }; } else if (isMacOSDesktop()) { deviceInfo = (deviceInfo as MacOsDeviceInfo); @@ -128,6 +148,8 @@ extension DeviceInfoExtension on BaseDeviceInfo { "os_release": deviceInfo.osRelease, "patch_version": deviceInfo.patchVersion, "system_guid": deviceInfo.systemGUID, + "language": deviceLocales["language"], + "languages": deviceLocales["languages"], }; } else if (isWindowsDesktop()) { deviceInfo = (deviceInfo as WindowsDeviceInfo); @@ -157,6 +179,8 @@ extension DeviceInfoExtension on BaseDeviceInfo { "registered_owner": deviceInfo.registeredOwner, "release_id": deviceInfo.releaseId, "device_id": deviceInfo.deviceId, + "language": deviceLocales["language"], + "languages": deviceLocales["languages"], }; } return {}; diff --git a/sdk/python/packages/flet/src/flet/controls/device_info.py b/sdk/python/packages/flet/src/flet/controls/device_info.py index 2ca19bf135..6a3a0e434b 100644 --- a/sdk/python/packages/flet/src/flet/controls/device_info.py +++ b/sdk/python/packages/flet/src/flet/controls/device_info.py @@ -91,6 +91,18 @@ class MacOsDeviceInfo(DeviceInfo): system_guid: Optional[str] = None """Device GUID.""" + language: Optional[str] = None + """A string representing the user's preferred language on the device. + + For example: `"en-US"`. + """ + + languages: Optional[list[str]] = None + """A list of preferred locale/language tags in priority order. + + For example: `["en-US", "de-DE"]`. + """ + class WebBrowserName(Enum): """ @@ -445,6 +457,18 @@ class AndroidDeviceInfo(DeviceInfo): Android operating system version values derived from `android.os.Build.VERSION`. """ + language: Optional[str] = None + """A string representing the user's preferred language on the device. + + For example: `"en-US"`. + """ + + languages: Optional[list[str]] = None + """A list of preferred locale/language tags in priority order. + + For example: `["en-US", "de-DE"]`. + """ + @dataclass class LinuxDeviceInfo(DeviceInfo): @@ -580,6 +604,18 @@ class LinuxDeviceInfo(DeviceInfo): hexadecimal, this corresponds to a 16-byte/128-bit value. """ + language: Optional[str] = None + """A string representing the user's preferred language on the device. + + For example: `"en-US"`. + """ + + languages: Optional[list[str]] = None + """A list of preferred locale/language tags in priority order. + + For example: `["en-US", "de-DE"]`. + """ + @dataclass class WindowsDeviceInfo(DeviceInfo): @@ -725,6 +761,18 @@ class WindowsDeviceInfo(DeviceInfo): registry key. """ + language: Optional[str] = None + """A string representing the user's preferred language on the device. + + For example: `"en-US"`. + """ + + languages: Optional[list[str]] = None + """A list of preferred locale/language tags in priority order. + + For example: `["en-US", "de-DE"]`. + """ + @dataclass class IosUtsname: @@ -829,3 +877,15 @@ class IosDeviceInfo(DeviceInfo): More info: https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor """ + + language: Optional[str] = None + """A string representing the user's preferred language on the device. + + For example: `"en-US"`. + """ + + languages: Optional[list[str]] = None + """A list of preferred locale/language tags in priority order. + + For example: `["en-US", "de-DE"]`. + """ From 70cfcb865d93d4e88138ac33f5658d637ca52bc5 Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Fri, 20 Feb 2026 01:14:42 +0100 Subject: [PATCH 2/9] feat(locale): add language_tag property and locale identifier formatting --- .../packages/flet/src/flet/controls/types.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/sdk/python/packages/flet/src/flet/controls/types.py b/sdk/python/packages/flet/src/flet/controls/types.py index 862b57a00d..4a1b8a8fc6 100644 --- a/sdk/python/packages/flet/src/flet/controls/types.py +++ b/sdk/python/packages/flet/src/flet/controls/types.py @@ -1140,6 +1140,42 @@ def __post_init__(self): if self.language_code == "": raise ValueError("language_code cannot be empty") + @property + def language_tag(self) -> str: + """ + Returns a syntactically valid Unicode BCP47 Locale Identifier. + + See [this](https://www.unicode.org/reports/tr35) for technical details. + + Examples: `en`, `es-419`, `hi-Deva-IN`, `zh-Hans-CN` + """ + return self._raw_to_string("-") + + def __str__(self) -> str: + return self._raw_to_string("_") + + def _raw_to_string(self, separator: str) -> str: + """Returns the locale identifier joined by the given separator. + + Components are ordered as language, script (if any), and country + (if any). Empty or `None` values are omitted. + + Args: + separator: String used to join the subtags. + + Returns: + The formatted locale identifier. + """ + out_parts: list[str] = [self.language_code] + + if self.script_code is not None and self.script_code != "": + out_parts.append(self.script_code) + + if self.country_code is not None and self.country_code != "": + out_parts.append(self.country_code) + + return separator.join(out_parts) + @dataclass class LocaleConfiguration: From 48f5a7f7c11752b00c8e17ea06770f1260b18a9f Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Fri, 20 Feb 2026 01:15:04 +0100 Subject: [PATCH 3/9] feat(device_info): replace `language` and `languages` fields with unified `locales` representation --- packages/flet/lib/src/utils/device_info.dart | 34 +++---- packages/flet/lib/src/utils/locale.dart | 8 ++ .../flet/src/flet/controls/device_info.py | 91 +++++-------------- 3 files changed, 43 insertions(+), 90 deletions(-) diff --git a/packages/flet/lib/src/utils/device_info.dart b/packages/flet/lib/src/utils/device_info.dart index 66714ba022..7f315439c1 100644 --- a/packages/flet/lib/src/utils/device_info.dart +++ b/packages/flet/lib/src/utils/device_info.dart @@ -2,6 +2,7 @@ import 'dart:ui'; import 'package:collection/collection.dart'; import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flet/src/utils/locale.dart'; import 'package:flutter/services.dart'; import 'platform.dart'; @@ -12,21 +13,15 @@ Future> getDeviceInfo() async { return deviceInfo.asMap(); } -Map getDeviceLocales() { - final locales = PlatformDispatcher.instance.locales - .map((locale) => locale.toLanguageTag()) - .where((localeTag) => localeTag.isNotEmpty) - .toList(growable: false); - return { - "language": locales.firstOrNull, - "languages": locales.isNotEmpty ? locales : null, - }; -} +List> getDeviceLocales() => + PlatformDispatcher.instance.locales + .map((locale) => locale.toMap()) + .toList(); extension DeviceInfoExtension on BaseDeviceInfo { Map asMap() { var deviceInfo = this; - + final deviceLocales = getDeviceLocales(); if (isWebPlatform()) { deviceInfo = (deviceInfo as WebBrowserInfo); return { @@ -45,9 +40,9 @@ extension DeviceInfoExtension on BaseDeviceInfo { "vendor_sub": deviceInfo.vendorSub, "max_touch_points": deviceInfo.maxTouchPoints, "hardware_concurrency": deviceInfo.hardwareConcurrency, + "locales": deviceLocales, }; } else { - final deviceLocales = getDeviceLocales(); if (isAndroidMobile()) { deviceInfo = (deviceInfo as AndroidDeviceInfo); return { @@ -85,8 +80,7 @@ extension DeviceInfoExtension on BaseDeviceInfo { 'preview_sdk': deviceInfo.version.previewSdkInt, 'security_patch': deviceInfo.version.securityPatch, }, - "language": deviceLocales["language"], - "languages": deviceLocales["languages"], + "locales": deviceLocales, }; } else if (isIOSMobile()) { deviceInfo = (deviceInfo as IosDeviceInfo); @@ -111,8 +105,7 @@ extension DeviceInfoExtension on BaseDeviceInfo { "version": deviceInfo.utsname.version, }, "identifier_for_vendor": deviceInfo.identifierForVendor, - "language": deviceLocales["language"], - "languages": deviceLocales["languages"], + "locales": deviceLocales, }; } else if (isLinuxDesktop()) { deviceInfo = (deviceInfo as LinuxDeviceInfo); @@ -128,8 +121,7 @@ extension DeviceInfoExtension on BaseDeviceInfo { "variant": deviceInfo.variant, "variant_id": deviceInfo.variantId, "machine_id": deviceInfo.machineId, - "language": deviceLocales["language"], - "languages": deviceLocales["languages"], + "locales": deviceLocales, }; } else if (isMacOSDesktop()) { deviceInfo = (deviceInfo as MacOsDeviceInfo); @@ -148,8 +140,7 @@ extension DeviceInfoExtension on BaseDeviceInfo { "os_release": deviceInfo.osRelease, "patch_version": deviceInfo.patchVersion, "system_guid": deviceInfo.systemGUID, - "language": deviceLocales["language"], - "languages": deviceLocales["languages"], + "locales": deviceLocales, }; } else if (isWindowsDesktop()) { deviceInfo = (deviceInfo as WindowsDeviceInfo); @@ -179,8 +170,7 @@ extension DeviceInfoExtension on BaseDeviceInfo { "registered_owner": deviceInfo.registeredOwner, "release_id": deviceInfo.releaseId, "device_id": deviceInfo.deviceId, - "language": deviceLocales["language"], - "languages": deviceLocales["languages"], + "locales": deviceLocales, }; } return {}; diff --git a/packages/flet/lib/src/utils/locale.dart b/packages/flet/lib/src/utils/locale.dart index d2cdcbabe8..b1260ca332 100644 --- a/packages/flet/lib/src/utils/locale.dart +++ b/packages/flet/lib/src/utils/locale.dart @@ -72,4 +72,12 @@ extension LocaleExtention on Locale { ]]) { return delegates.every((d) => d.isSupported(this)); } + + Map toMap() { + return { + "language_code": languageCode, + "country_code": countryCode, + "script_code": scriptCode, + }; + } } diff --git a/sdk/python/packages/flet/src/flet/controls/device_info.py b/sdk/python/packages/flet/src/flet/controls/device_info.py index 6a3a0e434b..71e8435f13 100644 --- a/sdk/python/packages/flet/src/flet/controls/device_info.py +++ b/sdk/python/packages/flet/src/flet/controls/device_info.py @@ -16,8 +16,10 @@ "WindowsDeviceInfo", ] +from flet.controls.types import Locale -@dataclass + +@dataclass(kw_only=True) class DeviceInfo: """ Base class for device information. @@ -31,8 +33,21 @@ class DeviceInfo: - [`WindowsDeviceInfo`][flet.] """ + locales: list[Locale] + """ + The full system-reported supported locales of the device. -@dataclass + This establishes the language and formatting conventions that application + should, if possible, use to render their user interface. + + The list is ordered in order of priority, with lower-indexed locales being + preferred over higher-indexed ones. The first element is the primary locale. + + The [on_locale_change] callback is called whenever this value changes. + """ + + +@dataclass(kw_only=True) class MacOsDeviceInfo(DeviceInfo): active_cpus: int """Number of active CPUs.""" @@ -91,18 +106,6 @@ class MacOsDeviceInfo(DeviceInfo): system_guid: Optional[str] = None """Device GUID.""" - language: Optional[str] = None - """A string representing the user's preferred language on the device. - - For example: `"en-US"`. - """ - - languages: Optional[list[str]] = None - """A list of preferred locale/language tags in priority order. - - For example: `["en-US", "de-DE"]`. - """ - class WebBrowserName(Enum): """ @@ -134,7 +137,7 @@ class WebBrowserName(Enum): """Unknown web browser""" -@dataclass +@dataclass(kw_only=True) class WebDeviceInfo(DeviceInfo): """ Information derived from `navigator`. @@ -261,7 +264,7 @@ class WebDeviceInfo(DeviceInfo): """ -@dataclass +@dataclass(kw_only=True) class AndroidBuildVersion: code_name: str """ @@ -304,7 +307,7 @@ class AndroidBuildVersion: """ -@dataclass +@dataclass(kw_only=True) class AndroidDeviceInfo(DeviceInfo): available_ram_size: int """Total available RAM size in bytes.""" @@ -457,20 +460,8 @@ class AndroidDeviceInfo(DeviceInfo): Android operating system version values derived from `android.os.Build.VERSION`. """ - language: Optional[str] = None - """A string representing the user's preferred language on the device. - - For example: `"en-US"`. - """ - - languages: Optional[list[str]] = None - """A list of preferred locale/language tags in priority order. - - For example: `["en-US", "de-DE"]`. - """ - -@dataclass +@dataclass(kw_only=True) class LinuxDeviceInfo(DeviceInfo): """ Device information for a Linux system. @@ -604,20 +595,8 @@ class LinuxDeviceInfo(DeviceInfo): hexadecimal, this corresponds to a 16-byte/128-bit value. """ - language: Optional[str] = None - """A string representing the user's preferred language on the device. - - For example: `"en-US"`. - """ - - languages: Optional[list[str]] = None - """A list of preferred locale/language tags in priority order. - - For example: `["en-US", "de-DE"]`. - """ - -@dataclass +@dataclass(kw_only=True) class WindowsDeviceInfo(DeviceInfo): computer_name: str """The computer's fully-qualified DNS name, where available.""" @@ -761,18 +740,6 @@ class WindowsDeviceInfo(DeviceInfo): registry key. """ - language: Optional[str] = None - """A string representing the user's preferred language on the device. - - For example: `"en-US"`. - """ - - languages: Optional[list[str]] = None - """A list of preferred locale/language tags in priority order. - - For example: `["en-US", "de-DE"]`. - """ - @dataclass class IosUtsname: @@ -798,7 +765,7 @@ class IosUtsname: """Version level.""" -@dataclass +@dataclass(kw_only=True) class IosDeviceInfo(DeviceInfo): available_ram_size: int """Current unallocated RAM size of the device in megabytes.""" @@ -877,15 +844,3 @@ class IosDeviceInfo(DeviceInfo): More info: https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor """ - - language: Optional[str] = None - """A string representing the user's preferred language on the device. - - For example: `"en-US"`. - """ - - languages: Optional[list[str]] = None - """A list of preferred locale/language tags in priority order. - - For example: `["en-US", "de-DE"]`. - """ From 965dbf228514baf8362101c01e45ead5bf9ec0fa Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Fri, 20 Feb 2026 01:18:08 +0100 Subject: [PATCH 4/9] add .fvm/ to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 4a09f882ea..c719d39134 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,9 @@ # Pycharm Files .idea/ +# FVM +.fvm/ + # mac specific .DS_Store *.bkp From e6b5d2a84e299f9bf49050a9220b0fd9abad7f52 Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Fri, 20 Feb 2026 02:14:54 +0100 Subject: [PATCH 5/9] docs + example --- packages/flet/lib/src/controls/page.dart | 20 +++++++++++++ .../examples/controls/page/device_locale.py | 26 +++++++++++++++++ .../flet/docs/types/localechangeevent.md | 1 + sdk/python/packages/flet/mkdocs.yml | 1 + sdk/python/packages/flet/src/flet/__init__.py | 2 ++ .../flet/src/flet/controls/device_info.py | 3 +- .../packages/flet/src/flet/controls/page.py | 29 ++++++++++++++++++- 7 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 sdk/python/examples/controls/page/device_locale.py create mode 100644 sdk/python/packages/flet/docs/types/localechangeevent.md diff --git a/packages/flet/lib/src/controls/page.dart b/packages/flet/lib/src/controls/page.dart index d8cf0d7dfc..f54a55aad4 100644 --- a/packages/flet/lib/src/controls/page.dart +++ b/packages/flet/lib/src/controls/page.dart @@ -65,6 +65,7 @@ class _PageControlState extends State with WidgetsBindingObserver { Control? _windowControl; bool? _prevOnKeyboardEvent; bool _keyboardHandlerSubscribed = false; + List? _locales; String? _prevViewRoutes; final Set _pendingPoppedViewRoutes = {}; final Set _sentViewPopEventsForRoutes = {}; @@ -151,6 +152,25 @@ class _PageControlState extends State with WidgetsBindingObserver { _updateMultiViews(); } + @override + void didChangeLocales(List? locales) { + super.didChangeLocales(locales); + + final effectiveLocales = + locales ?? WidgetsBinding.instance.platformDispatcher.locales; + final nextLocales = List.unmodifiable(effectiveLocales); + if (_locales != null && + const ListEquality().equals(_locales!, nextLocales)) { + return; + } + + _locales = nextLocales; + widget.control.triggerEvent( + 'locale_change', + {'locales': nextLocales.map((l) => l.toMap()).toList(growable: false)}, + ); + } + @override void dispose() { debugPrint("Page.dispose: ${widget.control.id}"); diff --git a/sdk/python/examples/controls/page/device_locale.py b/sdk/python/examples/controls/page/device_locale.py new file mode 100644 index 0000000000..e240c53f18 --- /dev/null +++ b/sdk/python/examples/controls/page/device_locale.py @@ -0,0 +1,26 @@ +import flet as ft + + +async def main(page: ft.Page): + def format_locales(locales: list[ft.Locale]) -> str: + """Format locale list for display.""" + return ", ".join(str(loc) for loc in locales) + + def handle_locale_change(e: ft.LocaleChangeEvent): + page.add(ft.Text(f"Locales changed: {format_locales(e.locales)}")) + + page.on_locale_change = handle_locale_change + page.scroll = ft.ScrollMode.AUTO + page.horizontal_alignment = ft.CrossAxisAlignment.CENTER + + initial_locales = (await page.get_device_info()).locales + page.add( + ft.Text(f"Initial locales: {format_locales(initial_locales)}"), + ft.Text( + "Change your system or browser language to trigger on_locale_change.", + color=ft.Colors.BLUE, + ), + ) + + +ft.run(main) diff --git a/sdk/python/packages/flet/docs/types/localechangeevent.md b/sdk/python/packages/flet/docs/types/localechangeevent.md new file mode 100644 index 0000000000..d46bf019d0 --- /dev/null +++ b/sdk/python/packages/flet/docs/types/localechangeevent.md @@ -0,0 +1 @@ +{{ class_all_options("flet.LocaleChangeEvent") }} diff --git a/sdk/python/packages/flet/mkdocs.yml b/sdk/python/packages/flet/mkdocs.yml index 7f96fedd5b..8273bd48ed 100644 --- a/sdk/python/packages/flet/mkdocs.yml +++ b/sdk/python/packages/flet/mkdocs.yml @@ -911,6 +911,7 @@ nav: - KeyDownEvent: types/keydownevent.md - KeyRepeatEvent: types/keyrepeatevent.md - KeyUpEvent: types/keyupevent.md + - LocaleChangeEvent: types/localechangeevent.md - LoginEvent: types/loginevent.md - LongPressEndEvent: types/longpressendevent.md - LongPressStartEvent: types/longpressstartevent.md diff --git a/sdk/python/packages/flet/src/flet/__init__.py b/sdk/python/packages/flet/src/flet/__init__.py index ed0eb64e88..e8cd63b1a4 100644 --- a/sdk/python/packages/flet/src/flet/__init__.py +++ b/sdk/python/packages/flet/src/flet/__init__.py @@ -396,6 +396,7 @@ from flet.controls.page import ( AppLifecycleStateChangeEvent, KeyboardEvent, + LocaleChangeEvent, LoginEvent, MultiViewAddEvent, MultiViewRemoveEvent, @@ -838,6 +839,7 @@ "ListTileTitleAlignment", "ListView", "Locale", + "LocaleChangeEvent", "LocaleConfiguration", "LoginEvent", "LongPressDownEvent", diff --git a/sdk/python/packages/flet/src/flet/controls/device_info.py b/sdk/python/packages/flet/src/flet/controls/device_info.py index 71e8435f13..858d95a859 100644 --- a/sdk/python/packages/flet/src/flet/controls/device_info.py +++ b/sdk/python/packages/flet/src/flet/controls/device_info.py @@ -43,7 +43,8 @@ class DeviceInfo: The list is ordered in order of priority, with lower-indexed locales being preferred over higher-indexed ones. The first element is the primary locale. - The [on_locale_change] callback is called whenever this value changes. + The [`Page.on_locale_change`][flet.] event is called + whenever this value changes. """ diff --git a/sdk/python/packages/flet/src/flet/controls/page.py b/sdk/python/packages/flet/src/flet/controls/page.py index fc4e6f1351..b91afb7aea 100644 --- a/sdk/python/packages/flet/src/flet/controls/page.py +++ b/sdk/python/packages/flet/src/flet/controls/page.py @@ -57,6 +57,7 @@ AppLifecycleState, Brightness, DeviceOrientation, + Locale, PagePlatform, Url, UrlTarget, @@ -134,6 +135,24 @@ class PlatformBrightnessChangeEvent(Event["Page"]): brightness: Brightness +@dataclass +class LocaleChangeEvent(Event["Page"]): + """ + Event payload describing a change in the host platform's locale preferences. + + See also: + - [`Page.on_locale_change`][flet.]: event called when locale preferences/settings + of the host platform have changed. + """ + + locales: list[Locale] + """ + The full, ordered list of locales reported by the host platform. + + The first item represents the highest-priority locale. + """ + + @dataclass class ViewPopEvent(Event["Page"]): route: str @@ -316,11 +335,19 @@ class Page(BasePage): Called when brightness of app host platform has changed. """ + on_locale_change: Optional[EventHandler[LocaleChangeEvent]] = None + """ + Called when the locale preferences/settings of the host platform have changed. + + For example, when the user updates device language + settings or browser preferred languages. + """ + on_app_lifecycle_state_change: Optional[ EventHandler[AppLifecycleStateChangeEvent] ] = None """ - Triggers when app lifecycle state changes. + Called when app lifecycle state changes. """ on_route_change: Optional[EventHandler[RouteChangeEvent]] = None From 2d2cf8dfdee7333b12dc6a09d229104007e23d68 Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Fri, 20 Feb 2026 02:19:08 +0100 Subject: [PATCH 6/9] more docs --- sdk/python/packages/flet/docs/controls/page.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sdk/python/packages/flet/docs/controls/page.md b/sdk/python/packages/flet/docs/controls/page.md index 2cbe5d586e..b2a5e58740 100644 --- a/sdk/python/packages/flet/docs/controls/page.md +++ b/sdk/python/packages/flet/docs/controls/page.md @@ -50,4 +50,10 @@ If you need this feature when packaging a desktop app using --8<-- "{{ examples }}/semantics_debugger.py" ``` +### Get device locales + +```python +--8<-- "{{ examples }}/device_locale.py" +``` + {{ class_members(class_name) }} From 764c9d6bdb888a644277eeaf2d4f3b8ad114c37e Mon Sep 17 00:00:00 2001 From: TheEthicalBoy <98978078+ndonkoHenri@users.noreply.github.com> Date: Fri, 20 Feb 2026 08:52:38 +0100 Subject: [PATCH 7/9] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- sdk/python/packages/flet/src/flet/controls/device_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/python/packages/flet/src/flet/controls/device_info.py b/sdk/python/packages/flet/src/flet/controls/device_info.py index 44fd29e148..99cfb5f79e 100644 --- a/sdk/python/packages/flet/src/flet/controls/device_info.py +++ b/sdk/python/packages/flet/src/flet/controls/device_info.py @@ -713,7 +713,7 @@ class WindowsDeviceInfo(DeviceInfo): build_lab_ex: str """Value of `HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\BuildLabEx` registry key. - For example: `"22000.1.amd64free.co_release.210604-1628"`. + For example: `"22000.1.amd64fre.co_release.210604-1628"`. """ # noqa: E501 # digital_product_id: str From a51cff0b1be2d3d816a60aacda1451df2e2cffeb Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Fri, 20 Feb 2026 09:09:04 +0100 Subject: [PATCH 8/9] remove unused import of 'package:collection/collection.dart' --- packages/flet/lib/src/utils/device_info.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/flet/lib/src/utils/device_info.dart b/packages/flet/lib/src/utils/device_info.dart index e46da99067..25de2a4392 100644 --- a/packages/flet/lib/src/utils/device_info.dart +++ b/packages/flet/lib/src/utils/device_info.dart @@ -1,11 +1,10 @@ import 'dart:ui'; -import 'package:collection/collection.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flet/src/utils/locale.dart'; import 'package:flutter/services.dart'; -import 'enums.dart'; +import 'enums.dart'; import 'platform.dart'; /// Returns device information as a Map. From 7ec9c1f184ee79ad933b52c3de6c1bc3cc9670a0 Mon Sep 17 00:00:00 2001 From: ndonkoHenri Date: Sat, 21 Feb 2026 00:33:36 +0100 Subject: [PATCH 9/9] docs: Add documentation for FLET_HIDE_WINDOW_ON_START environment variable --- .../examples/controls/page/window_hidden_on_start.py | 8 +++++++- sdk/python/packages/flet/docs/publish/index.md | 1 + .../packages/flet/docs/reference/environment-variables.md | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/sdk/python/examples/controls/page/window_hidden_on_start.py b/sdk/python/examples/controls/page/window_hidden_on_start.py index 331bc2b459..1750371282 100644 --- a/sdk/python/examples/controls/page/window_hidden_on_start.py +++ b/sdk/python/examples/controls/page/window_hidden_on_start.py @@ -1,7 +1,7 @@ # # Use -n (--hidden) option to run this example with `flet run` command: # -# flet run -n examples/controls/page/window_hidden_on_start.py +# flet run --hidden window_hidden_on_start.py # import asyncio @@ -12,11 +12,17 @@ async def main(page: ft.Page): print("Window is hidden on start. Will show after 3 seconds...") page.add(ft.Text("Hello!")) + + # some configuration that we want to do before showing the window page.window.width = 300 page.window.height = 200 page.update() await page.window.center() + + # wait for 3 seconds before showing the window await asyncio.sleep(3) + + # show the window page.window.visible = True page.update() diff --git a/sdk/python/packages/flet/docs/publish/index.md b/sdk/python/packages/flet/docs/publish/index.md index 2ea412ceb8..89b8810590 100644 --- a/sdk/python/packages/flet/docs/publish/index.md +++ b/sdk/python/packages/flet/docs/publish/index.md @@ -846,6 +846,7 @@ Its value is determined in the following order of precedence: - `[tool.flet..app].hide_window_on_start`, where `` can be `windows`, `macos` or `linux` - `[tool.flet.app].hide_window_on_start` +- [`FLET_HIDE_WINDOW_ON_START`](../reference/environment-variables.md#flet_hide_window_on_start) #### Example diff --git a/sdk/python/packages/flet/docs/reference/environment-variables.md b/sdk/python/packages/flet/docs/reference/environment-variables.md index bf3ac35a49..da55bf7baa 100644 --- a/sdk/python/packages/flet/docs/reference/environment-variables.md +++ b/sdk/python/packages/flet/docs/reference/environment-variables.md @@ -44,6 +44,12 @@ Whether to skip running `flutter doctor` when a build fails. Defaults to `False`. +### `FLET_HIDE_WINDOW_ON_START` + +Set to `true` to start app with the main window hidden. + +Defaults to `False`. + ### `FLET_FORCE_WEB_SERVER` Set to `true` to force running app as a web app. Automatically set on headless Linux hosts.