diff --git a/CHANGELOG.md b/CHANGELOG.md index 52be73a2..9c262c7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.15.0 + +- **FIX**: `ShadSelect` crash when using `optionsBuilder`. +- **FEAT**: Add `itemCount` and `shrinkWrap` to `ShadSelect` and `ShadSelectFormField`. + ## 0.14.1 - **FIX**: `ShadApp` scroll behavior. diff --git a/lib/src/components/form/fields/select.dart b/lib/src/components/form/fields/select.dart index 882ddf44..63222b65 100644 --- a/lib/src/components/form/fields/select.dart +++ b/lib/src/components/form/fields/select.dart @@ -53,6 +53,12 @@ class ShadSelectFormField extends ShadFormBuilderField { Widget? footer, bool allowDeselection = false, bool closeOnSelect = true, + + /// {@macro ShadSelect.itemCount} + int? itemCount, + + /// {@macro ShadSelect.shrinkWrap} + bool? shrinkWrap, }) : super( decorationBuilder: (context) => (ShadTheme.of(context).selectTheme.decoration ?? @@ -88,6 +94,8 @@ class ShadSelectFormField extends ShadFormBuilderField { header: header, footer: footer, closeOnSelect: closeOnSelect, + itemCount: itemCount, + shrinkWrap: shrinkWrap, ); }, ); @@ -143,6 +151,12 @@ class ShadSelectFormField extends ShadFormBuilderField { Widget? footer, bool allowDeselection = false, bool closeOnSelect = true, + + /// {@macro ShadSelect.itemCount} + int? itemCount, + + /// {@macro ShadSelect.shrinkWrap} + bool? shrinkWrap, }) : super( decorationBuilder: (context) => (ShadTheme.of(context).selectTheme.decoration ?? @@ -185,6 +199,8 @@ class ShadSelectFormField extends ShadFormBuilderField { header: header, footer: footer, closeOnSelect: closeOnSelect, + itemCount: itemCount, + shrinkWrap: shrinkWrap, ); }, ); @@ -237,6 +253,12 @@ class ShadSelectFormField extends ShadFormBuilderField { Widget? footer, bool allowDeselection = false, bool closeOnSelect = true, + + /// {@macro ShadSelect.itemCount} + int? itemCount, + + /// {@macro ShadSelect.shrinkWrap} + bool? shrinkWrap, }) : assert( variant == ShadSelectVariant.primary || onSearchChanged != null, 'onSearchChanged must be provided when variant is search', @@ -287,6 +309,8 @@ class ShadSelectFormField extends ShadFormBuilderField { footer: footer, allowDeselection: allowDeselection, closeOnSelect: closeOnSelect, + itemCount: itemCount, + shrinkWrap: shrinkWrap, ); }, ); diff --git a/lib/src/components/select.dart b/lib/src/components/select.dart index c23c768c..9b622ade 100644 --- a/lib/src/components/select.dart +++ b/lib/src/components/select.dart @@ -58,6 +58,8 @@ class ShadSelect extends StatefulWidget { this.footer, this.closeOnSelect = true, this.allowDeselection = false, + this.itemCount, + this.shrinkWrap, }) : variant = ShadSelectVariant.primary, onSearchChanged = null, searchDivider = null, @@ -114,6 +116,8 @@ class ShadSelect extends StatefulWidget { this.footer, this.closeOnSelect = true, this.allowDeselection = false, + this.itemCount, + this.shrinkWrap, }) : variant = ShadSelectVariant.search, selectedOptionsBuilder = null, onMultipleChanged = null, @@ -152,6 +156,8 @@ class ShadSelect extends StatefulWidget { this.footer, this.allowDeselection = true, this.closeOnSelect = true, + this.itemCount, + this.shrinkWrap, }) : variant = ShadSelectVariant.multiple, onSearchChanged = null, initialValue = null, @@ -206,6 +212,8 @@ class ShadSelect extends StatefulWidget { this.footer, this.allowDeselection = true, this.closeOnSelect = true, + this.itemCount, + this.shrinkWrap, }) : variant = ShadSelectVariant.multipleWithSearch, selectedOptionBuilder = null, onChanged = null, @@ -257,6 +265,8 @@ class ShadSelect extends StatefulWidget { this.footer, this.allowDeselection = false, this.closeOnSelect = true, + this.itemCount, + this.shrinkWrap, }) : assert( variant == ShadSelectVariant.primary || onSearchChanged != null, 'onSearchChanged must be provided when variant is search', @@ -418,6 +428,20 @@ class ShadSelect extends StatefulWidget { /// {@endtemplate} final bool closeOnSelect; + /// {@template ShadSelect.itemCount} + /// The number of items in the options, used in combination with + /// [optionsBuilder]. + /// {@endtemplate} + final int? itemCount; + + /// {@template ShadSelect.shrinkWrap} + /// Whether the options returned by [optionsBuilder] should shrink wrap, + /// defaults to `false`. + /// You may set it to `true` when the [itemCount] provided is small and you + /// want the content to be resized. + /// {@endtemplate} + final bool? shrinkWrap; + static ShadSelectState of(BuildContext context, {bool listen = true}) { return maybeOf(context, listen: listen)!; } @@ -660,7 +684,7 @@ class ShadSelectState extends State> { theme.selectTheme.optionsPadding ?? const EdgeInsets.all(4); - Widget? search = switch (widget.variant) { + final search = switch (widget.variant) { ShadSelectVariant.primary || ShadSelectVariant.multiple => null, ShadSelectVariant.search || ShadSelectVariant.multipleWithSearch => @@ -689,10 +713,6 @@ class ShadSelectState extends State> { ), }; - if (search != null && effectiveMaxWidth.isInfinite) { - search = search; - } - return CallbackShortcuts( bindings: { const SingleActivator(LogicalKeyboardKey.enter): controller.toggle, @@ -727,6 +747,8 @@ class ShadSelectState extends State> { effectiveChild = ListView.builder( padding: effectiveOptionsPadding, controller: scrollController, + itemCount: widget.itemCount, + shrinkWrap: widget.shrinkWrap ?? false, itemBuilder: (context, index) { return widget.optionsBuilder?.call(context, index); }, @@ -858,48 +880,53 @@ class ShadSelectState extends State> { } }); + Widget effectiveColumn = Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + if (search != null) + Flexible( + child: ConstrainedBox( + constraints: effectiveConstraints, + child: search, + ), + ), + if (widget.header != null) + Flexible( + child: ConstrainedBox( + constraints: effectiveConstraints, + child: widget.header, + ), + ), + if (scrollToTopChild != null) scrollToTopChild, + Flexible( + child: ConstrainedBox( + constraints: effectiveConstraints, + child: effectiveChild, + ), + ), + if (scrollToBottomChild != null) scrollToBottomChild, + if (widget.footer != null) + Flexible( + child: ConstrainedBox( + constraints: effectiveConstraints, + child: widget.footer, + ), + ), + ], + ); + + if (widget.optionsBuilder == null) { + effectiveColumn = IntrinsicWidth(child: effectiveColumn); + } + return ConstrainedBox( constraints: BoxConstraints( maxHeight: effectiveMaxHeight, minWidth: calculatedMinWidth, maxWidth: effectiveMaxWidth, ), - child: IntrinsicWidth( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (search != null) - Flexible( - child: ConstrainedBox( - constraints: effectiveConstraints, - child: search, - ), - ), - if (widget.header != null) - Flexible( - child: ConstrainedBox( - constraints: effectiveConstraints, - child: widget.header, - ), - ), - if (scrollToTopChild != null) scrollToTopChild, - Flexible( - child: ConstrainedBox( - constraints: effectiveConstraints, - child: effectiveChild, - ), - ), - if (scrollToBottomChild != null) scrollToBottomChild, - if (widget.footer != null) - Flexible( - child: ConstrainedBox( - constraints: effectiveConstraints, - child: widget.footer, - ), - ), - ], - ), - ), + child: effectiveColumn, ); }, child: select, diff --git a/pubspec.yaml b/pubspec.yaml index 89dbfae5..0117108d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: shadcn_ui description: shadcn-ui ported in Flutter. Awesome UI components for Flutter, fully customizable. -version: 0.14.1 +version: 0.15.0 homepage: https://flutter-shadcn-ui.mariuti.com repository: https://github.com/nank1ro/flutter-shadcn-ui documentation: https://flutter-shadcn-ui.mariuti.com