Skip to content

v1: Declarative/Reactive approach in Flet #5342

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Jun 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
bb914f3
Bump Flutter to 3.32.0
FeodorFitsner May 21, 2025
4290d6d
CI: FLUTTER_VERSION: 3.32.0
FeodorFitsner May 21, 2025
974f2fd
`@data_view` decorator
FeodorFitsner May 21, 2025
0fe24f1
OptionalControlStateValue > Optional[ControlStateValue]
FeodorFitsner May 22, 2025
2b17e7a
Compare "frozen" dataclasses
FeodorFitsner May 22, 2025
dec4518
data view lists comparison
FeodorFitsner May 27, 2025
6b9c263
Fixed in-place collection comparison. added tests.
FeodorFitsner May 27, 2025
3ab31e0
Center control
FeodorFitsner May 29, 2025
c5da9e7
ControlBuilder control
FeodorFitsner May 29, 2025
967d9a1
Merge branch 'v1' into feodor/v1-reactive
FeodorFitsner May 29, 2025
3967c9e
Simple event handlers can now omit `e` parameter
FeodorFitsner May 29, 2025
3d0cc6e
Fixed event handlers comparison for frozen controls
FeodorFitsner May 29, 2025
5a34962
Tests for nested ControlBuilders
FeodorFitsner May 29, 2025
11a6499
Merge branch 'v1' into feodor/v1-reactive
FeodorFitsner May 29, 2025
8057fc5
Squashed commit of the following:
FeodorFitsner Jun 8, 2025
b62d64f
Pyodide 0.27.7
FeodorFitsner Jun 8, 2025
7d49afa
Skip fields are comparable
FeodorFitsner Jun 8, 2025
28210d8
ft.context.page works in all event handlers
FeodorFitsner Jun 8, 2025
fc1726c
Added cache to ControlBuilder
FeodorFitsner Jun 8, 2025
f7a04b2
`list_key` and `scroll_key` migrated to a unified `key`
FeodorFitsner Jun 10, 2025
1c0c4af
Fixed method calls for frozen controls
FeodorFitsner Jun 11, 2025
e6799ab
Strongly-typed events and event handlers
FeodorFitsner Jun 11, 2025
82ed1c1
Merge branch 'v1' into feodor/v1-reactive
FeodorFitsner Jun 11, 2025
e2f11f3
update and auto-update fixed for frozen controls
FeodorFitsner Jun 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ skip_commits:

environment:
python_stack: python 3.12
FLUTTER_VERSION: 3.29.2
FLUTTER_VERSION: 3.32.2
GITHUB_TOKEN:
secure: 9SKIwc3VSfYJ5IChvNR74hQprJ0DRmcV9pPX+8KyE6IXIdfMsX6ikeUmMhJGRu3ztkZaF45jmU7Xn/6tauXQXhDBxK1N8kFHFSAnq6LjUXyhS0TZKX/H+jDozBeVbCXp
TWINE_USERNAME: __token__
Expand Down Expand Up @@ -68,8 +68,8 @@ environment:
- job_name: Build Flet for web
job_group: build_flet
job_depends_on: build_flet_package
PYODIDE_URL: https://github.com/pyodide/pyodide/releases/download/0.27.5/pyodide-core-0.27.5.tar.bz2
PYODIDE_CDN_URL: https://cdn.jsdelivr.net/pyodide/v0.27.5/full
PYODIDE_URL: https://github.com/pyodide/pyodide/releases/download/0.27.7/pyodide-core-0.27.7.tar.bz2
PYODIDE_CDN_URL: https://cdn.jsdelivr.net/pyodide/v0.27.7/full
APPVEYOR_BUILD_WORKER_IMAGE: ubuntu2004

