From 6e135020cbddcf7e448abdfadd7eaca127acff56 Mon Sep 17 00:00:00 2001 From: mikecoomber <58986130+mikecoomber@users.noreply.github.com> Date: Mon, 11 Nov 2024 14:55:50 +0000 Subject: [PATCH] feat(UX-1310): Added more customization options to ZetaListItem (#205) fix(UX-1309): Wrapped initials with FittedBox inside ZetaAvatar so that the text scales correctly with device text scaling --- .../lib/pages/components/avatar_example.dart | 12 ++ .../pages/components/list_item_example.dart | 8 ++ lib/src/components/avatars/avatar.dart | 30 +++-- .../list_item/dropdown_list_item.dart | 31 ++++- lib/src/components/list_item/list_item.dart | 106 +++++++++++++----- 5 files changed, 139 insertions(+), 48 deletions(-) diff --git a/example/lib/pages/components/avatar_example.dart b/example/lib/pages/components/avatar_example.dart index 990f3c68..ff3aaa90 100644 --- a/example/lib/pages/components/avatar_example.dart +++ b/example/lib/pages/components/avatar_example.dart @@ -18,6 +18,13 @@ class AvatarExample extends StatelessWidget { ); return ExampleScaffold( + actions: [ + ZetaAvatar.initials( + initials: 'W W', + size: ZetaAvatarSize.xxs, + backgroundColor: Zeta.of(context).colors.green, + ), + ], name: AvatarExample.name, child: SingleChildScrollView( scrollDirection: Axis.horizontal, @@ -25,6 +32,11 @@ class AvatarExample extends StatelessWidget { padding: EdgeInsets.all(Zeta.of(context).spacing.medium), child: Column( children: [ + ZetaAvatar.initials( + initials: 'WW', + size: ZetaAvatarSize.xxs, + backgroundColor: Zeta.of(context).colors.green, + ), Column( children: [ Text( diff --git a/example/lib/pages/components/list_item_example.dart b/example/lib/pages/components/list_item_example.dart index cc0cf1d4..7a271232 100644 --- a/example/lib/pages/components/list_item_example.dart +++ b/example/lib/pages/components/list_item_example.dart @@ -38,6 +38,14 @@ class _ListItemExampleState extends State { primaryText: 'List Item', secondaryText: 'Descriptor', )), + _buildListItem( + 'Custom Title', + ZetaListItem( + title: ZetaButton( + label: 'Custom Title Button', + onPressed: () {}, + ), + )), _buildListItem( 'Icon Left', ZetaListItem(primaryText: 'List Item', leading: ZetaIcon(ZetaIcons.star)), diff --git a/lib/src/components/avatars/avatar.dart b/lib/src/components/avatars/avatar.dart index 145d9e7c..7ca1443f 100644 --- a/lib/src/components/avatars/avatar.dart +++ b/lib/src/components/avatars/avatar.dart @@ -228,15 +228,17 @@ class ZetaAvatar extends ZetaStatelessWidget { final innerChild = image ?? (initials != null ? Center( - child: Text( - initials!, - style: initialTextStyle ?? - TextStyle( - fontSize: size.fontSize(context), - letterSpacing: Zeta.of(context).spacing.none, - color: backgroundColor?.onColor, - fontWeight: FontWeight.w500, - ), + child: FittedBox( + child: Text( + initials!, + style: initialTextStyle ?? + TextStyle( + fontSize: size.fontSize(context), + letterSpacing: Zeta.of(context).spacing.none, + color: backgroundColor?.onColor, + fontWeight: FontWeight.w500, + ), + ), ), ) : null); @@ -280,20 +282,14 @@ class ZetaAvatar extends ZetaStatelessWidget { border: Border.all(color: borderColor!, width: borderSize(context)), borderRadius: Zeta.of(context).radius.full, ), - child: ClipRRect( - borderRadius: Zeta.of(context).radius.full, - child: innerContent, - ), + child: innerContent, ) : DecoratedBox( decoration: BoxDecoration( borderRadius: Zeta.of(context).radius.full, color: backgroundColor ?? zetaColors.surfaceHover, ), - child: ClipRRect( - borderRadius: Zeta.of(context).radius.full, - child: innerContent, - ), + child: innerContent, ), ), ), diff --git a/lib/src/components/list_item/dropdown_list_item.dart b/lib/src/components/list_item/dropdown_list_item.dart index 2a922002..8b8d508e 100644 --- a/lib/src/components/list_item/dropdown_list_item.dart +++ b/lib/src/components/list_item/dropdown_list_item.dart @@ -15,24 +15,40 @@ class ZetaDropdownListItem extends ZetaStatefulWidget { const ZetaDropdownListItem({ super.key, super.rounded, - required this.primaryText, required this.items, + this.title, + this.primaryText, this.secondaryText, + this.primaryTextStyle, + this.secondaryTextStyle, this.expanded = false, this.leading, this.showDivider, this.semanticLabel, - }); + }) : assert( + (title != null && primaryText == null && secondaryText == null) || + (title == null && (primaryText != null || secondaryText != null)), + 'Provide one of either title or primaryText/secondaryText', + ); /// The list of [ZetaListItem]s contained within the dropdown. final List items; + /// {@macro list-item-title} + final Widget? title; + /// {@macro list-item-primary-text} - final String primaryText; + final String? primaryText; + + /// {@macro list-item-primary-text-style} + final TextStyle? primaryTextStyle; /// {@macro list-item-secondary-text} final String? secondaryText; + /// {@macro list-item-secondary-text-style} + final TextStyle? secondaryTextStyle; + /// {@macro list-item-leading} final Widget? leading; @@ -61,7 +77,9 @@ class ZetaDropdownListItem extends ZetaStatefulWidget { ..add(DiagnosticsProperty('expanded', expanded)) ..add(DiagnosticsProperty('rounded', rounded)) ..add(DiagnosticsProperty('showDivider', showDivider)) - ..add(StringProperty('semanticLabel', semanticLabel)); + ..add(StringProperty('semanticLabel', semanticLabel)) + ..add(DiagnosticsProperty('primaryTextStyle', primaryTextStyle)) + ..add(DiagnosticsProperty('secondaryTextStyle', secondaryTextStyle)); } } @@ -125,7 +143,7 @@ class _ZetaDropdownListItemState extends State with Single child: Semantics( button: true, selected: _expanded, - label: widget.semanticLabel ?? (widget.primaryText + (widget.secondaryText ?? '')), + label: widget.semanticLabel ?? ((widget.primaryText ?? '') + (widget.secondaryText ?? '')), // DecoratedBox does not correctly animated the border when the widget expands. // ignore: use_decorated_box child: Container( @@ -140,8 +158,11 @@ class _ZetaDropdownListItemState extends State with Single children: [ ExcludeSemantics( child: ZetaListItem( + title: widget.title, primaryText: widget.primaryText, + primaryTextStyle: widget.primaryTextStyle, secondaryText: widget.secondaryText, + secondaryTextStyle: widget.secondaryTextStyle, leading: widget.leading, onTap: _onTap, showDivider: false, diff --git a/lib/src/components/list_item/list_item.dart b/lib/src/components/list_item/list_item.dart index 01d69254..d5b14d02 100644 --- a/lib/src/components/list_item/list_item.dart +++ b/lib/src/components/list_item/list_item.dart @@ -61,22 +61,32 @@ class ZetaList extends ZetaStatelessWidget { class ZetaListItem extends ZetaStatelessWidget { /// Creates a [ZetaListItem]. const ZetaListItem({ - required this.primaryText, + this.title, + this.primaryText, this.secondaryText, + this.primaryTextStyle, + this.secondaryTextStyle, this.leading, this.onTap, this.showDivider, this.trailing, super.key, super.rounded, - }); + }) : assert( + (title != null && primaryText == null && secondaryText == null) || + (title == null && primaryText != null || secondaryText != null), + 'Provide one of either title or primaryText/secondaryText', + ); /// Creates a [ZetaListItem] with a [ZetaSwitch] in the trailing widget space. ZetaListItem.toggle({ super.key, super.rounded, - required this.primaryText, + this.title, + this.primaryText, this.secondaryText, + this.primaryTextStyle, + this.secondaryTextStyle, this.showDivider, this.leading, bool value = false, @@ -86,14 +96,22 @@ class ZetaListItem extends ZetaStatelessWidget { onChanged: onChanged, variant: ZetaSwitchType.android, ), - onTap = (() => onChanged?.call(!value)); + onTap = (() => onChanged?.call(!value)), + assert( + (title != null && primaryText == null && secondaryText == null) || + (title == null && (primaryText != null || secondaryText != null)), + 'Provide one of either title or primaryText/secondaryText', + ); /// Creates a [ZetaListItem] with a [ZetaCheckbox] in the trailing widget space. ZetaListItem.checkbox({ super.key, super.rounded, - required this.primaryText, + this.title, + this.primaryText, this.secondaryText, + this.primaryTextStyle, + this.secondaryTextStyle, this.leading, this.showDivider, bool value = false, @@ -105,13 +123,21 @@ class ZetaListItem extends ZetaStatelessWidget { useIndeterminate: useIndeterminate, rounded: rounded, ), - onTap = (() => onChanged?.call(!value)); + onTap = (() => onChanged?.call(!value)), + assert( + (title != null && primaryText == null && secondaryText == null) || + (title == null && (primaryText != null || secondaryText != null)), + 'Provide one of either title or primaryText/secondaryText', + ); /// Creates a [ZetaListItem] with a [ZetaRadio] in the trailing widget space. ZetaListItem.radio({ - required this.primaryText, required dynamic value, + this.title, + this.primaryText, this.secondaryText, + this.primaryTextStyle, + this.secondaryTextStyle, this.leading, this.showDivider, dynamic groupValue, @@ -123,7 +149,12 @@ class ZetaListItem extends ZetaStatelessWidget { groupValue: groupValue, onChanged: onChanged, ), - onTap = (() => onChanged?.call(value)); + onTap = (() => onChanged?.call(value)), + assert( + (title != null && primaryText == null && secondaryText == null) || + (title == null && (primaryText != null || secondaryText != null)), + 'Provide one of either title or primaryText/secondaryText', + ); /// {@template list-item-leading} /// A widget to display before the title; @@ -133,16 +164,34 @@ class ZetaListItem extends ZetaStatelessWidget { /// Called when user taps on the [ZetaListItem]. final VoidCallback? onTap; + /// {@template list-item-title} + /// The primary content of the list item. + /// Cannot be provided with [primaryText] or [secondaryText]. + /// {@endtemplate} + final Widget? title; + /// {@template list-item-primary-text} /// The primary text of the [ZetaListItem]. + /// Cannot be provided with [title]. + /// {@endtemplate} + final String? primaryText; + + /// {@template list-item-primary-text-style} + /// The text style applied to [primaryText]. /// {@endtemplate} - final String primaryText; + final TextStyle? primaryTextStyle; /// {@template list-item-secondary-text} /// The secondary text of the [ZetaListItem]. + /// Cannot be provided with [title]. /// {@endtemplate} final String? secondaryText; + /// {@template list-item-secondary-text-style} + /// The text style applied to [secondaryText]. + /// {@endtemplate} + final TextStyle? secondaryTextStyle; + /// A widget to display after the primary text. /// If this is a checkbox, radio button, or switch, use the relevant named constructor. final Widget? trailing; @@ -162,7 +211,9 @@ class ZetaListItem extends ZetaStatelessWidget { ..add(DiagnosticsProperty('trailing', trailing)) ..add(StringProperty('label', primaryText)) ..add(StringProperty('secondaryText', secondaryText)) - ..add(DiagnosticsProperty('showDivider', showDivider)); + ..add(DiagnosticsProperty('showDivider', showDivider)) + ..add(DiagnosticsProperty('secondaryTextStyle', secondaryTextStyle)) + ..add(DiagnosticsProperty('primaryTextStyle', primaryTextStyle)); } @override @@ -209,23 +260,26 @@ class ZetaListItem extends ZetaStatelessWidget { child: leadingWidget, ), Flexible( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - primaryText, - maxLines: 1, - overflow: TextOverflow.ellipsis, + child: title ?? + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (primaryText != null && primaryText!.isNotEmpty) + Text( + primaryText!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: primaryTextStyle, + ), + if (secondaryText != null && secondaryText!.isNotEmpty) + Text( + secondaryText!, + style: secondaryTextStyle ?? ZetaTextStyles.bodySmall, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], ), - if (secondaryText != null && secondaryText!.isNotEmpty) - Text( - secondaryText!, - style: ZetaTextStyles.bodySmall, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ], - ), ), ], ),