From b841a6e69d8b5a501b66e5b4f6d98ce2fed4cfe4 Mon Sep 17 00:00:00 2001 From: Feodor Fitsner Date: Fri, 3 Jun 2022 20:35:41 -0700 Subject: [PATCH] Row, Column and ListView spacing and scrolling improvements (#13) --- client/lib/controls/column.dart | 2 ++ client/lib/controls/list_view.dart | 17 ++++++++-- client/lib/controls/page.dart | 3 +- client/lib/controls/row.dart | 9 ++++-- client/lib/controls/scrollable_control.dart | 32 ++++++++++++++---- sdk/python/flet/column.py | 12 +++++++ sdk/python/flet/list_view.py | 36 +++++++++++++++++++++ sdk/python/flet/page.py | 10 ++++++ sdk/python/flet/row.py | 12 +++++++ 9 files changed, 120 insertions(+), 13 deletions(-) diff --git a/client/lib/controls/column.dart b/client/lib/controls/column.dart index 9afac3751..27e318db8 100644 --- a/client/lib/controls/column.dart +++ b/client/lib/controls/column.dart @@ -33,6 +33,7 @@ class ColumnControl extends StatelessWidget { m.name.toLowerCase() == control.attrString("scroll", "")!.toLowerCase(), orElse: () => ScrollMode.none); + final autoScroll = control.attrBool("autoScroll", false)!; bool disabled = control.isDisabled || parentDisabled; List controls = []; @@ -79,6 +80,7 @@ class ColumnControl extends StatelessWidget { child: widget, scrollDirection: wrap ? Axis.horizontal : Axis.vertical, scrollMode: scrollMode, + autoScroll: autoScroll, ) : widget, parent, diff --git a/client/lib/controls/list_view.dart b/client/lib/controls/list_view.dart index 5ab3b0819..7fa7705d0 100644 --- a/client/lib/controls/list_view.dart +++ b/client/lib/controls/list_view.dart @@ -20,7 +20,6 @@ class ListViewControl extends StatelessWidget { final ScrollController _controller = ScrollController(); -// This is what you're looking for! void _scrollDown() { _controller.animateTo( _controller.position.maxScrollExtent, @@ -38,6 +37,9 @@ class ListViewControl extends StatelessWidget { final horizontal = control.attrBool("horizontal", false)!; final autoScroll = control.attrBool("autoScroll", false)!; final spacing = control.attrDouble("spacing", 0)!; + final dividerThickness = control.attrDouble("dividerThickness", 0)!; + final itemExtent = control.attrDouble("itemExtent"); + final firstItemPrototype = control.attrBool("firstItemPrototype", false)!; final padding = parseEdgeInsets(control, "padding"); List visibleControls = children.where((c) => c.isVisible).toList(); @@ -60,7 +62,15 @@ class ListViewControl extends StatelessWidget { control, visibleControls[index].id, disabled); }, separatorBuilder: (context, index) { - return Divider(height: spacing); + return horizontal + ? dividerThickness == 0 + ? SizedBox(width: spacing) + : VerticalDivider( + width: spacing, thickness: dividerThickness) + : dividerThickness == 0 + ? SizedBox(height: spacing) + : Divider( + height: spacing, thickness: dividerThickness); }, ) : ListView.builder( @@ -68,11 +78,12 @@ class ListViewControl extends StatelessWidget { scrollDirection: horizontal ? Axis.horizontal : Axis.vertical, padding: padding, itemCount: children.length, + itemExtent: itemExtent, itemBuilder: (context, index) { return createControl( control, visibleControls[index].id, disabled); }, - prototypeItem: children.isNotEmpty + prototypeItem: firstItemPrototype && children.isNotEmpty ? createControl(control, visibleControls[0].id, disabled) : null, ), diff --git a/client/lib/controls/page.dart b/client/lib/controls/page.dart index 04609ec16..6ee2fde38 100644 --- a/client/lib/controls/page.dart +++ b/client/lib/controls/page.dart @@ -42,7 +42,7 @@ class PageControl extends StatelessWidget { control.attrString("scroll", "")!.toLowerCase(), orElse: () => ScrollMode.none); - debugPrint("scrollMode: $scrollMode"); + final autoScroll = control.attrBool("autoScroll", false)!; Control? offstage; Control? appBar; @@ -168,6 +168,7 @@ class PageControl extends StatelessWidget { child: column, scrollDirection: Axis.vertical, scrollMode: scrollMode, + autoScroll: autoScroll, ) : column)), ...offstageWidgets, diff --git a/client/lib/controls/row.dart b/client/lib/controls/row.dart index dc14aa82c..234f20ce3 100644 --- a/client/lib/controls/row.dart +++ b/client/lib/controls/row.dart @@ -33,6 +33,7 @@ class RowControl extends StatelessWidget { m.name.toLowerCase() == control.attrString("scroll", "")!.toLowerCase(), orElse: () => ScrollMode.none); + final autoScroll = control.attrBool("autoScroll", false)!; bool disabled = control.isDisabled || parentDisabled; List controls = []; @@ -75,18 +76,22 @@ class RowControl extends StatelessWidget { children: controls, ), wrap: wrap, - scrollMode: scrollMode), + scrollMode: scrollMode, + autoScroll: autoScroll), parent, control); } Widget wrapAutoScroll(Widget child, - {required bool wrap, required ScrollMode scrollMode}) { + {required bool wrap, + required ScrollMode scrollMode, + required bool autoScroll}) { return scrollMode != ScrollMode.none ? ScrollableControl( child: child, scrollDirection: wrap ? Axis.vertical : Axis.horizontal, scrollMode: scrollMode, + autoScroll: autoScroll, ) : child; } diff --git a/client/lib/controls/scrollable_control.dart b/client/lib/controls/scrollable_control.dart index 12a75c805..4fe4c5438 100644 --- a/client/lib/controls/scrollable_control.dart +++ b/client/lib/controls/scrollable_control.dart @@ -9,12 +9,14 @@ class ScrollableControl extends StatefulWidget { final Widget child; final Axis scrollDirection; final ScrollMode scrollMode; + final bool autoScroll; const ScrollableControl( {Key? key, required this.child, required this.scrollDirection, - required this.scrollMode}) + required this.scrollMode, + required this.autoScroll}) : super(key: key); @override @@ -24,16 +26,32 @@ class ScrollableControl extends StatefulWidget { class _ScrollableControlState extends State { final ScrollController _controller = ScrollController(); + void _scrollDown() { + _controller.animateTo( + _controller.position.maxScrollExtent, + duration: const Duration(seconds: 1), + curve: Curves.fastOutSlowIn, + ); + } + @override Widget build(BuildContext context) { - bool isAlwaysShown = widget.scrollMode == ScrollMode.always || - (widget.scrollMode == ScrollMode.adaptive && - !kIsWeb && - !Platform.isIOS && - !Platform.isAndroid); + bool? thumbVisibility = widget.scrollMode == ScrollMode.always || + (widget.scrollMode == ScrollMode.adaptive && + !kIsWeb && + !Platform.isIOS && + !Platform.isAndroid) + ? true + : null; + + if (widget.autoScroll) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _scrollDown(); + }); + } return Scrollbar( - isAlwaysShown: isAlwaysShown, + thumbVisibility: thumbVisibility, controller: _controller, child: SingleChildScrollView( controller: _controller, diff --git a/sdk/python/flet/column.py b/sdk/python/flet/column.py index 4744126fa..91d45573a 100644 --- a/sdk/python/flet/column.py +++ b/sdk/python/flet/column.py @@ -35,6 +35,7 @@ def __init__( wrap: bool = None, run_spacing: OptionalNumber = None, scroll: ScrollMode = None, + auto_scroll: bool = None, ): ConstrainedControl.__init__( self, @@ -58,6 +59,7 @@ def __init__( self.run_spacing = run_spacing self.__scroll = False self.scroll = scroll + self.auto_scroll = auto_scroll def _get_control_name(self): return "column" @@ -144,6 +146,16 @@ def scroll(self, value: ScrollMode): value = "none" self._set_attr("scroll", value) + # auto_scroll + @property + def auto_scroll(self): + return self._get_attr("autoScroll") + + @auto_scroll.setter + @beartype + def auto_scroll(self, value: Optional[bool]): + self._set_attr("autoScroll", value) + # controls @property def controls(self): diff --git a/sdk/python/flet/list_view.py b/sdk/python/flet/list_view.py index e32e4b98a..594a18ec0 100644 --- a/sdk/python/flet/list_view.py +++ b/sdk/python/flet/list_view.py @@ -25,6 +25,9 @@ def __init__( # horizontal: bool = None, spacing: OptionalNumber = None, + item_extent: OptionalNumber = None, + first_item_prototype: bool = None, + divider_thickness: OptionalNumber = None, padding: PaddingValue = None, auto_scroll: bool = None, ): @@ -44,6 +47,9 @@ def __init__( self.controls = controls self.horizontal = horizontal self.spacing = spacing + self.divider_thickness = divider_thickness + self.item_extent = item_extent + self.first_item_prototype = first_item_prototype self.padding = padding self.auto_scroll = auto_scroll @@ -77,6 +83,36 @@ def spacing(self): def spacing(self, value: OptionalNumber): self._set_attr("spacing", value) + # divider_thickness + @property + def divider_thickness(self): + return self._get_attr("dividerThickness") + + @divider_thickness.setter + @beartype + def divider_thickness(self, value: OptionalNumber): + self._set_attr("dividerThickness", value) + + # item_extent + @property + def item_extent(self): + return self._get_attr("itemExtent") + + @item_extent.setter + @beartype + def item_extent(self, value: OptionalNumber): + self._set_attr("itemExtent", value) + + # first_item_prototype + @property + def first_item_prototype(self): + return self._get_attr("firstItemPrototype") + + @first_item_prototype.setter + @beartype + def first_item_prototype(self, value: Optional[bool]): + self._set_attr("firstItemPrototype", value) + # padding @property def padding(self): diff --git a/sdk/python/flet/page.py b/sdk/python/flet/page.py index fdeff07ee..dc5a83121 100644 --- a/sdk/python/flet/page.py +++ b/sdk/python/flet/page.py @@ -445,6 +445,16 @@ def scroll(self, value: ScrollMode): value = "none" self._set_attr("scroll", value) + # auto_scroll + @property + def auto_scroll(self): + return self._get_attr("autoScroll") + + @auto_scroll.setter + @beartype + def auto_scroll(self, value: Optional[bool]): + self._set_attr("autoScroll", value) + # window_width @property def window_width(self): diff --git a/sdk/python/flet/row.py b/sdk/python/flet/row.py index 624174b1c..e2f24160d 100644 --- a/sdk/python/flet/row.py +++ b/sdk/python/flet/row.py @@ -35,6 +35,7 @@ def __init__( wrap: bool = None, run_spacing: OptionalNumber = None, scroll: ScrollMode = None, + auto_scroll: bool = None, ): ConstrainedControl.__init__( self, @@ -58,6 +59,7 @@ def __init__( self.run_spacing = run_spacing self.__scroll = False self.scroll = scroll + self.auto_scroll = auto_scroll def _get_control_name(self): return "row" @@ -144,6 +146,16 @@ def scroll(self, value: ScrollMode): value = "none" self._set_attr("scroll", value) + # auto_scroll + @property + def auto_scroll(self): + return self._get_attr("autoScroll") + + @auto_scroll.setter + @beartype + def auto_scroll(self, value: Optional[bool]): + self._set_attr("autoScroll", value) + # controls @property def controls(self):