Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions packages/flet/lib/src/controls/column.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class ColumnControl extends StatelessWidget {
child = ScrollableControl(
control: control,
scrollDirection: wrap ? Axis.horizontal : Axis.vertical,
wrapIntoScrollableView: true,
child: child,
);

Expand Down
40 changes: 29 additions & 11 deletions packages/flet/lib/src/controls/list_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,11 @@ class _ListViewControlState extends State<ListViewControl> {
widget.control.getBool("build_controls_on_demand", true)!;
var firstItemPrototype =
widget.control.getBool("first_item_prototype", false)!;
var prototypeItem = firstItemPrototype
? widget.control.buildWidget("prototype_item")
: null;
var controls = widget.control.children("controls");
var prototypeItem = widget.control.buildWidget("prototype_item") ??
(firstItemPrototype && controls.isNotEmpty
? ControlWidget(control: controls.first)
: null);

Widget listView = LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
Expand All @@ -79,14 +80,31 @@ class _ListViewControlState extends State<ListViewControl> {
shrinkWrap: shrinkWrap,
padding: padding,
semanticChildCount: semanticChildCount,
itemExtent: itemExtent,
prototypeItem: prototypeItem,
children: controls
.map((item) => ControlWidget(
key: ValueKey(item.getKey("key")?.value ?? item.id),
control: item,
))
.toList(),
itemExtent: spacing > 0 ? null : itemExtent,
prototypeItem: spacing > 0 ? null : prototypeItem,
children: () {
final childWidgets = <Widget>[];
for (var index = 0; index < controls.length; index++) {
final item = controls[index];
childWidgets.add(ControlWidget(
key: ValueKey(item.getKey("key")?.value ?? item.id),
control: item,
));
if (spacing > 0 && index < controls.length - 1) {
childWidgets.add(horizontal
? dividerThickness == 0
? SizedBox(width: spacing)
: VerticalDivider(
width: spacing, thickness: dividerThickness)
: dividerThickness == 0
? SizedBox(height: spacing)
: Divider(
height: spacing,
thickness: dividerThickness));
}
}
return childWidgets;
}(),
)
: spacing > 0
? ListView.separated(
Expand Down
1 change: 1 addition & 0 deletions packages/flet/lib/src/controls/row.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class RowControl extends StatelessWidget {
child = ScrollableControl(
control: control,
scrollDirection: wrap ? Axis.vertical : Axis.horizontal,
wrapIntoScrollableView: true,
child: child);

if (control.getBool("on_scroll", false)!) {
Expand Down
28 changes: 17 additions & 11 deletions packages/flet/lib/src/controls/scrollable_control.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ class ScrollableControl extends StatefulWidget {
final Widget child;
final Axis scrollDirection;
final ScrollController? scrollController;
final bool wrapIntoScrollableView;

ScrollableControl(
{Key? key,
required this.control,
required this.child,
required this.scrollDirection,
this.scrollController})
this.scrollController,
this.wrapIntoScrollableView = false})
: super(key: key ?? ValueKey("control_${control.id}"));

@override
Expand Down Expand Up @@ -102,22 +104,26 @@ class _ScrollableControlState extends State<ScrollableControl>
return scrollMode != ScrollMode.none
? Scrollbar(
// todo: create class ScrollBarConfiguration on Py end, for more customizability
thumbVisibility: scrollMode == ScrollMode.always ||
(scrollMode == ScrollMode.adaptive && !isMobilePlatform())
? true
: false,
trackVisibility: scrollMode == ScrollMode.hidden ? false : null,
thumbVisibility: (scrollMode == ScrollMode.always ||
(scrollMode == ScrollMode.adaptive &&
!isMobilePlatform())) &&
scrollMode != ScrollMode.hidden,
thickness: scrollMode == ScrollMode.hidden
? 0
: isMobilePlatform()
? 4.0
: null,
//interactive: true,
controller: _controller,
child: SingleChildScrollView(
controller: _controller,
scrollDirection: widget.scrollDirection,
child: widget.child,
child: ScrollConfiguration(
behavior:
ScrollConfiguration.of(context).copyWith(scrollbars: false),
child: widget.wrapIntoScrollableView
? SingleChildScrollView(
controller: _controller,
scrollDirection: widget.scrollDirection,
child: widget.child,
)
: widget.child,
))
: widget.child;
}
Expand Down
8 changes: 5 additions & 3 deletions packages/flet/lib/src/controls/view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ class _ViewControlState extends State<ViewControl> {
if (_popCompleter != null && !_popCompleter!.isCompleted) {
_popCompleter?.complete(args["should_pop"]);
}
default:
throw Exception("Unknown View method: $name");
}
}

Expand Down Expand Up @@ -130,7 +128,11 @@ class _ViewControlState extends State<ViewControl> {
.toList());

Widget child = ScrollableControl(
control: control, scrollDirection: Axis.vertical, child: column);
control: control,
scrollDirection: Axis.vertical,
wrapIntoScrollableView: true,
child: column,
);

if (control.getBool("on_scroll", false)!) {
child = ScrollNotificationControl(control: control, child: child);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import flet as ft


@ft.component
def App():
items, set_items = ft.use_state(list(range(5)))

return ft.ListView(
controls=[
ft.Dismissible(
key=i,
content=ft.ListTile(title=ft.Text(f"Item {i}")),
dismiss_direction=ft.DismissDirection.HORIZONTAL,
background=ft.Container(bgcolor=ft.Colors.GREEN),
secondary_background=ft.Container(bgcolor=ft.Colors.RED),
on_dismiss=lambda e, index=i: set_items(
[item for item in items if item != index]
),
dismiss_thresholds={
ft.DismissDirection.HORIZONTAL: 0.1,
ft.DismissDirection.START_TO_END: 0.1,
},
)
for i in items
],
)


ft.run(lambda page: page.render(App))
2 changes: 1 addition & 1 deletion sdk/python/packages/flet-map/src/flet_map/marker_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Marker(ft.Control):
The coordinates of the marker.

This will be the center of the marker,
if [`alignment`][(c).] is [`Alignment.CENTER`][flet.Alignment.].
if [`alignment`][(c).] is [`Alignment.CENTER`][flet.].
"""

rotate: Optional[bool] = None
Expand Down
32 changes: 29 additions & 3 deletions sdk/python/packages/flet/docs/controls/dismissible.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,39 @@ example_images: ../examples/controls/dismissible/media

[Live example](https://flet-controls-gallery.fly.dev/layout/dismissible)

### Dismissable `ListTile`s
### Dismissible `ListTile`s

```python
--8<-- "{{ examples }}/dismissable_list_tiles.py"
--8<-- "{{ examples }}/dismissible_list_tiles.py"
```

{{ image(example_images + "/dismissable_list_tiles.gif", alt="dismissable-list-tiles", width="80%") }}
{{ image(example_images + "/dismissible_list_tiles.gif", alt="dismissible-list-tiles", width="80%") }}

### Remove Dismissible `on_dismiss` inside component

/// admonition | Important
type: warning
Always specify a key for `Dismissible` when using inside Flet component.
///

The issue you may encounter here is specific to the `Dismissible` control used inside Flet component (declarative UI).

When a user swipes (dismisses) an item, that widget is marked as “dismissed” on the Flutter side and effectively removed from the UI.
However, when Flet recalculates the UI diff on the Python side, it may attempt to reuse widgets in the list based on their order rather than their identity.

If no key is provided, Flet’s diffing algorithm can’t tell that a particular `Dismissible` corresponds to a specific item — so it assumes the items have merely shifted.
That leads to update commands like:

> “Update text in items 0…N-1, then delete the last item (N).”

On Flutter’s side, though, the already-dismissed `Dismissible` widget in the middle of the list can’t be updated — it’s gone — causing runtime errors.

**Always assign a stable, unique key to each `Dismissible`, typically based on the item’s identifier or index.**

Example:

```python
--8<-- "{{ examples }}/remove_on_dismiss_declarative.py"
```

{{ class_members(class_name) }}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Context:
{{ log.debug("Rendering " + attribute.path) }}
{% endblock logs %}

{% set attr_class_name = "doc-symbol-event" if attribute.name.startswith('on_') else "doc-symbol-attribute" %}
{% set attr_class_name = "doc-symbol-event" if attribute.name.startswith('on_') and (config.extra.events is not defined or (config.extra.events and attribute.name in config.extra.events)) else "doc-symbol-attribute" %}

<div class="doc doc-object doc-attribute">
{% with obj = attribute, html_id = attribute.path %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Context:
{% set ns = namespace(props=[], events=[]) %}
{% for attribute in ordered %}
{% if config.filters == "public" or members_list is not none or (not attribute.is_imported or attribute.is_public) %}
{% if attribute.name.startswith("on_") %}
{% if attribute.name.startswith('on_') and (config.extra.events is not defined or (config.extra.events and attribute.name in config.extra.events)) %}
{% set _ = ns.events.append(attribute) %}
{% else %}
{% set _ = ns.props.append(attribute) %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{# Split attributes into events and properties #}
{% set ns = namespace(events=[], props=[]) %}
{% for attribute in section.value %}
{% if attribute.name.startswith('on_') %}
{% if attribute.name.startswith('on_') and (config.extra.events is not defined or (config.extra.events and attribute.name in config.extra.events)) %}
{% set _ = ns.events.append(attribute) %}
{% else %}
{% set _ = ns.props.append(attribute) %}
Expand Down
2 changes: 1 addition & 1 deletion sdk/python/packages/flet/docs/types/colorscheme.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{{ class_all_options("flet.ColorScheme") }}
{{ class_all_options("flet.ColorScheme", extra={'show_class_docstring': True, 'show_children': True,'events': []}) }}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading