Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3b797e5
initial commit
ndonkoHenri Aug 7, 2025
1860cb1
combine `url` and `url_target` into `Url`
ndonkoHenri Aug 7, 2025
328acfb
part 2 - combine `url` and `url_target` into `Url`
ndonkoHenri Aug 7, 2025
10f1999
fix TextSpan
ndonkoHenri Aug 7, 2025
b1654b5
Update sdk/python/packages/flet/src/flet/controls/material/container.py
ndonkoHenri Aug 7, 2025
6249c8e
Update packages/flet/lib/src/controls/cupertino_list_tile.dart
ndonkoHenri Aug 7, 2025
ffc34b1
Merge branch 'main' into v1-icon-button-variants
ndonkoHenri Aug 9, 2025
f725e78
`canvas.capture_async()` returns the captured image
ndonkoHenri Aug 9, 2025
aa46974
tests: use fvm (if available) in flutter command
ndonkoHenri Aug 9, 2025
9bff0dc
tests: `IconButton` tests
ndonkoHenri Aug 9, 2025
195e669
fix some examples
ndonkoHenri Aug 8, 2025
d86d591
`page.on_platform_brightness_change` of event type `PlatformBrightnes…
ndonkoHenri Aug 8, 2025
1bd9760
document `PlatformBrightnessChangeEvent`
ndonkoHenri Aug 9, 2025
18f248f
run with fvm using `FLET_TEST_USE_FVM` env var
ndonkoHenri Aug 9, 2025
dabd00e
create `utils/inspect_golden_images.py`
ndonkoHenri Aug 9, 2025
40a944d
revert: `canvas.capture_async()` returns the captured image
ndonkoHenri Aug 9, 2025
3eca70c
Update sdk/python/packages/flet/tests/test_object_diff_in_place.py
ndonkoHenri Aug 9, 2025
16d2022
Update sdk/python/packages/flet/src/flet/controls/types.py
ndonkoHenri Aug 9, 2025
83a8098
Update sdk/python/packages/flet/src/flet/controls/core/canvas/path.py
ndonkoHenri Aug 9, 2025
3c7d1ce
Merge branch 'main' into v1-icon-button-variants
FeodorFitsner Aug 16, 2025
7465cdd
Fixes after merge
FeodorFitsner Aug 16, 2025
f814146
Refactor IconButton variants and update tests
FeodorFitsner Aug 16, 2025
3ceafc8
Remove inspect_golden_images utility script
FeodorFitsner Aug 16, 2025
1c63ae4
Add icon button variants and expand integration tests
FeodorFitsner Aug 16, 2025
8f99028
Refactor alert dialog tests and update fixtures
FeodorFitsner Aug 16, 2025
f13dc40
Update terminology from 'web' to 'web mode'
FeodorFitsner Aug 16, 2025
bb8b967
Update splash_radius assertion to use integer comparison
FeodorFitsner Aug 16, 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
5 changes: 2 additions & 3 deletions packages/flet/lib/src/controls/button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class _ButtonControlState extends State<ButtonControl> with FletStoreMixin {
bool isTextButton = widget.control.type == "TextButton";
bool isOutlinedButton = widget.control.type == "OutlinedButton";

var url = widget.control.getString("url");
var url = widget.control.getUrl("url");
var iconColor = widget.control.getColor("icon_color", context);
var clipBehavior =
widget.control.getClipBehavior("clip_behavior", Clip.none)!;
Expand All @@ -65,8 +65,7 @@ class _ButtonControlState extends State<ButtonControl> with FletStoreMixin {
Function()? onPressed = !widget.control.disabled
? () {
if (url != null) {
openWebBrowser(url,
webWindowName: widget.control.getString("url_target"));
openWebBrowser(url);
}
widget.control.triggerEvent("click");
}
Expand Down
20 changes: 10 additions & 10 deletions packages/flet/lib/src/controls/canvas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -471,11 +471,11 @@ class FletCustomPainter extends CustomPainter {
}
for (var elem in (j as List)) {
var type = elem["_type"];
if (type == "moveto") {
if (type == "MoveTo") {
path.moveTo(parseDouble(elem["x"], 0)!, parseDouble(elem["y"], 0)!);
} else if (type == "lineto") {
} else if (type == "LineTo") {
path.lineTo(parseDouble(elem["x"], 0)!, parseDouble(elem["y"], 0)!);
} else if (type == "arc") {
} else if (type == "Arc") {
path.addArc(
Rect.fromLTWH(
parseDouble(elem["x"], 0)!,
Expand All @@ -484,20 +484,20 @@ class FletCustomPainter extends CustomPainter {
parseDouble(elem["height"], 0)!),
parseDouble(elem["start_angle"], 0)!,
parseDouble(elem["sweep_angle"], 0)!);
} else if (type == "arcto") {
} else if (type == "ArcTo") {
path.arcToPoint(
Offset(parseDouble(elem["x"], 0)!, parseDouble(elem["y"], 0)!),
radius: Radius.circular(parseDouble(elem["radius"], 0)!),
rotation: parseDouble(elem["rotation"], 0)!,
largeArc: parseBool(elem["large_arc"], false)!,
clockwise: parseBool(elem["clockwise"], true)!);
} else if (type == "oval") {
} else if (type == "Oval") {
path.addOval(Rect.fromLTWH(
parseDouble(elem["x"], 0)!,
parseDouble(elem["y"], 0)!,
parseDouble(elem["width"], 0)!,
parseDouble(elem["height"], 0)!));
} else if (type == "rect") {
} else if (type == "Rect") {
var borderRadius = parseBorderRadius(elem["border_radius"]);
path.addRRect(RRect.fromRectAndCorners(
Rect.fromLTWH(
Expand All @@ -509,25 +509,25 @@ class FletCustomPainter extends CustomPainter {
topRight: borderRadius?.topRight ?? Radius.zero,
bottomLeft: borderRadius?.bottomLeft ?? Radius.zero,
bottomRight: borderRadius?.bottomRight ?? Radius.zero));
} else if (type == "conicto") {
} else if (type == "QuadraticTo") {
path.conicTo(
parseDouble(elem["cp1x"], 0)!,
parseDouble(elem["cp1y"], 0)!,
parseDouble(elem["x"], 0)!,
parseDouble(elem["y"], 0)!,
parseDouble(elem["w"], 0)!);
} else if (type == "cubicto") {
} else if (type == "CubicTo") {
path.cubicTo(
parseDouble(elem["cp1x"], 0)!,
parseDouble(elem["cp1y"], 0)!,
parseDouble(elem["cp2x"], 0)!,
parseDouble(elem["cp2y"], 0)!,
parseDouble(elem["x"], 0)!,
parseDouble(elem["y"], 0)!);
} else if (type == "subpath") {
} else if (type == "SubPath") {
path.addPath(buildPath(elem["elements"]),
Offset(parseDouble(elem["x"], 0)!, parseDouble(elem["y"], 0)!));
} else if (type == "close") {
} else if (type == "Close") {
path.close();
}
}
Expand Down
7 changes: 3 additions & 4 deletions packages/flet/lib/src/controls/container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ class ContainerControl extends StatelessWidget with FletStoreMixin {
var ink = control.getBool("ink", false)!;
var onClick = control.getBool("on_click", false)!;
var onTapDown = control.getBool("on_tap_down", false)!;
var url = control.getString("url");
var urlTarget = control.getString("url_target");
var url = control.getUrl("url");
var onLongPress = control.getBool("on_long_press", false)!;
var onHover = control.getBool("on_hover", false)!;
var ignoreInteractions = control.getBool("ignore_interactions", false)!;
Expand Down Expand Up @@ -66,7 +65,7 @@ class ContainerControl extends StatelessWidget with FletStoreMixin {
onTap: onClick || url != null || onTapDown
? () {
if (url != null) {
openWebBrowser(url, webWindowName: urlTarget);
openWebBrowser(url);
}
if (onClick) {
control.triggerEvent("click");
Expand Down Expand Up @@ -162,7 +161,7 @@ class ContainerControl extends StatelessWidget with FletStoreMixin {
onTap: onClick || url != null
? () {
if (url != null) {
openWebBrowser(url, webWindowName: urlTarget);
openWebBrowser(url);
}
if (onClick) {
control.triggerEvent("click");
Expand Down
5 changes: 2 additions & 3 deletions packages/flet/lib/src/controls/cupertino_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,11 @@ class _CupertinoButtonControlState extends State<CupertinoButtonControl> {
.copyWith(color: color),
child: child);
}
var url = widget.control.getString("url");
var url = widget.control.getUrl("url");
Function()? onPressed = !widget.control.disabled
? () {
if (url != null) {
openWebBrowser(url,
webWindowName: widget.control.getString("url_target"));
openWebBrowser(url);
}
widget.control.triggerEvent("click");
}
Expand Down
7 changes: 3 additions & 4 deletions packages/flet/lib/src/controls/cupertino_list_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,16 @@ class CupertinoListTileControl extends StatelessWidget {
control.getDouble("leading_to_title", notched ? 12.0 : 16.0)!;
var onclick = control.getBool("on_click", false)!;
var toggleInputs = control.getBool("toggle_inputs", false)!;
var url = control.getString("url");
var urlTarget = control.getString("url_target");
var url = control.getUrl("url");

Function()? onPressed =
(onclick || toggleInputs || url != "") && !control.disabled
(onclick || toggleInputs || url != null) && !control.disabled
? () {
if (toggleInputs) {
_clickNotifier.onClick();
}
if (url != null) {
openWebBrowser(url, webWindowName: urlTarget);
openWebBrowser(url);
}
if (onclick) {
control.triggerEvent("click");
Expand Down
5 changes: 2 additions & 3 deletions packages/flet/lib/src/controls/floating_action_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ class FloatingActionButtonControl extends StatelessWidget {

var content = control.buildTextOrWidget("content");
var icon = control.buildIconOrWidget("icon");
var url = control.getString("url");
var urlTarget = control.getString("url_target");
var url = control.getUrl("url");
var disabledElevation = control.getDouble("disabled_elevation");
var elevation = control.getDouble("elevation");
var hoverElevation = control.getDouble("hover_elevation");
Expand All @@ -49,7 +48,7 @@ class FloatingActionButtonControl extends StatelessWidget {
? null
: () {
if (url != null) {
openWebBrowser(url, webWindowName: urlTarget);
openWebBrowser(url);
}
control.triggerEvent("click");
};
Expand Down
111 changes: 83 additions & 28 deletions packages/flet/lib/src/controls/icon_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ class _IconButtonControlState extends State<IconButtonControl>
var highlightColor = widget.control.getColor("highlight_color", context);
var selectedIconColor =
widget.control.getColor("selected_icon_color", context);
var bgcolor = widget.control.getColor("bgcolor", context);
var disabledColor = widget.control.getColor("disabled_color", context);
var hoverColor = widget.control.getColor("hover_color", context);
var splashColor = widget.control.getColor("splash_color", context);
Expand All @@ -82,15 +81,13 @@ class _IconButtonControlState extends State<IconButtonControl>
var autofocus = widget.control.getBool("autofocus", false)!;
var enableFeedback = widget.control.getBool("enable_feedback", true)!;
var selected = widget.control.getBool("selected", false)!;
var url = widget.control.getString("url");
var urlTarget = widget.control.getString("url_target");
var mouseCursor = widget.control.getMouseCursor("mouse_cursor");
var visualDensity = widget.control.getVisualDensity("visual_density");
var url = widget.control.getUrl("url");

Function()? onPressed = !widget.control.disabled
? () {
if (url != null) {
openWebBrowser(url, webWindowName: urlTarget);
openWebBrowser(url);
}
widget.control.triggerEvent("click");
}
Expand All @@ -109,7 +106,7 @@ class _IconButtonControlState extends State<IconButtonControl>

var theme = Theme.of(context);
var style = parseButtonStyle(
widget.control.get("style"), Theme.of(context),
widget.control.internals?["style"], Theme.of(context),
defaultForegroundColor: theme.colorScheme.primary,
defaultBackgroundColor: Colors.transparent,
defaultOverlayColor: Colors.transparent,
Expand All @@ -126,27 +123,74 @@ class _IconButtonControlState extends State<IconButtonControl>
if (icon is Control) {
iconWidget = ControlWidget(control: icon);
} else if (icon is String) {
iconWidget = Icon(
widget.control.getIcon("icon"),
color: iconColor,
);
iconWidget = Icon(widget.control.getIcon("icon"), color: iconColor);
} else if (content != null) {
iconWidget = ControlWidget(control: content);
}

Widget? selectedIconWidget;

if (selectedIcon is Control) {
selectedIconWidget = ControlWidget(control: selectedIcon);
} else if (selectedIcon is String) {
selectedIconWidget = Icon(
widget.control.getIcon("selected_icon"),
color: selectedIconColor,
);
selectedIconWidget = Icon(widget.control.getIcon("selected_icon"),
color: selectedIconColor);
}

if (iconWidget != null) {
button = IconButton(
if (iconWidget == null) {
return const ErrorControl(
"IconButton must have either icon or a visible content specified.");
}

var variant = widget.control.type;

if (variant == "FilledIconButton") {
button = IconButton.filled(
autofocus: autofocus,
focusNode: _focusNode,
highlightColor: highlightColor,
disabledColor: disabledColor,
hoverColor: hoverColor,
enableFeedback: enableFeedback,
padding: padding,
alignment: alignment,
focusColor: focusColor,
splashColor: splashColor,
splashRadius: splashRadius,
icon: iconWidget,
iconSize: iconSize,
mouseCursor: mouseCursor,
style: style,
isSelected: selected,
constraints: sizeConstraints,
onLongPress: onLongPressHandler,
onHover: onHoverHandler,
selectedIcon: selectedIconWidget,
onPressed: onPressed);
} else if (variant == "FilledTonalIconButton") {
button = IconButton.filledTonal(
autofocus: autofocus,
focusNode: _focusNode,
highlightColor: highlightColor,
disabledColor: disabledColor,
hoverColor: hoverColor,
enableFeedback: enableFeedback,
padding: padding,
alignment: alignment,
focusColor: focusColor,
splashColor: splashColor,
splashRadius: splashRadius,
icon: iconWidget,
iconSize: iconSize,
mouseCursor: mouseCursor,
style: style,
isSelected: selected,
constraints: sizeConstraints,
onLongPress: onLongPressHandler,
onHover: onHoverHandler,
selectedIcon: selectedIconWidget,
onPressed: onPressed);
} else if (variant == "OutlinedIconButton") {
button = IconButton.outlined(
autofocus: autofocus,
focusNode: _focusNode,
highlightColor: highlightColor,
Expand All @@ -161,7 +205,6 @@ class _IconButtonControlState extends State<IconButtonControl>
icon: iconWidget,
iconSize: iconSize,
mouseCursor: mouseCursor,
visualDensity: visualDensity,
style: style,
isSelected: selected,
constraints: sizeConstraints,
Expand All @@ -170,16 +213,28 @@ class _IconButtonControlState extends State<IconButtonControl>
selectedIcon: selectedIconWidget,
onPressed: onPressed);
} else {
return const ErrorControl(
"IconButton must have either icon or a visible content specified.");
}

if (bgcolor != null) {
button = Container(
decoration:
ShapeDecoration(color: bgcolor, shape: const CircleBorder()),
child: button,
);
button = IconButton(
autofocus: autofocus,
focusNode: _focusNode,
highlightColor: highlightColor,
disabledColor: disabledColor,
hoverColor: hoverColor,
enableFeedback: enableFeedback,
padding: padding,
alignment: alignment,
focusColor: focusColor,
splashColor: splashColor,
splashRadius: splashRadius,
icon: iconWidget,
iconSize: iconSize,
mouseCursor: mouseCursor,
style: style,
isSelected: selected,
constraints: sizeConstraints,
onLongPress: onLongPressHandler,
onHover: onHoverHandler,
selectedIcon: selectedIconWidget,
onPressed: onPressed);
}

return ConstrainedControl(control: widget.control, child: button);
Expand Down
7 changes: 3 additions & 4 deletions packages/flet/lib/src/controls/list_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,16 @@ class ListTileControl extends StatelessWidget with FletStoreMixin {
var trailing = control.buildIconOrWidget("trailing");
var onClick = control.getBool("on_click", false)!;
var toggleInputs = control.getBool("toggle_inputs", false)!;
var url = control.getString("url");
var urlTarget = control.getString("url_target");
var url = control.getUrl("url");

Function()? onPressed =
(onClick || toggleInputs || url != "") && !control.disabled
(onClick || toggleInputs || url != null) && !control.disabled
? () {
if (toggleInputs) {
_clickNotifier.onClick();
}
if (url != null) {
openWebBrowser(url, webWindowName: urlTarget);
openWebBrowser(url);
}
if (onClick) {
control.triggerEvent("click");
Expand Down
2 changes: 1 addition & 1 deletion packages/flet/lib/src/controls/markdown.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class MarkdownControl extends StatelessWidget {
onTapText: () => control.triggerEvent("tap_text"),
onTapLink: (String text, String? href, String title) {
if (autoFollowLinks && href != null) {
openWebBrowser(href, webWindowName: autoFollowLinksTarget);
openWebBrowser(Url(href, autoFollowLinksTarget));
}
control.triggerEvent("tap_link", href);
});
Expand Down
2 changes: 1 addition & 1 deletion packages/flet/lib/src/flet_backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ class FletBackend extends ChangeNotifier {
debugPrint("Platform brightness updated: $newBrightness");
platformBrightness = newBrightness;
updateControl(page.id, {"platform_brightness": newBrightness.name});
triggerControlEvent(page, "platform_brightness_change");
triggerControlEvent(page, "platform_brightness_change", newBrightness.name);
notifyListeners();
}

Expand Down
3 changes: 3 additions & 0 deletions packages/flet/lib/src/flet_core_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ class FletCoreExtension extends FletExtension {
case "InteractiveViewer":
return InteractiveViewerControl(key: key, control: control);
case "IconButton":
case "FilledIconButton":
case "FilledTonalIconButton":
case "OutlinedIconButton":
return IconButtonControl(key: key, control: control);
case "CupertinoActivityIndicator":
return CupertinoActivityIndicatorControl(key: key, control: control);
Expand Down
Loading