Skip to content

Commit

Permalink
feat: add ListView.build_controls_on_demand property for optional o…
Browse files Browse the repository at this point in the history
…n-demand building of controls (#4751)

* initial commit

* reorder ListTile props
  • Loading branch information
ndonkoHenri authored Jan 22, 2025
1 parent d342a47 commit 6ed3098
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 62 deletions.
121 changes: 69 additions & 52 deletions packages/flet/lib/src/controls/list_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,27 @@ class _ListViewControlState extends State<ListViewControl> {
bool? adaptive =
widget.control.attrBool("adaptive") ?? widget.parentAdaptive;

final horizontal = widget.control.attrBool("horizontal", false)!;
final spacing = widget.control.attrDouble("spacing", 0)!;
final dividerThickness = widget.control.attrDouble("dividerThickness", 0)!;
final itemExtent = widget.control.attrDouble("itemExtent");
final cacheExtent = widget.control.attrDouble("cacheExtent");
final semanticChildCount = widget.control.attrInt("semanticChildCount");
final firstItemPrototype =
var horizontal = widget.control.attrBool("horizontal", false)!;
var spacing = widget.control.attrDouble("spacing", 0)!;
var dividerThickness = widget.control.attrDouble("dividerThickness", 0)!;
var itemExtent = widget.control.attrDouble("itemExtent");
var cacheExtent = widget.control.attrDouble("cacheExtent");
var semanticChildCount = widget.control.attrInt("semanticChildCount");
var firstItemPrototype =
widget.control.attrBool("firstItemPrototype", false)!;
final padding = parseEdgeInsets(widget.control, "padding");
final reverse = widget.control.attrBool("reverse", false)!;
var padding = parseEdgeInsets(widget.control, "padding");
var reverse = widget.control.attrBool("reverse", false)!;
var clipBehavior =
parseClip(widget.control.attrString("clipBehavior"), Clip.hardEdge)!;

List<Control> visibleControls =
widget.children.where((c) => c.isVisible).toList();
List<Control> ctrls = widget.children.where((c) => c.isVisible).toList();
var scrollDirection = horizontal ? Axis.horizontal : Axis.vertical;
var buildControlsOnDemand =
widget.control.attrBool("buildControlsOnDemand", true)!;
var prototypeItem = firstItemPrototype && widget.children.isNotEmpty
? createControl(widget.control, ctrls[0].id, disabled,
parentAdaptive: adaptive)
: null;

Widget listView = LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
Expand All @@ -77,56 +82,68 @@ class _ListViewControlState extends State<ListViewControl> {
var shrinkWrap =
(!horizontal && constraints.maxHeight == double.infinity) ||
(horizontal && constraints.maxWidth == double.infinity);

Widget child = spacing > 0
? ListView.separated(
Widget child = !buildControlsOnDemand
? ListView(
controller: _controller,
cacheExtent: cacheExtent,
reverse: reverse,
clipBehavior: clipBehavior,
scrollDirection: scrollDirection,
shrinkWrap: shrinkWrap,
padding: padding,
itemCount: widget.children.length,
itemBuilder: (context, index) {
return createControl(
widget.control, visibleControls[index].id, disabled,
parentAdaptive: adaptive);
},
separatorBuilder: (context, index) {
return horizontal
? dividerThickness == 0
? SizedBox(width: spacing)
: VerticalDivider(
width: spacing, thickness: dividerThickness)
: dividerThickness == 0
? SizedBox(height: spacing)
: Divider(
height: spacing, thickness: dividerThickness);
},
)
: ListView.builder(
controller: _controller,
clipBehavior: clipBehavior,
semanticChildCount: semanticChildCount,
reverse: reverse,
cacheExtent: cacheExtent,
scrollDirection: scrollDirection,
shrinkWrap: shrinkWrap,
padding: padding,
itemCount: widget.children.length,
itemExtent: itemExtent,
itemBuilder: (context, index) {
return createControl(
widget.control, visibleControls[index].id, disabled,
parentAdaptive: adaptive);
},
prototypeItem: firstItemPrototype && widget.children.isNotEmpty
? createControl(
widget.control, visibleControls[0].id, disabled,
parentAdaptive: adaptive)
: null,
);
children: ctrls
.map((c) => createControl(widget.control, c.id, disabled,
parentAdaptive: adaptive))
.toList(),
prototypeItem: prototypeItem,
)
: spacing > 0
? ListView.separated(
controller: _controller,
cacheExtent: cacheExtent,
reverse: reverse,
clipBehavior: clipBehavior,
scrollDirection: scrollDirection,
shrinkWrap: shrinkWrap,
padding: padding,
itemCount: widget.children.length,
itemBuilder: (context, index) {
return createControl(
widget.control, ctrls[index].id, disabled,
parentAdaptive: adaptive);
},
separatorBuilder: (context, index) {
return horizontal
? dividerThickness == 0
? SizedBox(width: spacing)
: VerticalDivider(
width: spacing, thickness: dividerThickness)
: dividerThickness == 0
? SizedBox(height: spacing)
: Divider(
height: spacing, thickness: dividerThickness);
},
)
: ListView.builder(
controller: _controller,
clipBehavior: clipBehavior,
semanticChildCount: semanticChildCount,
reverse: reverse,
cacheExtent: cacheExtent,
scrollDirection: scrollDirection,
shrinkWrap: shrinkWrap,
padding: padding,
itemCount: widget.children.length,
itemExtent: itemExtent,
itemBuilder: (context, index) {
return createControl(
widget.control, ctrls[index].id, disabled,
parentAdaptive: adaptive);
},
prototypeItem: prototypeItem,
);

child = ScrollableControl(
control: widget.control,
Expand Down
16 changes: 8 additions & 8 deletions sdk/python/packages/flet/src/flet/core/list_tile.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,15 @@ def main(page):

def __init__(
self,
content_padding: Optional[PaddingValue] = None,
bgcolor: Optional[ColorValue] = None,
bgcolor_activated: Optional[str] = None,
hover_color: Optional[ColorValue] = None,
leading: Optional[Control] = None,
title: Optional[Control] = None,
subtitle: Optional[Control] = None,
trailing: Optional[Control] = None,
is_three_line: Optional[bool] = None,
leading: Optional[Control] = None,
trailing: Optional[Control] = None,
content_padding: PaddingValue = None,
bgcolor: Optional[ColorValue] = None,
bgcolor_activated: Optional[str] = None,
hover_color: Optional[ColorValue] = None,
selected: Optional[bool] = None,
dense: Optional[bool] = None,
autofocus: Optional[bool] = None,
Expand All @@ -114,8 +114,8 @@ def __init__(
subtitle_text_style: Optional[TextStyle] = None,
leading_and_trailing_text_style: Optional[TextStyle] = None,
min_height: OptionalNumber = None,
on_click=None,
on_long_press=None,
on_click: OptionalControlEventCallable = None,
on_long_press: OptionalControlEventCallable = None,
#
# ConstrainedControl and AdaptiveControl
#
Expand Down
16 changes: 14 additions & 2 deletions sdk/python/packages/flet/src/flet/core/list_view.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Any, Callable, List, Optional, Sequence, Union
from typing import Any, List, Optional, Sequence, Union

from flet.core.adaptive_control import AdaptiveControl
from flet.core.animation import AnimationValue
Expand All @@ -10,6 +10,7 @@
ClipBehavior,
OffsetValue,
OptionalControlEventCallable,
OptionalEventCallable,
PaddingValue,
ResponsiveNumber,
RotateValue,
Expand Down Expand Up @@ -68,13 +69,14 @@ def __init__(
clip_behavior: Optional[ClipBehavior] = None,
semantic_child_count: Optional[int] = None,
cache_extent: OptionalNumber = None,
build_controls_on_demand: Optional[bool] = None,
#
# ScrollableControl specific
#
auto_scroll: Optional[bool] = None,
reverse: Optional[bool] = None,
on_scroll_interval: OptionalNumber = None,
on_scroll: Optional[Callable[[OnScrollEvent], None]] = None,
on_scroll: OptionalEventCallable[OnScrollEvent] = None,
#
# ConstrainedControl
#
Expand Down Expand Up @@ -160,6 +162,7 @@ def __init__(
self.clip_behavior = clip_behavior
self.semantic_child_count = semantic_child_count
self.cache_extent = cache_extent
self.build_controls_on_demand = build_controls_on_demand

def _get_control_name(self):
return "listview"
Expand Down Expand Up @@ -265,3 +268,12 @@ def semantic_child_count(self) -> Optional[int]:
@semantic_child_count.setter
def semantic_child_count(self, value: Optional[int]):
self._set_attr("semanticChildCount", value)

# build_controls_on_demand
@property
def build_controls_on_demand(self) -> Optional[bool]:
return self._get_attr("buildControlsOnDemand", data_type="bool", def_value=True)

@build_controls_on_demand.setter
def build_controls_on_demand(self, value: Optional[bool]):
self._set_attr("buildControlsOnDemand", value)

0 comments on commit 6ed3098

Please sign in to comment.