- job_name: Test Python 3.10
Expand Down
2 changes: 1 addition & 1 deletion client/.fvmrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"flutter": "3.29.0"
"flutter": "3.32.2"
}
24 changes: 12 additions & 12 deletions client/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ packages:
dependency: transitive
description:
name: async
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
url: "https://pub.dev"
source: hosted
version: "2.12.0"
version: "2.13.0"
audioplayers:
dependency: transitive
description:
Expand Down Expand Up @@ -189,10 +189,10 @@ packages:
dependency: transitive
description:
name: fake_async
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
version: "1.3.2"
version: "1.3.3"
ffi:
dependency: transitive
description:
Expand Down Expand Up @@ -377,10 +377,10 @@ packages:
dependency: transitive
description:
name: intl
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
url: "https://pub.dev"
source: hosted
version: "0.19.0"
version: "0.20.2"
json_annotation:
dependency: transitive
description:
Expand All @@ -393,10 +393,10 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
url: "https://pub.dev"
source: hosted
version: "10.0.8"
version: "10.0.9"
leak_tracker_flutter_testing:
dependency: transitive
description:
Expand Down Expand Up @@ -1014,10 +1014,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
url: "https://pub.dev"
source: hosted
version: "14.3.1"
version: "15.0.0"
volume_controller:
dependency: transitive
description:
Expand Down Expand Up @@ -1070,10 +1070,10 @@ packages:
dependency: transitive
description:
name: webdriver
sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8"
sha256: "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade"
url: "https://pub.dev"
source: hosted
version: "3.0.4"
version: "3.1.0"
webview_flutter_android:
dependency: "direct overridden"
description:
Expand Down
2 changes: 1 addition & 1 deletion client/web/python.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const defaultPyodideUrl = "https://cdn.jsdelivr.net/pyodide/v0.27.5/full/pyodide.js";
const defaultPyodideUrl = "https://cdn.jsdelivr.net/pyodide/v0.27.7/full/pyodide.js";

let _apps = {};
let _documentUrl = document.URL;
Expand Down
22 changes: 0 additions & 22 deletions packages/flet/lib/src/controls/center.dart

This file was deleted.

19 changes: 19 additions & 0 deletions packages/flet/lib/src/controls/control_builder.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import 'package:flutter/widgets.dart';

import '../extensions/control.dart';
import '../models/control.dart';
import 'base_controls.dart';

class ControlBuilderControl extends StatelessWidget {
final Control control;

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

@override
Widget build(BuildContext context) {
debugPrint("ControlBuilder.build: ${control.id}");
return BaseControl(
control: control,
child: control.buildWidget("content") ?? const SizedBox.shrink());
}
}
22 changes: 11 additions & 11 deletions packages/flet/lib/src/controls/control_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:provider/provider.dart';

import '../flet_backend.dart';
import '../models/control.dart';
import '../utils/keys.dart';
import '../utils/numbers.dart';
import '../utils/theme.dart';
import '../widgets/control_inherited_notifier.dart';
Expand All @@ -15,21 +16,20 @@ class ControlWidget extends StatelessWidget {

@override
Widget build(BuildContext context) {
Key? controlKey;
var key = control.getString("scroll_key", "")!;
if (key != "") {
if (key.startsWith("test:")) {
controlKey = Key(key.substring(5));
} else {
var globalKey = controlKey = GlobalKey();
FletBackend.of(context).globalKeys[key] = globalKey;
}
ControlKey? controlKey = control.getKey("key");
Key? key;
if (controlKey is ControlScrollKey) {
key = GlobalKey();
FletBackend.of(context).globalKeys[controlKey.toString()] =
key as GlobalKey;
} else if (controlKey != null) {
key = ValueKey(controlKey.value);
}

Widget? widget;
if (control.get("_skip_inherited_notifier") == true) {
for (var extension in FletBackend.of(context).extensions) {
widget = extension.createWidget(controlKey, control);
widget = extension.createWidget(key, control);
if (widget != null) return widget;
}
widget = ErrorControl("Unknown control: ${control.type}");
Expand All @@ -41,7 +41,7 @@ class ControlWidget extends StatelessWidget {

Widget? cw;
for (var extension in FletBackend.of(context).extensions) {
cw = extension.createWidget(controlKey, control);
cw = extension.createWidget(key, control);
if (cw != null) return cw;
}

Expand Down
3 changes: 2 additions & 1 deletion packages/flet/lib/src/controls/cupertino_navigation_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ class _CupertinoNavigationBarControlState

void _onTap(int index) {
_selectedIndex = index;
widget.control.updateProperties({"selected_index": _selectedIndex});
widget.control
.updateProperties({"selected_index": _selectedIndex}, notify: true);
widget.control.triggerEvent("change", _selectedIndex);
}

Expand Down
4 changes: 2 additions & 2 deletions packages/flet/lib/src/controls/navigation_rail.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ class _NavigationRailControlState extends State<NavigationRailControl>
void _destinationChanged(int index) {
_selectedIndex = index;
debugPrint("NavigationRail selected_index: $_selectedIndex");
widget.control.updateProperties({"selected_index": _selectedIndex});
widget.control
.updateProperties({"selected_index": _selectedIndex}, notify: true);
widget.control.triggerEvent("change", _selectedIndex);
}

Expand Down Expand Up @@ -88,7 +89,6 @@ class _NavigationRailControlState extends State<NavigationRailControl>
destinations:
widget.control.children("destinations").map((destinationControl) {
destinationControl.notifyParent = true;
var label = destinationControl.getString("label", "")!;
var icon = destinationControl.buildWidget("icon") ??
Icon(parseIcon(destinationControl.getString("icon")));
var selectedIcon = destinationControl
Expand Down
11 changes: 7 additions & 4 deletions packages/flet/lib/src/controls/scrollable_control.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:flet/flet.dart';
import 'package:flutter/material.dart';

import '../utils/keys.dart';

class ScrollableControl extends StatefulWidget {
final Control control;
final Widget child;
Expand Down Expand Up @@ -40,15 +42,16 @@ class _ScrollableControlState extends State<ScrollableControl>
debugPrint("ScrollableControl.$name($args)");
var offset = parseDouble(args["offset"]);
var delta = parseDouble(args["delta"]);
var scrollKey = args["scroll_key"] != null
? widget.control.backend.globalKeys[args["scroll_key"]]
var scrollKey = parseKey(args["scroll_key"]);
var globalKey = scrollKey != null
? widget.control.backend.globalKeys[scrollKey.toString()]
: null;
switch (name) {
case "scroll_to":
var duration = parseDuration(args["duration"], Duration.zero)!;
var curve = parseCurve(args["curve"], Curves.ease)!;
if (scrollKey != null) {
var ctx = scrollKey.currentContext;
if (globalKey != null) {
var ctx = globalKey.currentContext;
if (ctx != null) {
Scrollable.ensureVisible(ctx, duration: duration, curve: curve);
}
Expand Down
8 changes: 4 additions & 4 deletions packages/flet/lib/src/flet_backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ class FletBackend extends ChangeNotifier {
_reconnectDelayMs = 0;
error = "";

page.applyPatch(resp.patch, this);
page.update(resp.patch);

// drain send queue
debugPrint("Send queue: ${_sendQueue.length}");
Expand Down Expand Up @@ -250,7 +250,7 @@ class FletBackend extends ChangeNotifier {
}

// update page details
page.applyPatch({"route": newRoute, "platform": platform}, this,
page.update({"route": newRoute, "platform": platform},
shouldNotify: false);

// connect to the server
Expand Down Expand Up @@ -362,7 +362,7 @@ class FletBackend extends ChangeNotifier {
var control = controlsIndex.get(id);
if (control != null) {
if (dart) {
control.applyPatch(props, this, shouldNotify: notify);
control.update(props, shouldNotify: notify);
}
if (python) {
_send(Message(
Expand Down Expand Up @@ -487,7 +487,7 @@ class FletBackend extends ChangeNotifier {

_send(Message message, {bool unbuffered = false}) {
if (unbuffered || !isLoading) {
debugPrint("_send: ${message.payload}");
debugPrint("_send: ${message.action} ${message.payload}");
_backendChannel?.send(message);
} else {
_sendQueue.add(message);
Expand Down
6 changes: 3 additions & 3 deletions packages/flet/lib/src/flet_core_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ import 'controls/bottom_app_bar.dart';
import 'controls/bottom_sheet.dart';
import 'controls/canvas.dart';
import 'controls/card.dart';
import 'controls/center.dart';
import 'controls/chip.dart';
import 'controls/circle_avatar.dart';
import 'controls/column.dart';
import 'controls/container.dart';
import 'controls/control_builder.dart';
import 'controls/cupertino_action_sheet.dart';
import 'controls/cupertino_action_sheet_action.dart';
import 'controls/cupertino_activity_indicator.dart';
Expand Down Expand Up @@ -158,8 +158,8 @@ class FletCoreExtension extends FletExtension {
return CupertinoBottomSheetControl(key: key, control: control);
case "PopupMenuButton":
return PopupMenuButtonControl(key: key, control: control);
case "Center":
return CenterControl(key: key, control: control);
case "ControlBuilder":
return ControlBuilderControl(key: key, control: control);
case "CupertinoSlidingSegmentedButton":
return CupertinoSlidingSegmentedButtonControl(
key: key, control: control);
Expand Down
Loading