From 1011782826a5c00cb046f902ed5a24a38164720e Mon Sep 17 00:00:00 2001 From: atanasyordanov21 <63714308+atanasyordanov21@users.noreply.github.com> Date: Fri, 26 Apr 2024 11:36:57 +0300 Subject: [PATCH 1/7] feat: Select input (#35) * create ZetaSelectInput * add extra parameters; improve * leading icon; error handling in example * _MenuPosition * widgetbook * restore all menu items on each open * fix setState * initialize _menuSize with Size.zero --- example/lib/home.dart | 2 + .../components/select_input_example.dart | 113 +++ example/widgetbook/main.dart | 2 + .../components/select_input_widgetbook.dart | 99 +++ .../components/select_input/select_input.dart | 649 ++++++++++++++++++ lib/zeta_flutter.dart | 1 + 6 files changed, 866 insertions(+) create mode 100644 example/lib/pages/components/select_input_example.dart create mode 100644 example/widgetbook/pages/components/select_input_widgetbook.dart create mode 100644 lib/src/components/select_input/select_input.dart diff --git a/example/lib/home.dart b/example/lib/home.dart index a0b93d43..3a55cec8 100644 --- a/example/lib/home.dart +++ b/example/lib/home.dart @@ -19,6 +19,7 @@ import 'package:zeta_example/pages/components/navigation_bar_example.dart'; import 'package:zeta_example/pages/components/navigation_rail_example.dart'; import 'package:zeta_example/pages/components/phone_input_example.dart'; import 'package:zeta_example/pages/components/radio_example.dart'; +import 'package:zeta_example/pages/components/select_input_example.dart'; import 'package:zeta_example/pages/components/search_bar_example.dart'; import 'package:zeta_example/pages/components/segmented_control_example.dart'; import 'package:zeta_example/pages/components/stepper_example.dart'; @@ -74,6 +75,7 @@ final List components = [ Component(SearchBarExample.name, (context) => const SearchBarExample()), Component(TooltipExample.name, (context) => const TooltipExample()), Component(NavigationRailExample.name, (context) => const NavigationRailExample()), + Component(SelectInputExample.name, (context) => const SelectInputExample()), ]; final List theme = [ diff --git a/example/lib/pages/components/select_input_example.dart b/example/lib/pages/components/select_input_example.dart new file mode 100644 index 00000000..d8f85c81 --- /dev/null +++ b/example/lib/pages/components/select_input_example.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:zeta_example/widgets.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +class SelectInputExample extends StatefulWidget { + static const String name = 'SelectInput'; + const SelectInputExample({super.key}); + + @override + State createState() => _SelectInputExampleState(); +} + +class _SelectInputExampleState extends State { + String? _errorText; + ZetaSelectInputItem? selectedItem = ZetaSelectInputItem( + value: 'Item 1', + ); + + @override + Widget build(BuildContext context) { + final zeta = Zeta.of(context); + + return ExampleScaffold( + name: 'Select Input', + child: Center( + child: SingleChildScrollView( + child: SizedBox( + width: 320, + child: Column( + children: [ + ZetaSelectInput( + label: Row( + children: [ + Text('Label'), + Padding( + padding: const EdgeInsets.only(left: 6), + child: Text( + '*', + style: TextStyle(color: zeta.colors.red.shade60), + ), + ), + ], + ), + hint: 'Default hint text', + leadingIcon: Icon(ZetaIcons.star_round), + hasError: _errorText != null, + errorText: _errorText, + onChanged: (item) { + setState(() { + selectedItem = item; + if (item != null) { + _errorText = null; + } + }); + }, + onTextChanged: (value) { + setState(() { + if (value.isEmpty) { + _errorText = 'Required'; + } else { + _errorText = null; + } + }); + }, + selectedItem: selectedItem, + items: [ + ZetaSelectInputItem( + value: 'Item 1', + ), + ZetaSelectInputItem( + value: 'Item 2', + ), + ZetaSelectInputItem( + value: 'Item 3', + ), + ZetaSelectInputItem( + value: 'Item 4', + ), + ZetaSelectInputItem( + value: 'Item 5', + ), + ZetaSelectInputItem( + value: 'Item 6', + ), + ZetaSelectInputItem( + value: 'Item 7', + ), + ZetaSelectInputItem( + value: 'Item 8', + ), + ZetaSelectInputItem( + value: 'Item 9', + ), + ZetaSelectInputItem( + value: 'Item 10', + ), + ZetaSelectInputItem( + value: 'Item 11', + ), + ZetaSelectInputItem( + value: 'Item 12', + ), + ], + ), + const SizedBox(height: 120), + ], + ), + ), + ), + ), + ); + } +} diff --git a/example/widgetbook/main.dart b/example/widgetbook/main.dart index 0a7ffcce..c8882d05 100644 --- a/example/widgetbook/main.dart +++ b/example/widgetbook/main.dart @@ -28,6 +28,7 @@ import 'pages/components/progress_widgetbook.dart'; import 'pages/components/radio_widgetbook.dart'; import 'pages/components/search_bar_widgetbook.dart'; import 'pages/components/segmented_control_widgetbook.dart'; +import 'pages/components/select_input_widgetbook.dart'; import 'pages/components/stepper_widgetbook.dart'; import 'pages/components/switch_widgetbook.dart'; import 'pages/components/snack_bar_widgetbook.dart'; @@ -137,6 +138,7 @@ class HotReload extends StatelessWidget { WidgetbookUseCase(name: 'Search Bar', builder: (context) => searchBarUseCase(context)), WidgetbookUseCase(name: 'Navigation Rail', builder: (context) => navigationRailUseCase(context)), WidgetbookUseCase(name: 'Tooltip', builder: (context) => tooltipUseCase(context)), + WidgetbookUseCase(name: 'Select Input', builder: (context) => selectInputUseCase(context)), ]..sort((a, b) => a.name.compareTo(b.name)), ), WidgetbookCategory( diff --git a/example/widgetbook/pages/components/select_input_widgetbook.dart b/example/widgetbook/pages/components/select_input_widgetbook.dart new file mode 100644 index 00000000..da2ff862 --- /dev/null +++ b/example/widgetbook/pages/components/select_input_widgetbook.dart @@ -0,0 +1,99 @@ +import 'package:flutter/material.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +import '../../test/test_components.dart'; +import '../../utils/utils.dart'; + +Widget selectInputUseCase(BuildContext context) { + final zeta = Zeta.of(context); + final items = [ + ZetaSelectInputItem(value: 'Item 1'), + ZetaSelectInputItem(value: 'Item 2'), + ZetaSelectInputItem(value: 'Item 3'), + ZetaSelectInputItem(value: 'Item 4'), + ZetaSelectInputItem(value: 'Item 5'), + ZetaSelectInputItem(value: 'Item 6'), + ZetaSelectInputItem(value: 'Item 7'), + ZetaSelectInputItem(value: 'Item 8'), + ZetaSelectInputItem(value: 'Item 9'), + ZetaSelectInputItem(value: 'Item 10'), + ZetaSelectInputItem(value: 'Item 11'), + ZetaSelectInputItem(value: 'Item 12'), + ]; + late ZetaSelectInputItem? selectedItem = items.first; + String? _errorText; + final label = context.knobs.string( + label: 'Label', + initialValue: 'Label', + ); + final hint = context.knobs.string( + label: 'Hint', + initialValue: 'Default hint text', + ); + final rounded = context.knobs.boolean(label: 'Rounded', initialValue: true); + final enabled = context.knobs.boolean(label: 'Enabled', initialValue: true); + final required = context.knobs.boolean(label: 'Required', initialValue: true); + final size = context.knobs.list( + label: 'Size', + options: ZetaWidgetSize.values, + labelBuilder: (size) => size.name, + ); + final iconData = iconKnob( + context, + name: "Icon", + rounded: rounded, + initial: rounded ? ZetaIcons.star_round : ZetaIcons.star_sharp, + ); + + return WidgetbookTestWidget( + widget: StatefulBuilder( + builder: (context, setState) { + return Padding( + padding: const EdgeInsets.all(ZetaSpacing.m), + child: ZetaSelectInput( + rounded: rounded, + enabled: enabled, + size: size, + label: Row( + children: [ + Text(label), + if (required) + Padding( + padding: const EdgeInsets.only(left: 6), + child: Text( + '*', + style: TextStyle(color: zeta.colors.red.shade60), + ), + ), + ], + ), + hint: hint, + leadingIcon: Icon(iconData), + hasError: _errorText != null, + errorText: _errorText, + onChanged: (item) { + setState(() { + selectedItem = item; + if (item != null) { + _errorText = null; + } + }); + }, + onTextChanged: (value) { + setState(() { + if (required && value.isEmpty) { + _errorText = 'Required'; + } else { + _errorText = null; + } + }); + }, + selectedItem: selectedItem, + items: items, + ), + ); + }, + ), + ); +} diff --git a/lib/src/components/select_input/select_input.dart b/lib/src/components/select_input/select_input.dart new file mode 100644 index 00000000..1b409290 --- /dev/null +++ b/lib/src/components/select_input/select_input.dart @@ -0,0 +1,649 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +import '../../../zeta_flutter.dart'; + +enum _MenuPosition { top, bottom } + +/// Class for [ZetaSelectInput] +class ZetaSelectInput extends StatefulWidget { + ///Constructor of [ZetaSelectInput] + const ZetaSelectInput({ + super.key, + required this.items, + this.onChanged, + this.onTextChanged, + this.selectedItem, + this.size, + this.leadingIcon, + this.label, + this.hint, + this.enabled = true, + this.rounded = true, + this.hasError = false, + this.errorText, + }); + + /// Input items as list of [ZetaSelectInputItem] + final List items; + + /// Currently selected item + final ZetaSelectInputItem? selectedItem; + + /// Handles changes of select menu + final ValueSetter? onChanged; + + /// Handles changes of input text + final ValueSetter? onTextChanged; + + /// Determines the size of the input field. + /// Default is `ZetaDateInputSize.large` + final ZetaWidgetSize? size; + + /// The input's leading icon. + final Widget? leadingIcon; + + /// If provided, displays a label above the input field. + final Widget? label; + + /// If provided, displays a hint below the input field. + final String? hint; + + /// Determines if the input field should be enabled (default) or disabled. + final bool enabled; + + /// Determines if the input field should be displayed in error style. + /// Default is `false`. + /// If `enabled` is `false`, this has no effect. + final bool hasError; + + /// In combination with `hasError: true`, provides the error message + /// to be displayed below the input field. + final String? errorText; + + /// {@macro zeta-component-rounded} + final bool rounded; + + @override + State createState() => _ZetaSelectInputState(); + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('rounded', rounded)) + ..add( + ObjectFlagProperty?>.has( + 'onChanged', + onChanged, + ), + ) + ..add(EnumProperty('size', size)) + ..add(StringProperty('hint', hint)) + ..add(DiagnosticsProperty('enabled', enabled)) + ..add(DiagnosticsProperty('hasError', hasError)) + ..add(StringProperty('errorText', errorText)) + ..add(ObjectFlagProperty?>.has('onTextChanged', onTextChanged)); + } +} + +class _ZetaSelectInputState extends State { + final OverlayPortalController _overlayController = OverlayPortalController(); + final _link = LayerLink(); + late String? _selectedValue; + late List _menuItems; + Size _menuSize = Size.zero; + _MenuPosition? _menuPosition = _MenuPosition.bottom; + + @override + void initState() { + super.initState(); + _selectedValue = widget.selectedItem?.value; + _menuItems = List.from(widget.items); + } + + @override + Widget build(BuildContext context) { + return CompositedTransformTarget( + link: _link, + child: OverlayPortal( + controller: _overlayController, + overlayChildBuilder: (BuildContext context) { + return CompositedTransformFollower( + link: _link, + targetAnchor: _menuPosition == _MenuPosition.top ? Alignment.topLeft : Alignment.bottomLeft, + followerAnchor: _menuPosition == _MenuPosition.top ? Alignment.bottomLeft : Alignment.topLeft, + child: Align( + alignment: _menuPosition == _MenuPosition.top ? Alignment.bottomLeft : Alignment.topLeft, + child: _ZetaSelectInputMenu( + size: _menuSize, + itemSize: widget.size, + items: _menuItems, + selectedValue: _selectedValue, + onSelected: (item) { + if (item != null) { + _selectedValue = item.value; + widget.onChanged?.call(item); + } + _overlayController.hide(); + }, + rounded: widget.rounded, + ), + ), + ); + }, + child: _InputComponent( + size: widget.size, + label: widget.label, + hint: widget.hint, + leadingIcon: widget.leadingIcon, + enabled: widget.enabled, + rounded: widget.rounded, + hasError: widget.hasError, + errorText: widget.errorText, + initialValue: _selectedValue, + onToggleMenu: widget.items.isEmpty + ? null + : () { + if (_overlayController.isShowing) { + _overlayController.hide(); + return setState(() {}); + } + final box = context.findRenderObject() as RenderBox?; + final offset = box?.size.topLeft( + box.localToGlobal(Offset.zero), + ); + final upperHeight = offset?.dy ?? 0; + final lowerHeight = MediaQuery.of(context).size.height - upperHeight - (box?.size.height ?? 0); + setState(() { + _menuPosition = upperHeight > lowerHeight ? _MenuPosition.top : _MenuPosition.bottom; + _menuSize = Size( + box?.size.width ?? (MediaQuery.of(context).size.width - ZetaSpacing.x10), + (upperHeight > lowerHeight ? upperHeight : lowerHeight) - ZetaSpacing.m, + ); + _menuItems = List.from(widget.items); + }); + _overlayController.show(); + }, + menuIsShowing: _overlayController.isShowing, + onChanged: (value) { + widget.onTextChanged?.call(value); + _selectedValue = value; + _menuItems = widget.items + .where( + (item) => item.value.toLowerCase().contains(value.toLowerCase()), + ) + .toList(); + final item = widget.items.firstWhereOrNull( + (item) => item.value.toLowerCase() == value.toLowerCase(), + ); + widget.onChanged?.call(item); + setState(() {}); + }, + ), + ), + ); + } +} + +class _InputComponent extends StatefulWidget { + const _InputComponent({ + this.size, + this.label, + this.hint, + this.leadingIcon, + this.enabled = true, + this.rounded = true, + this.hasError = false, + this.errorText, + this.initialValue, + this.onChanged, + this.onToggleMenu, + this.menuIsShowing = false, + }); + + final ZetaWidgetSize? size; + final Widget? label; + final String? hint; + final Widget? leadingIcon; + final bool enabled; + final bool rounded; + final bool hasError; + final String? errorText; + final String? initialValue; + final void Function(String)? onChanged; + final VoidCallback? onToggleMenu; + final bool menuIsShowing; + + @override + State<_InputComponent> createState() => _InputComponentState(); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(EnumProperty('size', size)) + ..add(StringProperty('hint', hint)) + ..add(DiagnosticsProperty('enabled', enabled)) + ..add(DiagnosticsProperty('rounded', rounded)) + ..add(DiagnosticsProperty('hasError', hasError)) + ..add(StringProperty('errorText', errorText)) + ..add(ObjectFlagProperty.has('onChanged', onChanged)) + ..add(ObjectFlagProperty.has('onToggleMenu', onToggleMenu)) + ..add(DiagnosticsProperty('menuIsShowing', menuIsShowing)) + ..add(StringProperty('initialValue', initialValue)); + } +} + +class _InputComponentState extends State<_InputComponent> { + final _controller = TextEditingController(); + late ZetaWidgetSize _size; + bool _hasError = false; + + @override + void initState() { + super.initState(); + _setParams(); + } + + @override + void didUpdateWidget(_InputComponent oldWidget) { + super.didUpdateWidget(oldWidget); + _setParams(); + } + + void _setParams() { + _controller.text = widget.initialValue ?? ''; + _size = widget.size ?? ZetaWidgetSize.large; + _hasError = widget.hasError; + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final zeta = Zeta.of(context); + final showError = _hasError && widget.errorText != null; + final hintErrorColor = widget.enabled + ? showError + ? zeta.colors.red + : zeta.colors.cool.shade70 + : zeta.colors.cool.shade50; + final iconSize = _iconSize(_size); + final inputVerticalPadding = _inputVerticalPadding(_size); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.label != null) + Padding( + padding: const EdgeInsets.only(bottom: 5), + child: DefaultTextStyle( + style: ZetaTextStyles.bodyMedium.copyWith( + color: widget.enabled ? zeta.colors.textDefault : zeta.colors.cool.shade50, + ), + child: widget.label!, + ), + ), + TextFormField( + enabled: widget.enabled, + controller: _controller, + onChanged: widget.onChanged, + style: _size == ZetaWidgetSize.small ? ZetaTextStyles.bodyXSmall : ZetaTextStyles.bodyMedium, + decoration: InputDecoration( + isDense: true, + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: inputVerticalPadding, + ), + prefixIcon: widget.leadingIcon == null + ? null + : Padding( + padding: const EdgeInsets.only(left: ZetaSpacing.x2_5, right: ZetaSpacing.xs), + child: IconTheme( + data: IconThemeData( + color: widget.enabled ? zeta.colors.cool.shade70 : zeta.colors.cool.shade50, + size: iconSize, + ), + child: widget.leadingIcon!, + ), + ), + prefixIconConstraints: const BoxConstraints( + minHeight: ZetaSpacing.m, + minWidth: ZetaSpacing.m, + ), + suffixIcon: widget.onToggleMenu == null + ? null + : Padding( + padding: const EdgeInsets.only(right: ZetaSpacing.xxs), + child: IconButton( + visualDensity: const VisualDensity( + horizontal: -4, + vertical: -4, + ), + onPressed: widget.onToggleMenu, + icon: Icon( + widget.menuIsShowing + ? (widget.rounded ? ZetaIcons.expand_less_round : ZetaIcons.expand_less_sharp) + : (widget.rounded ? ZetaIcons.expand_more_round : ZetaIcons.expand_more_sharp), + color: widget.enabled ? zeta.colors.textDefault : zeta.colors.cool.shade50, + size: iconSize, + ), + ), + ), + suffixIconConstraints: const BoxConstraints( + minHeight: ZetaSpacing.m, + minWidth: ZetaSpacing.m, + ), + hintStyle: _size == ZetaWidgetSize.small + ? ZetaTextStyles.bodyXSmall.copyWith( + color: widget.enabled ? zeta.colors.textDefault : zeta.colors.cool.shade50, + ) + : ZetaTextStyles.bodyMedium.copyWith( + color: widget.enabled ? zeta.colors.textDefault : zeta.colors.cool.shade50, + ), + filled: !widget.enabled || _hasError ? true : null, + fillColor: widget.enabled + ? _hasError + ? zeta.colors.red.shade10 + : null + : zeta.colors.cool.shade30, + enabledBorder: _hasError + ? _errorInputBorder(zeta, rounded: widget.rounded) + : _defaultInputBorder(zeta, rounded: widget.rounded), + focusedBorder: _hasError + ? _errorInputBorder(zeta, rounded: widget.rounded) + : _focusedInputBorder(zeta, rounded: widget.rounded), + disabledBorder: _defaultInputBorder(zeta, rounded: widget.rounded), + errorBorder: _errorInputBorder(zeta, rounded: widget.rounded), + focusedErrorBorder: _errorInputBorder(zeta, rounded: widget.rounded), + ), + ), + if (widget.hint != null || showError) + Padding( + padding: const EdgeInsets.only(top: 5), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(right: 8), + child: Icon( + showError && widget.enabled + ? (widget.rounded ? ZetaIcons.error_round : ZetaIcons.error_sharp) + : (widget.rounded ? ZetaIcons.info_round : ZetaIcons.info_sharp), + size: ZetaSpacing.b, + color: hintErrorColor, + ), + ), + Expanded( + child: Text( + showError && widget.enabled ? widget.errorText! : widget.hint!, + style: ZetaTextStyles.bodyXSmall.copyWith( + color: hintErrorColor, + ), + ), + ), + ], + ), + ), + ], + ); + } + + double _inputVerticalPadding(ZetaWidgetSize size) => switch (size) { + ZetaWidgetSize.large => ZetaSpacing.x3, + ZetaWidgetSize.medium => ZetaSpacing.x2, + ZetaWidgetSize.small => ZetaSpacing.x2, + }; + + double _iconSize(ZetaWidgetSize size) => switch (size) { + ZetaWidgetSize.large => ZetaSpacing.x5, + ZetaWidgetSize.medium => ZetaSpacing.x5, + ZetaWidgetSize.small => ZetaSpacing.x4, + }; + + OutlineInputBorder _defaultInputBorder( + Zeta zeta, { + required bool rounded, + }) => + OutlineInputBorder( + borderRadius: rounded ? ZetaRadius.minimal : ZetaRadius.none, + borderSide: BorderSide(color: zeta.colors.cool.shade40), + ); + + OutlineInputBorder _focusedInputBorder( + Zeta zeta, { + required bool rounded, + }) => + OutlineInputBorder( + borderRadius: rounded ? ZetaRadius.minimal : ZetaRadius.none, + borderSide: BorderSide(color: zeta.colors.blue.shade50), + ); + + OutlineInputBorder _errorInputBorder( + Zeta zeta, { + required bool rounded, + }) => + OutlineInputBorder( + borderRadius: rounded ? ZetaRadius.minimal : ZetaRadius.none, + borderSide: BorderSide(color: zeta.colors.red.shade50), + ); +} + +/// Class for [ZetaSelectInputItem] +class ZetaSelectInputItem extends StatelessWidget { + ///Public constructor for [ZetaSelectInputItem] + const ZetaSelectInputItem({ + super.key, + required this.value, + this.size = ZetaWidgetSize.large, + }) : rounded = true, + selected = false, + onPressed = null; + + const ZetaSelectInputItem._({ + super.key, + required this.rounded, + required this.selected, + required this.value, + this.onPressed, + this.size = ZetaWidgetSize.large, + }); + + /// {@macro zeta-component-rounded} + final bool rounded; + + /// If [ZetaSelectInputItem] is selected + final bool selected; + + /// Value of [ZetaSelectInputItem] + final String value; + + /// Handles clicking for [ZetaSelectInputItem] + final VoidCallback? onPressed; + + /// The size of [ZetaSelectInputItem] + final ZetaWidgetSize size; + + /// Returns copy of [ZetaSelectInputItem] with those private variables included + ZetaSelectInputItem copyWith({ + bool? rounded, + bool? selected, + VoidCallback? onPressed, + ZetaWidgetSize? size, + }) { + return ZetaSelectInputItem._( + rounded: rounded ?? this.rounded, + selected: selected ?? this.selected, + onPressed: onPressed ?? this.onPressed, + size: size ?? this.size, + value: value, + key: key, + ); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('rounded', rounded)) + ..add(DiagnosticsProperty('selected', selected)) + ..add(StringProperty('value', value)) + ..add(ObjectFlagProperty.has('onPressed', onPressed)) + ..add(EnumProperty('size', size)); + } + + @override + Widget build(BuildContext context) { + final colors = Zeta.of(context).colors; + + return DefaultTextStyle( + style: ZetaTextStyles.bodyMedium, + child: OutlinedButton( + onPressed: onPressed, + style: _getStyle(colors, size), + child: Text(value), + ), + ); + } + + ButtonStyle _getStyle(ZetaColors colors, ZetaWidgetSize size) { + final visualDensity = switch (size) { + ZetaWidgetSize.large => 0.0, + ZetaWidgetSize.medium => -2.0, + ZetaWidgetSize.small => -4.0, + }; + return ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.hovered)) { + return colors.surfaceHovered; + } + + if (states.contains(MaterialState.pressed)) { + return colors.surfaceSelected; + } + + if (states.contains(MaterialState.disabled) || onPressed == null) { + return colors.surfaceDisabled; + } + return colors.surfacePrimary; + }), + foregroundColor: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.disabled)) { + return colors.textDisabled; + } + return colors.textDefault; + }), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: rounded ? ZetaRadius.minimal : ZetaRadius.none, + ), + ), + side: MaterialStatePropertyAll( + selected ? BorderSide(color: colors.primary.shade60) : BorderSide.none, + ), + padding: const MaterialStatePropertyAll( + EdgeInsets.symmetric(horizontal: ZetaSpacing.b), + ), + elevation: const MaterialStatePropertyAll(0), + overlayColor: const MaterialStatePropertyAll(Colors.transparent), + textStyle: MaterialStatePropertyAll( + size == ZetaWidgetSize.small ? ZetaTextStyles.bodyXSmall : ZetaTextStyles.bodyMedium, + ), + minimumSize: const MaterialStatePropertyAll(Size.fromHeight(48)), + alignment: Alignment.centerLeft, + visualDensity: VisualDensity( + horizontal: visualDensity, + vertical: visualDensity, + ), + ); + } +} + +class _ZetaSelectInputMenu extends StatelessWidget { + const _ZetaSelectInputMenu({ + required this.items, + required this.onSelected, + required this.size, + this.selectedValue, + this.rounded = true, + this.itemSize, + }); + + /// Input items for the menu + final List items; + + /// Handles selecting an item from the menu + final ValueSetter onSelected; + + /// The value of the currently selected item + final String? selectedValue; + + /// The size of the menu. + final Size size; + + /// {@macro zeta-component-rounded} + final bool rounded; + + /// The size of [ZetaSelectInputItem] + final ZetaWidgetSize? itemSize; + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add( + ObjectFlagProperty>.has( + 'onSelected', + onSelected, + ), + ) + ..add(DiagnosticsProperty('rounded', rounded)) + ..add(StringProperty('selectedValue', selectedValue)) + ..add(DiagnosticsProperty('size', size)) + ..add(EnumProperty('itemSize', itemSize)); + } + + @override + Widget build(BuildContext context) { + final colors = Zeta.of(context).colors; + return ConstrainedBox( + constraints: BoxConstraints( + maxWidth: size.width, + maxHeight: size.height, + ), + child: DecoratedBox( + decoration: BoxDecoration( + color: colors.surfacePrimary, + borderRadius: rounded ? ZetaRadius.minimal : ZetaRadius.none, + boxShadow: const [ + BoxShadow(blurRadius: 2, color: Color.fromRGBO(40, 51, 61, 0.04)), + BoxShadow( + blurRadius: 8, + color: Color.fromRGBO(96, 104, 112, 0.16), + blurStyle: BlurStyle.outer, + offset: Offset(0, 4), + ), + ], + ), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: items.map((item) { + return item.copyWith( + rounded: rounded, + selected: selectedValue?.toLowerCase() == item.value.toLowerCase(), + onPressed: () => onSelected(item), + size: itemSize, + ); + }).toList(), + ), + ), + ), + ); + } +} diff --git a/lib/zeta_flutter.dart b/lib/zeta_flutter.dart index f56ab17e..dcb83bb9 100644 --- a/lib/zeta_flutter.dart +++ b/lib/zeta_flutter.dart @@ -38,6 +38,7 @@ export 'src/components/progress/progress_circle.dart'; export 'src/components/radio/radio.dart'; export 'src/components/search_bar/search_bar.dart'; export 'src/components/segmented_control/segmented_control.dart'; +export 'src/components/select_input/select_input.dart'; export 'src/components/snack_bar/snack_bar.dart'; export 'src/components/stepper/stepper.dart'; export 'src/components/switch/zeta_switch.dart'; From 51b355291346f4650ce9c69800ef6b1febac074e Mon Sep 17 00:00:00 2001 From: atanasyordanov21 <63714308+atanasyordanov21@users.noreply.github.com> Date: Mon, 29 Apr 2024 17:53:40 +0300 Subject: [PATCH 2/7] feat: Filter Selection (#36) * Filter Selection * use divide --- example/lib/home.dart | 2 + .../components/filter_selection_example.dart | 54 +++++++++++++++++ example/widgetbook/main.dart | 2 + .../filter_selection_widgetbook.dart | 37 ++++++++++++ lib/src/components/chips/chip.dart | 5 +- lib/src/components/chips/filter_chip.dart | 13 ++++ .../filter_selection/filter_selection.dart | 60 +++++++++++++++++++ lib/zeta_flutter.dart | 1 + 8 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 example/lib/pages/components/filter_selection_example.dart create mode 100644 example/widgetbook/pages/components/filter_selection_widgetbook.dart create mode 100644 lib/src/components/filter_selection/filter_selection.dart diff --git a/example/lib/home.dart b/example/lib/home.dart index 3a55cec8..5e7db604 100644 --- a/example/lib/home.dart +++ b/example/lib/home.dart @@ -14,6 +14,7 @@ import 'package:zeta_example/pages/components/date_input_example.dart'; import 'package:zeta_example/pages/components/dialog_example.dart'; import 'package:zeta_example/pages/components/dialpad_example.dart'; import 'package:zeta_example/pages/components/dropdown_example.dart'; +import 'package:zeta_example/pages/components/filter_selection_example.dart'; import 'package:zeta_example/pages/components/list_item_example.dart'; import 'package:zeta_example/pages/components/navigation_bar_example.dart'; import 'package:zeta_example/pages/components/navigation_rail_example.dart'; @@ -76,6 +77,7 @@ final List components = [ Component(TooltipExample.name, (context) => const TooltipExample()), Component(NavigationRailExample.name, (context) => const NavigationRailExample()), Component(SelectInputExample.name, (context) => const SelectInputExample()), + Component(FilterSelectionExample.name, (context) => const FilterSelectionExample()), ]; final List theme = [ diff --git a/example/lib/pages/components/filter_selection_example.dart b/example/lib/pages/components/filter_selection_example.dart new file mode 100644 index 00000000..61cde4b4 --- /dev/null +++ b/example/lib/pages/components/filter_selection_example.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +import '../../widgets.dart'; + +class FilterSelectionExample extends StatefulWidget { + static const String name = 'FilterSelection'; + + const FilterSelectionExample({super.key}); + + @override + State createState() => _FilterSelectionExampleState(); +} + +class _FilterSelectionExampleState extends State { + final items = List.generate(12, (index) => false); + final items2 = List.generate(12, (index) => false); + + @override + Widget build(BuildContext context) { + return ExampleScaffold( + name: FilterSelectionExample.name, + child: Column( + children: [ + const SizedBox(height: ZetaSpacing.b), + ZetaFilterSelection( + items: [ + for (int i = 0; i < items.length; i++) + ZetaFilterChip( + label: 'Label ${i + 1}', + selected: items[i], + onTap: (value) => setState(() => items[i] = value), + ), + ], + onPressed: () {}, + ), + const SizedBox(height: ZetaSpacing.b), + ZetaFilterSelection( + rounded: false, + items: [ + for (int i = 0; i < items2.length; i++) + ZetaFilterChip( + label: 'Label ${i + 1}', + selected: items2[i], + onTap: (value) => setState(() => items2[i] = value), + ), + ], + onPressed: () {}, + ), + ], + ), + ); + } +} diff --git a/example/widgetbook/main.dart b/example/widgetbook/main.dart index c8882d05..28cf6e87 100644 --- a/example/widgetbook/main.dart +++ b/example/widgetbook/main.dart @@ -17,6 +17,7 @@ import 'pages/components/date_input_widgetbook.dart'; import 'pages/components/dial_pad_widgetbook.dart'; import 'pages/components/dialog_widgetbook.dart'; import 'pages/components/dropdown_widgetbook.dart'; +import 'pages/components/filter_selection_widgetbook.dart'; import 'pages/components/in_page_banner_widgetbook.dart'; import 'pages/components/list_item_widgetbook.dart'; import 'pages/components/navigation_bar_widgetbook.dart'; @@ -139,6 +140,7 @@ class HotReload extends StatelessWidget { WidgetbookUseCase(name: 'Navigation Rail', builder: (context) => navigationRailUseCase(context)), WidgetbookUseCase(name: 'Tooltip', builder: (context) => tooltipUseCase(context)), WidgetbookUseCase(name: 'Select Input', builder: (context) => selectInputUseCase(context)), + WidgetbookUseCase(name: 'Filter Selection', builder: (context) => filterSelectionUseCase(context)), ]..sort((a, b) => a.name.compareTo(b.name)), ), WidgetbookCategory( diff --git a/example/widgetbook/pages/components/filter_selection_widgetbook.dart b/example/widgetbook/pages/components/filter_selection_widgetbook.dart new file mode 100644 index 00000000..27abb913 --- /dev/null +++ b/example/widgetbook/pages/components/filter_selection_widgetbook.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +import '../../test/test_components.dart'; + +Widget filterSelectionUseCase(BuildContext context) { + final items = List.generate(12, (index) => false); + final rounded = context.knobs.boolean( + label: 'Rounded', + initialValue: true, + ); + + return WidgetbookTestWidget( + widget: StatefulBuilder( + builder: (_, setState) { + return Column( + children: [ + const SizedBox(height: ZetaSpacing.m), + ZetaFilterSelection( + rounded: rounded, + items: [ + for (int i = 0; i < items.length; i++) + ZetaFilterChip( + label: 'Label ${i + 1}', + selected: items[i], + onTap: (value) => setState(() => items[i] = value), + ), + ], + onPressed: () {}, + ), + ], + ); + }, + ), + ); +} diff --git a/lib/src/components/chips/chip.dart b/lib/src/components/chips/chip.dart index 6432b23d..42a9b768 100644 --- a/lib/src/components/chips/chip.dart +++ b/lib/src/components/chips/chip.dart @@ -54,7 +54,7 @@ class ZetaChip extends StatefulWidget { final bool? selected; /// Callback when chip is tapped. - final VoidCallback? onTap; + final ValueSetter? onTap; @override State createState() => _ZetaChipState(); @@ -66,7 +66,7 @@ class ZetaChip extends StatefulWidget { ..add(StringProperty('label', label)) ..add(DiagnosticsProperty('rounded', rounded)) ..add(DiagnosticsProperty('selected', selected)) - ..add(ObjectFlagProperty.has('onTap', onTap)); + ..add(ObjectFlagProperty?>.has('onTap', onTap)); } } @@ -91,6 +91,7 @@ class _ZetaChipState extends State { onPressed: () { if (widget.type == ZetaChipType.filter) { setState(() => selected = !selected); + widget.onTap?.call(selected); } }, style: ButtonStyle( diff --git a/lib/src/components/chips/filter_chip.dart b/lib/src/components/chips/filter_chip.dart index bcb08f4e..7fa98fe1 100644 --- a/lib/src/components/chips/filter_chip.dart +++ b/lib/src/components/chips/filter_chip.dart @@ -10,5 +10,18 @@ class ZetaFilterChip extends ZetaChip { required super.label, super.rounded, super.selected, + super.onTap, }) : super(type: ZetaChipType.filter); + + /// Creates another instance of [ZetaFilterChip]. + ZetaFilterChip copyWith({ + bool? rounded, + }) { + return ZetaFilterChip( + label: label, + selected: selected, + rounded: rounded ?? this.rounded, + onTap: onTap, + ); + } } diff --git a/lib/src/components/filter_selection/filter_selection.dart b/lib/src/components/filter_selection/filter_selection.dart new file mode 100644 index 00000000..46248b5e --- /dev/null +++ b/lib/src/components/filter_selection/filter_selection.dart @@ -0,0 +1,60 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +import '../../../zeta_flutter.dart'; + +/// Component [ZetaFilterSelection] +class ZetaFilterSelection extends StatelessWidget { + /// Constructor for the component [ZetaFilterSelection] + const ZetaFilterSelection({ + super.key, + required this.items, + this.rounded = true, + this.onPressed, + }); + + /// The filter items - list of [ZetaFilterChip]. + final List items; + + /// {@macro zeta-component-rounded} + final bool rounded; + + /// Called on filter button pressed. + final VoidCallback? onPressed; + + @override + Widget build(BuildContext context) { + return SizedBox( + height: ZetaSpacing.x11, + child: Row( + children: [ + IconButton( + visualDensity: VisualDensity.compact, + onPressed: onPressed, + icon: Icon( + rounded ? ZetaIcons.filter_round : ZetaIcons.filter_sharp, + size: ZetaSpacing.m, + ), + ), + Expanded( + child: ListView( + shrinkWrap: true, + scrollDirection: Axis.horizontal, + padding: const EdgeInsets.all(ZetaSpacing.xxs), + children: + items.map((e) => e.copyWith(rounded: rounded)).divide(const SizedBox(width: ZetaSpacing.x2)).toList(), + ), + ), + ], + ), + ); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('rounded', rounded)) + ..add(ObjectFlagProperty.has('onPressed', onPressed)); + } +} diff --git a/lib/zeta_flutter.dart b/lib/zeta_flutter.dart index dcb83bb9..e3414716 100644 --- a/lib/zeta_flutter.dart +++ b/lib/zeta_flutter.dart @@ -27,6 +27,7 @@ export 'src/components/date_input/date_input.dart'; export 'src/components/dial_pad/dial_pad.dart'; export 'src/components/dialog/dialog.dart'; export 'src/components/dropdown/dropdown.dart'; +export 'src/components/filter_selection/filter_selection.dart'; export 'src/components/list_item/list_item.dart'; export 'src/components/navigation bar/navigation_bar.dart'; export 'src/components/navigation_rail/navigation_rail.dart'; From bb6d5de9c2f06ecd76d3ecd6367289def2915368 Mon Sep 17 00:00:00 2001 From: atanasyordanov21 <63714308+atanasyordanov21@users.noreply.github.com> Date: Mon, 29 Apr 2024 18:00:44 +0300 Subject: [PATCH 3/7] feat: ZetaScreenHeaderBar (#39) --- example/lib/home.dart | 2 + .../components/screen_header_bar_example.dart | 21 +++++++ example/widgetbook/main.dart | 2 + .../screen_header_bar_widgetbook.dart | 22 +++++++ lib/src/components/app_bar/app_bar.dart | 24 ++++++-- .../screen_header_bar/screen_header_bar.dart | 58 +++++++++++++++++++ lib/zeta_flutter.dart | 1 + 7 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 example/lib/pages/components/screen_header_bar_example.dart create mode 100644 example/widgetbook/pages/components/screen_header_bar_widgetbook.dart create mode 100644 lib/src/components/screen_header_bar/screen_header_bar.dart diff --git a/example/lib/home.dart b/example/lib/home.dart index 5e7db604..8d76134b 100644 --- a/example/lib/home.dart +++ b/example/lib/home.dart @@ -20,6 +20,7 @@ import 'package:zeta_example/pages/components/navigation_bar_example.dart'; import 'package:zeta_example/pages/components/navigation_rail_example.dart'; import 'package:zeta_example/pages/components/phone_input_example.dart'; import 'package:zeta_example/pages/components/radio_example.dart'; +import 'package:zeta_example/pages/components/screen_header_bar_example.dart'; import 'package:zeta_example/pages/components/select_input_example.dart'; import 'package:zeta_example/pages/components/search_bar_example.dart'; import 'package:zeta_example/pages/components/segmented_control_example.dart'; @@ -77,6 +78,7 @@ final List components = [ Component(TooltipExample.name, (context) => const TooltipExample()), Component(NavigationRailExample.name, (context) => const NavigationRailExample()), Component(SelectInputExample.name, (context) => const SelectInputExample()), + Component(ScreenHeaderBarExample.name, (context) => const ScreenHeaderBarExample()), Component(FilterSelectionExample.name, (context) => const FilterSelectionExample()), ]; diff --git a/example/lib/pages/components/screen_header_bar_example.dart b/example/lib/pages/components/screen_header_bar_example.dart new file mode 100644 index 00000000..c617db1b --- /dev/null +++ b/example/lib/pages/components/screen_header_bar_example.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:zeta_example/widgets.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +class ScreenHeaderBarExample extends StatelessWidget { + const ScreenHeaderBarExample({super.key}); + + static const String name = 'ScreenHeaderBar'; + + @override + Widget build(BuildContext context) { + return ExampleScaffold( + name: ScreenHeaderBarExample.name, + child: ZetaScreenHeaderBar( + title: Text("Add Subscribers"), + actionButtonLabel: 'Done', + onActionButtonPressed: () {}, + ), + ); + } +} diff --git a/example/widgetbook/main.dart b/example/widgetbook/main.dart index 28cf6e87..6d1f72fe 100644 --- a/example/widgetbook/main.dart +++ b/example/widgetbook/main.dart @@ -27,6 +27,7 @@ import 'pages/components/password_input_widgetbook.dart'; import 'pages/components/phone_input_widgetbook.dart'; import 'pages/components/progress_widgetbook.dart'; import 'pages/components/radio_widgetbook.dart'; +import 'pages/components/screen_header_bar_widgetbook.dart'; import 'pages/components/search_bar_widgetbook.dart'; import 'pages/components/segmented_control_widgetbook.dart'; import 'pages/components/select_input_widgetbook.dart'; @@ -140,6 +141,7 @@ class HotReload extends StatelessWidget { WidgetbookUseCase(name: 'Navigation Rail', builder: (context) => navigationRailUseCase(context)), WidgetbookUseCase(name: 'Tooltip', builder: (context) => tooltipUseCase(context)), WidgetbookUseCase(name: 'Select Input', builder: (context) => selectInputUseCase(context)), + WidgetbookUseCase(name: 'Screen Header Bar', builder: (context) => screenHeaderBarUseCase(context)), WidgetbookUseCase(name: 'Filter Selection', builder: (context) => filterSelectionUseCase(context)), ]..sort((a, b) => a.name.compareTo(b.name)), ), diff --git a/example/widgetbook/pages/components/screen_header_bar_widgetbook.dart b/example/widgetbook/pages/components/screen_header_bar_widgetbook.dart new file mode 100644 index 00000000..dfc66336 --- /dev/null +++ b/example/widgetbook/pages/components/screen_header_bar_widgetbook.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +import '../../test/test_components.dart'; + +Widget screenHeaderBarUseCase(BuildContext context) { + final title = context.knobs.string( + label: 'Title', + initialValue: 'Add Subscribers', + ); + final rounded = context.knobs.boolean(label: 'Rounded', initialValue: true); + + return WidgetbookTestWidget( + widget: ZetaScreenHeaderBar( + title: Text(title), + actionButtonLabel: 'Done', + onActionButtonPressed: () {}, + rounded: rounded, + ), + ); +} diff --git a/lib/src/components/app_bar/app_bar.dart b/lib/src/components/app_bar/app_bar.dart index a0debf79..5c54548f 100644 --- a/lib/src/components/app_bar/app_bar.dart +++ b/lib/src/components/app_bar/app_bar.dart @@ -11,6 +11,8 @@ class ZetaAppBar extends StatefulWidget implements PreferredSizeWidget { this.searchController, this.leading, this.title, + this.titleSpacing, + this.titleTextStyle, this.type = ZetaAppBarType.defaultAppBar, this.onSearch, this.searchHintText = 'Search', @@ -42,6 +44,12 @@ class ZetaAppBar extends StatefulWidget implements PreferredSizeWidget { /// Title of the app bar. Normally a [Text] widget. final Widget? title; + /// AppBar titleSpacing + final double? titleSpacing; + + /// AppBar titleTextStyle + final TextStyle? titleTextStyle; + /// Defines the styles of the app bar. final ZetaAppBarType type; @@ -76,7 +84,9 @@ class ZetaAppBar extends StatefulWidget implements PreferredSizeWidget { ), ) ..add(StringProperty('searchHintText', searchHintText)) - ..add(EnumProperty('type', type)); + ..add(EnumProperty('type', type)) + ..add(DoubleProperty('titleSpacing', titleSpacing)) + ..add(DiagnosticsProperty('titleTextStyle', titleTextStyle)); } } @@ -105,7 +115,7 @@ class _ZetaAppBarState extends State { Widget? _getTitle() { return widget.type != ZetaAppBarType.extendedTitle ? Padding( - padding: const EdgeInsets.symmetric(horizontal: ZetaSpacing.b), + padding: EdgeInsets.symmetric(horizontal: widget.titleSpacing ?? ZetaSpacing.b), child: widget.title, ) : null; @@ -131,9 +141,13 @@ class _ZetaAppBarState extends State { automaticallyImplyLeading: widget.automaticallyImplyLeading, centerTitle: widget.type == ZetaAppBarType.centeredTitle, titleSpacing: 0, - titleTextStyle: ZetaTextStyles.bodyLarge.copyWith( - color: colors.textDefault, - ), + titleTextStyle: widget.titleTextStyle == null + ? ZetaTextStyles.bodyLarge.copyWith( + color: colors.textDefault, + ) + : widget.titleTextStyle!.copyWith( + color: colors.textDefault, + ), title: widget.searchController != null ? _SearchField( searchController: widget.searchController, diff --git a/lib/src/components/screen_header_bar/screen_header_bar.dart b/lib/src/components/screen_header_bar/screen_header_bar.dart new file mode 100644 index 00000000..6a846778 --- /dev/null +++ b/lib/src/components/screen_header_bar/screen_header_bar.dart @@ -0,0 +1,58 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import '../../../zeta_flutter.dart'; + +/// [ZetaScreenHeaderBar] +class ZetaScreenHeaderBar extends StatelessWidget { + /// Constructor for [ZetaScreenHeaderBar]. + const ZetaScreenHeaderBar({ + this.title, + this.rounded = true, + this.actionButtonLabel, + this.onActionButtonPressed, + super.key, + }); + + /// The title of [ZetaScreenHeaderBar]. Normally a [Text] widget. + final Widget? title; + + /// {@macro zeta-component-rounded} + final bool rounded; + + /// The label of the action button. + final String? actionButtonLabel; + + /// Called when the action button is pressed. + final VoidCallback? onActionButtonPressed; + + @override + Widget build(BuildContext context) { + return ZetaAppBar( + leading: IconButton( + onPressed: () async => Navigator.maybePop(context), + icon: Icon(rounded ? ZetaIcons.chevron_left_round : ZetaIcons.chevron_left_sharp), + ), + title: title, + titleSpacing: 0, + titleTextStyle: ZetaTextStyles.titleLarge, + actions: actionButtonLabel == null + ? null + : [ + ZetaButton( + label: actionButtonLabel!, + onPressed: onActionButtonPressed, + borderType: rounded ? ZetaWidgetBorder.rounded : ZetaWidgetBorder.sharp, + ), + ], + ); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('rounded', rounded)) + ..add(StringProperty('actionButtonLabel', actionButtonLabel)) + ..add(ObjectFlagProperty.has('onActionButtonPressed', onActionButtonPressed)); + } +} diff --git a/lib/zeta_flutter.dart b/lib/zeta_flutter.dart index e3414716..cb37ebfd 100644 --- a/lib/zeta_flutter.dart +++ b/lib/zeta_flutter.dart @@ -37,6 +37,7 @@ export 'src/components/phone_input/phone_input.dart'; export 'src/components/progress/progress_bar.dart'; export 'src/components/progress/progress_circle.dart'; export 'src/components/radio/radio.dart'; +export 'src/components/screen_header_bar/screen_header_bar.dart'; export 'src/components/search_bar/search_bar.dart'; export 'src/components/segmented_control/segmented_control.dart'; export 'src/components/select_input/select_input.dart'; From cfaf08e178b88ad7e033c7f517ee58e1fa7d2e86 Mon Sep 17 00:00:00 2001 From: ahmed-osman3 <99483750+ahmed-osman3@users.noreply.github.com> Date: Tue, 30 Apr 2024 11:24:32 +0100 Subject: [PATCH 4/7] fix: Avatar update (#30) * chore: update contributing * fix: Fix button group immutability (#1) * Fix errors * fix copywith function * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * [automated commit] lint format and import sort * update on-main to push to firebase (#3) * ci: move firebase to flutter main host for qa (#4) * feat: Add List Item (#5) * feat: Add List Item * [automated commit] lint format and import sort --------- Co-authored-by: Simeon Dimitrov Co-authored-by: github-actions * Component dialog (#18) * create showZetaDialog * finished dialog for DeviceType.mobilePortrait * dialog variant for bigger screens * create widgetbook; add Zeta parameter, also in ZetaButton * useRootNavigator: false * feat(main): AppBar (#19) * feat(main): AppBar * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * Component dialog (#22) * chore: update contributing * fix: Fix button group immutability (#1) * Fix errors * fix copywith function * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * [automated commit] lint format and import sort * update on-main to push to firebase (#3) * ci: move firebase to flutter main host for qa (#4) * feat: Add List Item (#5) * feat: Add List Item * [automated commit] lint format and import sort --------- Co-authored-by: Simeon Dimitrov Co-authored-by: github-actions * fix(main): ListItem disabled color (#8) * fix(main): ListItem disabled color * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * feat : Dropdown menu (#7) * Create dropdown * Add sizes * create stoyrybook and add size * Fix errrs and respond to comments * Fix issues * [automated commit] lint format and import sort * Alter isLarge * Fix spacing * [automated commit] lint format and import sort * Alter leading styles * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * Component ZetaSwitch (#6) * create ZetaSwitch * ZetaSwitch using MaterialSwitch * widgetbook for ZetaSwitch * remove hover; fix initState * add showHover parameter * add comments 'Zeta change' in material_switch.dart * remove size parameter and factory constructors * fix example and widgetbook * Component Zeta Radio Button (#9) * create component Zeta Radio Button * remove hover color * fix label line height * feat(main): SnackBar (#10) * add snackbar example * Add snackbar widgetbook * feat(main): SnackBar * [automated commit] lint format and import sort * remove view icon * Add view icon * Add widgetbook icon helper * [automated commit] lint format and import sort * fix alphabetical imports * Fix delete and error background color --------- Co-authored-by: github-actions * feat(main): Tabs (#11) * feat(main): Tabs * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * chore: Update text styles (#13) * fix: switch on web (#14) * Component date input (#12) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * create showZetaDialog * finished dialog for DeviceType.mobilePortrait * Component date input (#16) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * fix Typography of Date Input * restore * remove text line height * dialog variant for bigger screens * create widgetbook; add Zeta parameter, also in ZetaButton * useRootNavigator: false * add iconKnob in Dialog widgetbook * final iconData = iconKnob --------- Co-authored-by: Luke Co-authored-by: ahmed-osman3 <99483750+ahmed-osman3@users.noreply.github.com> Co-authored-by: Osman Co-authored-by: github-actions Co-authored-by: Luke Walton Co-authored-by: Simeon Dimitrov Co-authored-by: sd-athlon <163880004+sd-athlon@users.noreply.github.com> * Merge * Component phone input (#21) * chore: update contributing * fix: Fix button group immutability (#1) * Fix errors * fix copywith function * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * [automated commit] lint format and import sort * update on-main to push to firebase (#3) * ci: move firebase to flutter main host for qa (#4) * feat: Add List Item (#5) * feat: Add List Item * [automated commit] lint format and import sort --------- Co-authored-by: Simeon Dimitrov Co-authored-by: github-actions * fix(main): ListItem disabled color (#8) * fix(main): ListItem disabled color * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * feat : Dropdown menu (#7) * Create dropdown * Add sizes * create stoyrybook and add size * Fix errrs and respond to comments * Fix issues * [automated commit] lint format and import sort * Alter isLarge * Fix spacing * [automated commit] lint format and import sort * Alter leading styles * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * Component ZetaSwitch (#6) * create ZetaSwitch * ZetaSwitch using MaterialSwitch * widgetbook for ZetaSwitch * remove hover; fix initState * add showHover parameter * add comments 'Zeta change' in material_switch.dart * remove size parameter and factory constructors * fix example and widgetbook * Component Zeta Radio Button (#9) * create component Zeta Radio Button * remove hover color * fix label line height * feat(main): SnackBar (#10) * add snackbar example * Add snackbar widgetbook * feat(main): SnackBar * [automated commit] lint format and import sort * remove view icon * Add view icon * Add widgetbook icon helper * [automated commit] lint format and import sort * fix alphabetical imports * Fix delete and error background color --------- Co-authored-by: github-actions * feat(main): Tabs (#11) * feat(main): Tabs * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * chore: Update text styles (#13) * fix: switch on web (#14) * Component date input (#12) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * Component date input (#16) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * fix Typography of Date Input * restore * remove text line height * ZetaPhoneInput initial commit * complete ZetaPhoneInput; add flags * create phoneInputUseCase in Widgetbook * refactor phone input to use native alert dialog --------- Co-authored-by: Luke Co-authored-by: ahmed-osman3 <99483750+ahmed-osman3@users.noreply.github.com> Co-authored-by: Osman Co-authored-by: github-actions Co-authored-by: Luke Walton Co-authored-by: Simeon Dimitrov Co-authored-by: sd-athlon <163880004+sd-athlon@users.noreply.github.com> * restore main.dart in example (#23) * Component phone input (#24) * chore: update contributing * fix: Fix button group immutability (#1) * Fix errors * fix copywith function * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * [automated commit] lint format and import sort * update on-main to push to firebase (#3) * ci: move firebase to flutter main host for qa (#4) * feat: Add List Item (#5) * feat: Add List Item * [automated commit] lint format and import sort --------- Co-authored-by: Simeon Dimitrov Co-authored-by: github-actions * fix(main): ListItem disabled color (#8) * fix(main): ListItem disabled color * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * feat : Dropdown menu (#7) * Create dropdown * Add sizes * create stoyrybook and add size * Fix errrs and respond to comments * Fix issues * [automated commit] lint format and import sort * Alter isLarge * Fix spacing * [automated commit] lint format and import sort * Alter leading styles * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * Component ZetaSwitch (#6) * create ZetaSwitch * ZetaSwitch using MaterialSwitch * widgetbook for ZetaSwitch * remove hover; fix initState * add showHover parameter * add comments 'Zeta change' in material_switch.dart * remove size parameter and factory constructors * fix example and widgetbook * Component Zeta Radio Button (#9) * create component Zeta Radio Button * remove hover color * fix label line height * feat(main): SnackBar (#10) * add snackbar example * Add snackbar widgetbook * feat(main): SnackBar * [automated commit] lint format and import sort * remove view icon * Add view icon * Add widgetbook icon helper * [automated commit] lint format and import sort * fix alphabetical imports * Fix delete and error background color --------- Co-authored-by: github-actions * feat(main): Tabs (#11) * feat(main): Tabs * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * chore: Update text styles (#13) * fix: switch on web (#14) * Component date input (#12) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * Component date input (#16) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * fix Typography of Date Input * restore * remove text line height * ZetaPhoneInput initial commit * complete ZetaPhoneInput; add flags * create phoneInputUseCase in Widgetbook * refactor phone input to use native alert dialog * don't use root navigator in widgetbook --------- Co-authored-by: Luke Co-authored-by: ahmed-osman3 <99483750+ahmed-osman3@users.noreply.github.com> Co-authored-by: Osman Co-authored-by: github-actions Co-authored-by: Luke Walton Co-authored-by: Simeon Dimitrov Co-authored-by: sd-athlon <163880004+sd-athlon@users.noreply.github.com> * Component phone input (#25) * chore: update contributing * fix: Fix button group immutability (#1) * Fix errors * fix copywith function * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * [automated commit] lint format and import sort * update on-main to push to firebase (#3) * ci: move firebase to flutter main host for qa (#4) * feat: Add List Item (#5) * feat: Add List Item * [automated commit] lint format and import sort --------- Co-authored-by: Simeon Dimitrov Co-authored-by: github-actions * fix(main): ListItem disabled color (#8) * fix(main): ListItem disabled color * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * feat : Dropdown menu (#7) * Create dropdown * Add sizes * create stoyrybook and add size * Fix errrs and respond to comments * Fix issues * [automated commit] lint format and import sort * Alter isLarge * Fix spacing * [automated commit] lint format and import sort * Alter leading styles * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * Component ZetaSwitch (#6) * create ZetaSwitch * ZetaSwitch using MaterialSwitch * widgetbook for ZetaSwitch * remove hover; fix initState * add showHover parameter * add comments 'Zeta change' in material_switch.dart * remove size parameter and factory constructors * fix example and widgetbook * Component Zeta Radio Button (#9) * create component Zeta Radio Button * remove hover color * fix label line height * feat(main): SnackBar (#10) * add snackbar example * Add snackbar widgetbook * feat(main): SnackBar * [automated commit] lint format and import sort * remove view icon * Add view icon * Add widgetbook icon helper * [automated commit] lint format and import sort * fix alphabetical imports * Fix delete and error background color --------- Co-authored-by: github-actions * feat(main): Tabs (#11) * feat(main): Tabs * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * chore: Update text styles (#13) * fix: switch on web (#14) * Component date input (#12) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * Component date input (#16) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * fix Typography of Date Input * restore * remove text line height * ZetaPhoneInput initial commit * complete ZetaPhoneInput; add flags * create phoneInputUseCase in Widgetbook * refactor phone input to use native alert dialog * don't use root navigator in widgetbook * pass parameter useRootNavigator * restore some missing countries in the list --------- Co-authored-by: Luke Co-authored-by: ahmed-osman3 <99483750+ahmed-osman3@users.noreply.github.com> Co-authored-by: Osman Co-authored-by: github-actions Co-authored-by: Luke Walton Co-authored-by: Simeon Dimitrov Co-authored-by: sd-athlon <163880004+sd-athlon@users.noreply.github.com> * Update avatar * Update avatar * [automated commit] lint format and import sort * Navigation rail (#27) * create NavigationRail * restore main.dart in example * navigation rail example * create widgetbook * add SafeArea; rename parameter wordWrap * add MouseRegion & SelectionContainer.disabled * Respond to comments * Component tooltip (#31) * create tooltip * create Widgetbook for ZetaTooltip * add LayoutBuilder * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * feat(main): Segmented control (#26) * feat(main): AppBar * Add segmented control * [automated commit] lint format and import sort * Fix mouse cursor, disable selection container and tap area * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * [automated commit] lint format and import sort * Add pill notifications * [automated commit] lint format and import sort * Remove notification border * Change sizes --------- Co-authored-by: Luke Co-authored-by: Osman Co-authored-by: github-actions Co-authored-by: Luke Walton Co-authored-by: Simeon Dimitrov Co-authored-by: atanasyordanov21 <63714308+atanasyordanov21@users.noreply.github.com> Co-authored-by: sd-athlon <163880004+sd-athlon@users.noreply.github.com> --- .../lib/pages/components/avatar_example.dart | 56 ++-- .../pages/components/avatar_widgetbook.dart | 25 +- lib/src/components/avatars/avatar.dart | 310 ++++++++++++++---- lib/src/components/buttons/button_group.dart | 5 +- lib/src/theme/tokens.dart | 6 + 5 files changed, 301 insertions(+), 101 deletions(-) diff --git a/example/lib/pages/components/avatar_example.dart b/example/lib/pages/components/avatar_example.dart index 39f42a8f..ad683953 100644 --- a/example/lib/pages/components/avatar_example.dart +++ b/example/lib/pages/components/avatar_example.dart @@ -202,7 +202,7 @@ class AvatarExample extends StatelessWidget { children: [ ZetaAvatar.image( size: size, - upperBadge: ZetaIndicator.notification(value: 3), + upperBadge: ZetaAvatarBadge.notification(value: 3), ), const SizedBox(height: 20), ], @@ -217,7 +217,7 @@ class AvatarExample extends StatelessWidget { ZetaAvatar.image( size: size, borderColor: Zeta.of(context).colors.green, - upperBadge: ZetaIndicator.notification(value: 3), + upperBadge: ZetaAvatarBadge.notification(value: 3), ), const SizedBox(height: 20), ], @@ -231,7 +231,7 @@ class AvatarExample extends StatelessWidget { children: [ ZetaAvatar.image( size: size, - upperBadge: ZetaIndicator.notification(value: 3), + upperBadge: ZetaAvatarBadge.notification(value: 3), image: image, ), const SizedBox(height: 20), @@ -247,7 +247,7 @@ class AvatarExample extends StatelessWidget { ZetaAvatar.image( size: size, borderColor: Zeta.of(context).colors.green, - upperBadge: ZetaIndicator.notification(value: 3), + upperBadge: ZetaAvatarBadge.notification(value: 3), image: image, ), const SizedBox(height: 20), @@ -294,7 +294,7 @@ class AvatarExample extends StatelessWidget { ZetaAvatar.initials( size: size, initials: 'AB', - upperBadge: ZetaIndicator.notification(value: 3), + upperBadge: ZetaAvatarBadge.notification(value: 3), ), const SizedBox(height: 20), ], @@ -310,7 +310,7 @@ class AvatarExample extends StatelessWidget { size: size, initials: 'AB', borderColor: Zeta.of(context).colors.green, - upperBadge: ZetaIndicator.notification(value: 3), + upperBadge: ZetaAvatarBadge.notification(value: 3), ), const SizedBox(height: 20), ], @@ -355,7 +355,7 @@ class AvatarExample extends StatelessWidget { children: [ ZetaAvatar.image( size: size, - lowerBadge: ZetaIndicator.icon(), + lowerBadge: ZetaAvatarBadge.icon(), ), const SizedBox(height: 20), ], @@ -370,7 +370,7 @@ class AvatarExample extends StatelessWidget { ZetaAvatar.image( size: size, borderColor: Zeta.of(context).colors.green, - lowerBadge: ZetaIndicator.icon(), + lowerBadge: ZetaAvatarBadge.icon(), ), const SizedBox(height: 20), ], @@ -384,7 +384,7 @@ class AvatarExample extends StatelessWidget { children: [ ZetaAvatar.image( size: size, - lowerBadge: ZetaIndicator.icon(), + lowerBadge: ZetaAvatarBadge.icon(), image: image, ), const SizedBox(height: 20), @@ -400,7 +400,7 @@ class AvatarExample extends StatelessWidget { ZetaAvatar.image( size: size, borderColor: Zeta.of(context).colors.green, - lowerBadge: ZetaIndicator.icon(), + lowerBadge: ZetaAvatarBadge.icon(), image: image, ), const SizedBox(height: 20), @@ -447,7 +447,7 @@ class AvatarExample extends StatelessWidget { ZetaAvatar.initials( size: size, initials: 'AB', - lowerBadge: ZetaIndicator.icon(), + lowerBadge: ZetaAvatarBadge.icon(), ), const SizedBox(height: 20), ], @@ -463,7 +463,7 @@ class AvatarExample extends StatelessWidget { size: size, initials: 'AB', borderColor: Zeta.of(context).colors.green, - lowerBadge: ZetaIndicator.icon(), + lowerBadge: ZetaAvatarBadge.icon(), ), const SizedBox(height: 20), ], @@ -509,8 +509,8 @@ class AvatarExample extends StatelessWidget { ZetaAvatar.image( size: size, image: image, - upperBadge: ZetaIndicator.notification(value: 3), - lowerBadge: ZetaIndicator.icon(), + upperBadge: ZetaAvatarBadge.notification(value: 3), + lowerBadge: ZetaAvatarBadge.icon(), ), const SizedBox(height: 20), ], @@ -526,8 +526,8 @@ class AvatarExample extends StatelessWidget { size: size, image: image, borderColor: Zeta.of(context).colors.green, - upperBadge: ZetaIndicator.notification(value: 3), - lowerBadge: ZetaIndicator.icon(), + upperBadge: ZetaAvatarBadge.notification(value: 3), + lowerBadge: ZetaAvatarBadge.icon(), ), const SizedBox(height: 20), ], @@ -541,8 +541,8 @@ class AvatarExample extends StatelessWidget { ZetaAvatar.initials( size: size, initials: 'AB', - upperBadge: ZetaIndicator.notification(value: 3), - lowerBadge: ZetaIndicator.icon(), + upperBadge: ZetaAvatarBadge.notification(value: 3), + lowerBadge: ZetaAvatarBadge.icon(), ), const SizedBox(height: 20), ], @@ -558,8 +558,8 @@ class AvatarExample extends StatelessWidget { size: size, initials: 'AB', borderColor: Zeta.of(context).colors.green, - upperBadge: ZetaIndicator.notification(value: 3), - lowerBadge: ZetaIndicator.icon(), + upperBadge: ZetaAvatarBadge.notification(value: 3), + lowerBadge: ZetaAvatarBadge.icon(), ), const SizedBox(height: 20), ], @@ -580,15 +580,23 @@ class AvatarExample extends StatelessWidget { extension on ZetaAvatarSize { double get pixelSize { switch (this) { + case ZetaAvatarSize.xxxl: + return ZetaSpacing.x50; + case ZetaAvatarSize.xxl: + return ZetaSpacing.x30; case ZetaAvatarSize.xl: - return ZetaSpacing.x16; + return ZetaSpacing.x20; case ZetaAvatarSize.l: - return ZetaSpacing.x12; + return ZetaSpacing.x16; case ZetaAvatarSize.m: - return ZetaSpacing.x10; + return ZetaSpacing.x12; case ZetaAvatarSize.s: - return ZetaSpacing.x8; + return ZetaSpacing.x10; case ZetaAvatarSize.xs: + return ZetaSpacing.x9; + case ZetaAvatarSize.xxs: + return ZetaSpacing.x8; + case ZetaAvatarSize.xxxs: return ZetaSpacing.x6; } } diff --git a/example/widgetbook/pages/components/avatar_widgetbook.dart b/example/widgetbook/pages/components/avatar_widgetbook.dart index f533cf08..488cd5a0 100644 --- a/example/widgetbook/pages/components/avatar_widgetbook.dart +++ b/example/widgetbook/pages/components/avatar_widgetbook.dart @@ -6,6 +6,7 @@ import '../../test/test_components.dart'; Widget avatarUseCase(BuildContext context) { final Widget image = Image.asset('assets/Omer.jpg', fit: BoxFit.cover); + final colors = Zeta.of(context).colors; return WidgetbookTestWidget( widget: ZetaAvatar( @@ -15,12 +16,26 @@ Widget avatarUseCase(BuildContext context) { options: ZetaAvatarSize.values, labelBuilder: (value) => value.name.split('.').last.toUpperCase(), ), - lowerBadge: context.knobs.boolean(label: 'Status Badge', initialValue: false) ? ZetaIndicator.icon() : null, - borderColor: context.knobs.colorOrNull(label: 'Outline', initialValue: null), - upperBadge: - context.knobs.boolean(label: 'Notification Badge', initialValue: false) ? ZetaIndicator.notification() : null, + upperBadge: context.knobs.boolean(label: 'Status Badge', initialValue: false) + ? ZetaAvatarBadge.icon( + icon: ZetaIcons.close_round, + iconColor: context.knobs.colorOrNull(label: "Badge Icon Color"), + color: context.knobs.colorOrNull(label: "Upper Badge Color", initialValue: colors.green) ?? + colors.iconDefault, + ) + : null, + borderColor: context.knobs.colorOrNull( + label: 'Outline', + ), + lowerBadge: context.knobs.boolean(label: 'Notification Badge', initialValue: false) + ? ZetaAvatarBadge.notification( + value: context.knobs.intOrNull.input(label: "Value", initialValue: 1), + ) + : null, initials: context.knobs.stringOrNull(label: 'Initials', initialValue: null), - backgroundColor: context.knobs.colorOrNull(label: 'Background color'), + backgroundColor: context.knobs.colorOrNull( + label: 'Background color', + ), ), ); } diff --git a/lib/src/components/avatars/avatar.dart b/lib/src/components/avatars/avatar.dart index c1d4bf6a..69b3cf72 100644 --- a/lib/src/components/avatars/avatar.dart +++ b/lib/src/components/avatars/avatar.dart @@ -1,23 +1,35 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import '../../../zeta_flutter.dart'; /// [ZetaAvatar] size enum ZetaAvatarSize { - /// [xl] 64 pixels + /// [xxxl] 200 pixels + xxxl, + + /// [xxl] 120 pixels + xxl, + + /// [xl] 80 pixels xl, - /// [l] 48 pixels + /// [l] 64 pixels l, - /// [m] 40 pixels + /// [m] 48 pixels m, - /// [s] 32 pixels + /// [s] 40 pixels s, - /// [xs] 24 pixels + /// [xs] 36 pixels xs, + + /// [xxs] 32 pixels + xxs, + + /// [xxxs] 24 pixels + xxxs, } /// ZetaAvatar component @@ -86,10 +98,10 @@ class ZetaAvatar extends StatelessWidget { final Color? borderColor; /// Status badge shown at lower right corner of avatar. - final ZetaIndicator? lowerBadge; + final ZetaAvatarBadge? lowerBadge; /// Notification Badge shown at top right corner of avatar. - final ZetaIndicator? upperBadge; + final ZetaAvatarBadge? upperBadge; bool get _showPlaceholder => image == null && (initials == null || initials!.isEmpty); @@ -99,39 +111,34 @@ class ZetaAvatar extends StatelessWidget { final borderSize = size.borderSize; final sizePixels = size.pixelSize; - final contentSizePixels = size.pixelSize - (borderColor != null ? size.borderSize * 2 : 0); + final contentSizePixels = size.pixelSize; - final innerContent = ClipRRect( - borderRadius: BorderRadius.circular(64), - clipBehavior: Clip.hardEdge, - child: image ?? - (_showPlaceholder - ? Container( - transform: Matrix4.translationValues(-contentSizePixels * 0.2, -contentSizePixels * 0.1, 0), - child: IconTheme( - data: IconThemeData( - color: Zeta.of(context).colors.cool, - size: contentSizePixels * 1.4, - ), - child: const Icon(ZetaIcons.user_round), + final innerChild = image ?? + (initials != null + ? Center( + child: Text( + size == ZetaAvatarSize.xs ? initials!.substring(0, 1) : initials!, + style: TextStyle( + fontSize: size.fontSize, + letterSpacing: 0, + color: backgroundColor?.onColor, ), - ) - : Center( - child: Text( - size == ZetaAvatarSize.xs ? initials!.substring(0, 1) : initials!, - style: TextStyle(fontSize: size.fontSize, letterSpacing: -0.5), - ), - )), + ), + ) + : null); + + final innerContent = ClipRRect( + borderRadius: ZetaRadius.full, + child: innerChild, ); return Stack( children: [ Container( - margin: const EdgeInsets.all(3), width: sizePixels, height: sizePixels, decoration: BoxDecoration( - border: borderColor != null ? Border.all(color: borderColor!, width: borderSize / ZetaSpacing.x0_5) : null, + border: borderColor != null ? Border.all(color: borderColor!, width: 0) : null, borderRadius: ZetaRadius.full, color: backgroundColor ?? (_showPlaceholder ? zetaColors.surfacePrimary : zetaColors.cool.shade20), ), @@ -140,30 +147,40 @@ class ZetaAvatar extends StatelessWidget { width: contentSizePixels, height: contentSizePixels, decoration: BoxDecoration( - color: _showPlaceholder ? backgroundColor ?? zetaColors.surfaceHovered : null, - border: Border.all(color: zetaColors.surfacePrimary, width: borderSize / ZetaSpacing.x0_5), + color: backgroundColor ?? zetaColors.surfaceHovered, + border: Border.all(color: borderColor!, width: borderSize), + borderRadius: ZetaRadius.full, + ), + child: ClipRRect( borderRadius: ZetaRadius.full, + child: innerContent, ), - child: ClipRRect(borderRadius: ZetaRadius.full, clipBehavior: Clip.hardEdge, child: innerContent), ) : DecoratedBox( decoration: BoxDecoration( borderRadius: ZetaRadius.full, color: backgroundColor ?? zetaColors.surfaceHovered, ), - child: innerContent, + child: ClipRRect( + borderRadius: ZetaRadius.full, + child: innerContent, + ), ), ), if (upperBadge != null) Positioned( - right: 1, - child: upperBadge!.copyWith(size: size.indicatorSize), + right: 0, + child: upperBadge!.copyWith( + size: size, + ), ), if (lowerBadge != null) Positioned( - right: 1, - bottom: 1, - child: lowerBadge!.copyWith(size: size.indicatorSize), + right: 0, + bottom: 0, + child: lowerBadge!.copyWith( + size: size, + ), ), ], ); @@ -175,8 +192,8 @@ class ZetaAvatar extends StatelessWidget { properties ..add(DiagnosticsProperty('size', size)) ..add(DiagnosticsProperty('name', initials)) - ..add(DiagnosticsProperty('specialStatus', lowerBadge)) - ..add(DiagnosticsProperty('badge', upperBadge)) + ..add(DiagnosticsProperty('specialStatus', lowerBadge)) + ..add(DiagnosticsProperty('badge', upperBadge)) ..add(DiagnosticsProperty('backgroundColor', backgroundColor)) ..add(ColorProperty('statusColor', borderColor)); } @@ -185,35 +202,32 @@ class ZetaAvatar extends StatelessWidget { extension on ZetaAvatarSize { double get pixelSize { switch (this) { + case ZetaAvatarSize.xxxl: + return ZetaSpacing.x50; + case ZetaAvatarSize.xxl: + return ZetaSpacing.x30; case ZetaAvatarSize.xl: - return ZetaSpacing.x16; + return ZetaSpacing.x20; case ZetaAvatarSize.l: - return ZetaSpacing.x12; + return ZetaSpacing.x16; case ZetaAvatarSize.m: - return ZetaSpacing.x10; + return ZetaSpacing.x12; case ZetaAvatarSize.s: - return ZetaSpacing.x8; + return ZetaSpacing.x10; case ZetaAvatarSize.xs: + return ZetaSpacing.x9; + case ZetaAvatarSize.xxs: + return ZetaSpacing.x8; + case ZetaAvatarSize.xxxs: return ZetaSpacing.x6; } } - ZetaWidgetSize get indicatorSize { - switch (this) { - case ZetaAvatarSize.xl: - case ZetaAvatarSize.l: - case ZetaAvatarSize.m: - return ZetaWidgetSize.large; - case ZetaAvatarSize.s: - return ZetaWidgetSize.medium; - - case ZetaAvatarSize.xs: - return ZetaWidgetSize.small; - } - } - double get borderSize { switch (this) { + case ZetaAvatarSize.xxxl: + return 11; + case ZetaAvatarSize.xxl: case ZetaAvatarSize.xl: case ZetaAvatarSize.l: case ZetaAvatarSize.m: @@ -221,21 +235,175 @@ extension on ZetaAvatarSize { case ZetaAvatarSize.s: case ZetaAvatarSize.xs: + case ZetaAvatarSize.xxs: + case ZetaAvatarSize.xxxs: return ZetaSpacing.x0_5; } } double get fontSize { - switch (this) { - case ZetaAvatarSize.xl: - return ZetaSpacing.x5; - case ZetaAvatarSize.l: - return ZetaSpacing.x4; - case ZetaAvatarSize.m: - return ZetaSpacing.x3_5; - case ZetaAvatarSize.s: - case ZetaAvatarSize.xs: - return ZetaSpacing.x3; - } + return pixelSize * 4 / 9; + } +} + +/// Enum of types for [ZetaAvatarBadge] +enum ZetaAvatarBadgeType { + /// Shows an icon on [ZetaAvatarBadge]. Defaults to [ZetaIcons.star_round]. + icon, + + /// Shows a number on [ZetaAvatarBadge] from provided [ZetaAvatarBadge.value]. + notification, +} + +/// ZetaAvatarBadge component + +class ZetaAvatarBadge extends StatelessWidget { + /// Constructor for [ZetaAvatarBadge] + const ZetaAvatarBadge({ + super.key, + this.color, + this.type = ZetaAvatarBadgeType.notification, + this.icon, + this.value, + this.iconColor, + }) : size = ZetaAvatarSize.xxxl; + + const ZetaAvatarBadge._({ + super.key, + required this.color, + required this.size, + required this.type, + this.icon, + this.value, + this.iconColor, + }); + + /// Constructs [ZetaAvatarBadge] with icon + const ZetaAvatarBadge.icon({ + super.key, + this.color, + this.icon = ZetaIcons.star_round, + this.iconColor, + }) : value = null, + size = ZetaAvatarSize.xxxl, + type = ZetaAvatarBadgeType.icon; + + /// Constructs [ZetaAvatarBadge] with notifications + + const ZetaAvatarBadge.notification({ + super.key, + required this.value, + }) : size = ZetaAvatarSize.xxxl, + icon = null, + iconColor = null, + color = null, + type = ZetaAvatarBadgeType.notification; + + /// Size of badge + final ZetaAvatarSize size; + + /// Type of badge + final ZetaAvatarBadgeType type; + + /// Background color for badge + final Color? color; + + /// Icon of badge + final IconData? icon; + + /// Icon color for badge + final Color? iconColor; + + /// Notification value for badge + final int? value; + + /// Returns copy of [ZetaAvatarBadge] + ZetaAvatarBadge copyWith({ + Color? color, + ZetaAvatarSize? size, + IconData? icon, + Color? iconColor, + int? value, + ZetaAvatarBadgeType? type, + }) { + return ZetaAvatarBadge._( + color: color ?? this.color, + size: size ?? this.size, + icon: icon ?? this.icon, + type: type ?? this.type, + value: value ?? this.value, + iconColor: iconColor ?? this.iconColor, + key: key, + ); + } + + @override + Widget build(BuildContext context) { + final colors = Zeta.of(context).colors; + final backgroundColor = type == ZetaAvatarBadgeType.notification ? colors.negative : color; + final badgeSize = _getContainerSize(); + final borderSize = _getBorderSize(); + final paddedSize = badgeSize + ZetaSpacing.x1; + + final innerContent = Container( + margin: const EdgeInsets.all(0.01), + decoration: BoxDecoration( + color: backgroundColor, + borderRadius: ZetaRadius.full, + ), + child: value != null + ? Center( + child: Text( + value! > 99 ? '99+' : '$value', + style: TextStyle( + color: backgroundColor?.onColor, + fontSize: ((10 / 12) * badgeSize) - 2, + height: 1, + ), + ), + ) + : icon != null + ? Icon( + icon, + size: badgeSize - borderSize, + color: iconColor ?? backgroundColor?.onColor, + ) + : null, + ); + + return Container( + width: type == ZetaAvatarBadgeType.icon ? paddedSize : badgeSize * 1.8, + height: type == ZetaAvatarBadgeType.icon ? paddedSize : badgeSize, + decoration: BoxDecoration( + borderRadius: ZetaRadius.full, + border: type != ZetaAvatarBadgeType.notification + ? Border.all( + width: borderSize, + color: Zeta.of(context).colors.surfacePrimary, + ) + : null, + ), + child: Center(child: innerContent), + ); + } + + double _getContainerSize() { + return size.pixelSize / 3; + } + + double _getBorderSize() { + return size.pixelSize / 48; + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(ColorProperty('color', color)) + ..add(EnumProperty('size', size)) + ..add(EnumProperty('type', type)) + ..add(DiagnosticsProperty('icon', icon)) + ..add(ColorProperty('iconColor', iconColor)) + ..add(IntProperty('value', value)); } } diff --git a/lib/src/components/buttons/button_group.dart b/lib/src/components/buttons/button_group.dart index bf903921..1f30db8e 100644 --- a/lib/src/components/buttons/button_group.dart +++ b/lib/src/components/buttons/button_group.dart @@ -245,7 +245,10 @@ class _ZetaGroupButtonState extends State { if (widget.icon != null) Icon(widget.icon, size: ZetaSpacing.x5), Text(widget.label ?? '', style: ZetaTextStyles.labelMedium), if (widget.dropdown != null) // TODO(UX-1006): Dropdown - Icon(widget.rounded ? ZetaIcons.expand_more_round : ZetaIcons.expand_more_sharp, size: ZetaSpacing.x5), + Icon( + widget.rounded ? ZetaIcons.expand_more_round : ZetaIcons.expand_more_sharp, + size: ZetaSpacing.x5, + ), ].divide(const SizedBox(width: ZetaSpacing.x1)).toList(), ).paddingAll(_padding), ), diff --git a/lib/src/theme/tokens.dart b/lib/src/theme/tokens.dart index caecce51..286ac88a 100644 --- a/lib/src/theme/tokens.dart +++ b/lib/src/theme/tokens.dart @@ -98,6 +98,12 @@ class ZetaSpacing { /// 96dp space. static const double xxxl = spacingBaseMultiplier * 24; + + /// 120dp space + static const double x30 = spacingBaseMultiplier * 30; + + /// 200dp space + static const double x50 = spacingBaseMultiplier * 50; } /// Tokens used for Border Radius. From 5414fca1c4d3fa8c26e1a2b5f7438a3ef7a12a89 Mon Sep 17 00:00:00 2001 From: sd-athlon <163880004+sd-athlon@users.noreply.github.com> Date: Tue, 30 Apr 2024 13:29:46 +0300 Subject: [PATCH 5/7] main: Contact List (#40) * feat(main): Contact List * [automated commit] lint format and import sort --------- Co-authored-by: github-actions --- example/lib/home.dart | 2 + .../components/contact_item_example.dart | 27 +++++ example/widgetbook/main.dart | 2 + .../components/contact_item_widgetbook.dart | 21 ++++ .../components/contact_item/contact_item.dart | 100 ++++++++++++++++++ lib/zeta_flutter.dart | 1 + 6 files changed, 153 insertions(+) create mode 100644 example/lib/pages/components/contact_item_example.dart create mode 100644 example/widgetbook/pages/components/contact_item_widgetbook.dart create mode 100644 lib/src/components/contact_item/contact_item.dart diff --git a/example/lib/home.dart b/example/lib/home.dart index 8d76134b..dd349a64 100644 --- a/example/lib/home.dart +++ b/example/lib/home.dart @@ -10,6 +10,7 @@ import 'package:zeta_example/pages/components/breadcrumbs_example.dart'; import 'package:zeta_example/pages/components/button_example.dart'; import 'package:zeta_example/pages/components/checkbox_example.dart'; import 'package:zeta_example/pages/components/chip_example.dart'; +import 'package:zeta_example/pages/components/contact_item_example.dart'; import 'package:zeta_example/pages/components/date_input_example.dart'; import 'package:zeta_example/pages/components/dialog_example.dart'; import 'package:zeta_example/pages/components/dialpad_example.dart'; @@ -58,6 +59,7 @@ final List components = [ Component(ButtonExample.name, (context) => const ButtonExample()), Component(CheckBoxExample.name, (context) => const CheckBoxExample()), Component(ChipExample.name, (context) => const ChipExample()), + Component(ContactItemExample.name, (context) => const ContactItemExample()), Component(ListItemExample.name, (context) => const ListItemExample()), Component(NavigationBarExample.name, (context) => const NavigationBarExample()), Component(PaginationExample.name, (context) => const PaginationExample()), diff --git a/example/lib/pages/components/contact_item_example.dart b/example/lib/pages/components/contact_item_example.dart new file mode 100644 index 00000000..cc566218 --- /dev/null +++ b/example/lib/pages/components/contact_item_example.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:zeta_example/widgets.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +class ContactItemExample extends StatefulWidget { + static const String name = 'ContactItem'; + + const ContactItemExample({super.key}); + + @override + State createState() => _ContactItemExampleState(); +} + +class _ContactItemExampleState extends State { + @override + Widget build(BuildContext context) { + return ExampleScaffold( + name: ContactItemExample.name, + child: ZetaContactItem( + onTap: () {}, + leading: ZetaAvatar(size: ZetaAvatarSize.s), + title: Text("Contact / Group Name"), + subtitle: Text("Store Associate - Bakery Dept."), + ), + ); + } +} diff --git a/example/widgetbook/main.dart b/example/widgetbook/main.dart index 6d1f72fe..38719607 100644 --- a/example/widgetbook/main.dart +++ b/example/widgetbook/main.dart @@ -13,6 +13,7 @@ import 'pages/components/breadcrumbs_widgetbook.dart'; import 'pages/components/button_widgetbook.dart'; import 'pages/components/checkbox_widgetbook.dart'; import 'pages/components/chip_widgetbook.dart'; +import 'pages/components/contact_item_widgetbook.dart'; import 'pages/components/date_input_widgetbook.dart'; import 'pages/components/dial_pad_widgetbook.dart'; import 'pages/components/dialog_widgetbook.dart'; @@ -83,6 +84,7 @@ class HotReload extends StatelessWidget { ), WidgetbookUseCase(name: 'Avatar', builder: (context) => avatarUseCase(context)), WidgetbookUseCase(name: 'Checkbox', builder: (context) => checkboxUseCase(context)), + WidgetbookUseCase(name: 'Contact Item', builder: (context) => contactItemUseCase(context)), WidgetbookComponent( name: 'Buttons', useCases: [ diff --git a/example/widgetbook/pages/components/contact_item_widgetbook.dart b/example/widgetbook/pages/components/contact_item_widgetbook.dart new file mode 100644 index 00000000..d41fa8c7 --- /dev/null +++ b/example/widgetbook/pages/components/contact_item_widgetbook.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +import '../../test/test_components.dart'; + +Widget contactItemUseCase(BuildContext context) { + final title = context.knobs.string(label: 'Title', initialValue: "Contact / Group Name"); + final subtitle = context.knobs.string(label: 'Subtitle', initialValue: "Store Associate - Bakery Dept."); + final enabledDivider = context.knobs.boolean(label: 'Enabled Divider', initialValue: true); + + return WidgetbookTestWidget( + widget: ZetaContactItem( + onTap: () {}, + leading: ZetaAvatar(size: ZetaAvatarSize.s), + title: Text(title), + subtitle: Text(subtitle), + enabledDivider: enabledDivider, + ), + ); +} diff --git a/lib/src/components/contact_item/contact_item.dart b/lib/src/components/contact_item/contact_item.dart new file mode 100644 index 00000000..ac1d2bcc --- /dev/null +++ b/lib/src/components/contact_item/contact_item.dart @@ -0,0 +1,100 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import '../../../zeta_flutter.dart'; + +/// A single row that contains avatar, title and subtitle. +class ZetaContactItem extends StatelessWidget { + /// Constructs [ZetaContactItem]. + const ZetaContactItem({ + required this.title, + required this.leading, + required this.subtitle, + this.enabledDivider = true, + this.onTap, + super.key, + }); + + /// The main text to be displayed on the top. + final Widget title; + + /// Normally an Avatar + final Widget leading; + + /// Text to be displayed under [title]. + final Widget subtitle; + + /// Callback to be called onTap. + final VoidCallback? onTap; + + /// Whether to display a divider at the bottom. + final bool enabledDivider; + + @override + Widget build(BuildContext context) { + final colors = Zeta.of(context).colors; + + return DecoratedBox( + decoration: BoxDecoration( + color: colors.surfacePrimary, + border: enabledDivider + ? Border( + bottom: BorderSide(color: colors.borderDisabled), + ) + : null, + ), + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: onTap, + child: Padding( + padding: const EdgeInsets.only( + top: ZetaSpacing.xs, + bottom: ZetaSpacing.xs, + left: ZetaSpacing.m, + ), + child: Row( + children: [ + leading, + Flexible( + child: Padding( + padding: const EdgeInsets.only(left: ZetaSpacing.s), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + DefaultTextStyle( + style: ZetaTextStyles.bodyMedium.copyWith( + color: colors.textDefault, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + child: title, + ), + DefaultTextStyle( + style: ZetaTextStyles.bodySmall.copyWith( + color: colors.textSubtle, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + child: subtitle, + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(ObjectFlagProperty.has('onTap', onTap)) + ..add(DiagnosticsProperty('enabledDivider', enabledDivider)); + } +} diff --git a/lib/zeta_flutter.dart b/lib/zeta_flutter.dart index cb37ebfd..48a3c902 100644 --- a/lib/zeta_flutter.dart +++ b/lib/zeta_flutter.dart @@ -23,6 +23,7 @@ export 'src/components/buttons/fab.dart'; export 'src/components/buttons/icon_button.dart'; export 'src/components/checkbox/checkbox.dart'; export 'src/components/chips/chip.dart'; +export 'src/components/contact_item/contact_item.dart'; export 'src/components/date_input/date_input.dart'; export 'src/components/dial_pad/dial_pad.dart'; export 'src/components/dialog/dialog.dart'; From 80c81d64b4f0caa2e91ef1296bd6d0c00cf98a54 Mon Sep 17 00:00:00 2001 From: sd-athlon <163880004+sd-athlon@users.noreply.github.com> Date: Tue, 30 Apr 2024 14:59:08 +0300 Subject: [PATCH 6/7] feat: Chat Item (#37) * feat(main): Chat Item * [automated commit] lint format and import sort * remove hard coded avatar * [automated commit] lint format and import sort --------- Co-authored-by: github-actions --- example/lib/home.dart | 2 + .../pages/components/chat_item_example.dart | 61 +++ example/widgetbook/main.dart | 5 + .../components/chat_item_widgetbook.dart | 49 +++ lib/src/components/chat_item/chat_item.dart | 368 ++++++++++++++++++ lib/zeta_flutter.dart | 1 + pubspec.yaml | 1 + 7 files changed, 487 insertions(+) create mode 100644 example/lib/pages/components/chat_item_example.dart create mode 100644 example/widgetbook/pages/components/chat_item_widgetbook.dart create mode 100644 lib/src/components/chat_item/chat_item.dart diff --git a/example/lib/home.dart b/example/lib/home.dart index dd349a64..a3e4f41e 100644 --- a/example/lib/home.dart +++ b/example/lib/home.dart @@ -8,6 +8,7 @@ import 'package:zeta_example/pages/components/banner_example.dart'; import 'package:zeta_example/pages/components/bottom_sheet_example.dart'; import 'package:zeta_example/pages/components/breadcrumbs_example.dart'; import 'package:zeta_example/pages/components/button_example.dart'; +import 'package:zeta_example/pages/components/chat_item_example.dart'; import 'package:zeta_example/pages/components/checkbox_example.dart'; import 'package:zeta_example/pages/components/chip_example.dart'; import 'package:zeta_example/pages/components/contact_item_example.dart'; @@ -57,6 +58,7 @@ final List components = [ Component(BottomSheetExample.name, (context) => const BottomSheetExample()), Component(BreadCrumbsExample.name, (context) => const BreadCrumbsExample()), Component(ButtonExample.name, (context) => const ButtonExample()), + Component(ChatItemExample.name, (context) => const ChatItemExample()), Component(CheckBoxExample.name, (context) => const CheckBoxExample()), Component(ChipExample.name, (context) => const ChipExample()), Component(ContactItemExample.name, (context) => const ContactItemExample()), diff --git a/example/lib/pages/components/chat_item_example.dart b/example/lib/pages/components/chat_item_example.dart new file mode 100644 index 00000000..e124e765 --- /dev/null +++ b/example/lib/pages/components/chat_item_example.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:zeta_example/widgets.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +class ChatItemExample extends StatefulWidget { + static const String name = 'ChatItem'; + + const ChatItemExample({Key? key}) : super(key: key); + + @override + State createState() => _ChatItemExampleState(); +} + +class _ChatItemExampleState extends State { + @override + Widget build(BuildContext context) { + return ExampleScaffold( + name: 'Chat Item', + child: SingleChildScrollView( + child: Column( + children: [ + ZetaChatItem( + time: DateTime.now(), + enabledWarningIcon: true, + enabledNotificationIcon: true, + leading: const ZetaAvatar( + size: ZetaAvatarSize.l, + ), + count: 100, + onTap: () {}, + onDeleteTap: () {}, + onCallTap: () {}, + onMenuMoreTap: () {}, + onPttTap: () {}, + title: Text("Chat name ID"), + subtitle: Text( + "Dummy text to represent the first lines of most recent message dsadas dsa dsa ds dssd sd sdsd s ds"), + ), + Padding( + padding: const EdgeInsets.only(top: ZetaSpacing.b), + child: ZetaChatItem( + highlighted: true, + count: 99, + time: DateTime.now(), + onTap: () {}, + starred: true, + leading: const ZetaAvatar( + size: ZetaAvatarSize.l, + ), + title: Text("Chat name ID"), + subtitle: Text( + "Dummy text to represent the first lines of most recent message", + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/example/widgetbook/main.dart b/example/widgetbook/main.dart index 38719607..3b53bea0 100644 --- a/example/widgetbook/main.dart +++ b/example/widgetbook/main.dart @@ -11,6 +11,7 @@ import 'pages/components/banner_widgetbook.dart'; import 'pages/components/bottom_sheet_widgetbook.dart'; import 'pages/components/breadcrumbs_widgetbook.dart'; import 'pages/components/button_widgetbook.dart'; +import 'pages/components/chat_item_widgetbook.dart'; import 'pages/components/checkbox_widgetbook.dart'; import 'pages/components/chip_widgetbook.dart'; import 'pages/components/contact_item_widgetbook.dart'; @@ -83,6 +84,10 @@ class HotReload extends StatelessWidget { ], ), WidgetbookUseCase(name: 'Avatar', builder: (context) => avatarUseCase(context)), + WidgetbookUseCase( + name: 'Chat Item', + builder: (context) => chatItemWidgetBook(context), + ), WidgetbookUseCase(name: 'Checkbox', builder: (context) => checkboxUseCase(context)), WidgetbookUseCase(name: 'Contact Item', builder: (context) => contactItemUseCase(context)), WidgetbookComponent( diff --git a/example/widgetbook/pages/components/chat_item_widgetbook.dart b/example/widgetbook/pages/components/chat_item_widgetbook.dart new file mode 100644 index 00000000..7c55a1cb --- /dev/null +++ b/example/widgetbook/pages/components/chat_item_widgetbook.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:widgetbook/widgetbook.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +import '../../test/test_components.dart'; + +Widget chatItemWidgetBook(BuildContext context) { + final title = context.knobs.string(label: 'Title', initialValue: 'Chat name ID'); + + final subtitle = context.knobs.string( + label: 'Subtitle', + initialValue: 'Dummy text to represent the first lines of most recent message', + ); + + final count = context.knobs.int.input(label: 'Count', initialValue: 3); + + final enabledWarningIcon = context.knobs.boolean(label: 'Warning Icon', initialValue: false); + final enabledNotificationIcon = context.knobs.boolean(label: 'Notification Icon', initialValue: false); + final starred = context.knobs.boolean(label: 'Starred', initialValue: false); + + final enabledOnTap = context.knobs.boolean(label: 'Enabled Tap', initialValue: true); + final enabledOnDelete = context.knobs.boolean(label: 'Delete', initialValue: true); + + final enabledOnMenuMore = context.knobs.boolean(label: 'Menu More', initialValue: true); + + final enabledOnCall = context.knobs.boolean(label: 'Call', initialValue: true); + + final enabledOnPtt = context.knobs.boolean(label: 'Ptt', initialValue: true); + + return WidgetbookTestWidget( + widget: ZetaChatItem( + time: DateTime.now(), + enabledWarningIcon: enabledWarningIcon, + enabledNotificationIcon: enabledNotificationIcon, + count: count, + onTap: enabledOnTap ? () {} : null, + onDeleteTap: enabledOnDelete ? () {} : null, + onCallTap: enabledOnCall ? () {} : null, + onMenuMoreTap: enabledOnMenuMore ? () {} : null, + onPttTap: enabledOnPtt ? () {} : null, + starred: starred, + leading: const ZetaAvatar( + size: ZetaAvatarSize.l, + ), + title: Text(title), + subtitle: Text(subtitle), + ), + ); +} diff --git a/lib/src/components/chat_item/chat_item.dart b/lib/src/components/chat_item/chat_item.dart new file mode 100644 index 00000000..0732cad9 --- /dev/null +++ b/lib/src/components/chat_item/chat_item.dart @@ -0,0 +1,368 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:intl/intl.dart'; +import '../../../zeta_flutter.dart'; + +/// Chat item widget that can be dragged to reveal contextual actions. +class ZetaChatItem extends StatelessWidget { + /// Creates a [ZetaChatItem] + const ZetaChatItem({ + super.key, + this.highlighted = false, + this.time, + this.timeFormat, + required this.title, + required this.subtitle, + required this.leading, + this.enabledWarningIcon = false, + this.enabledNotificationIcon = false, + this.additionalIcons = const [], + this.count, + this.onTap, + this.starred = false, + this.onMenuMoreTap, + this.onCallTap, + this.onDeleteTap, + this.onPttTap, + }); + + /// Whether to apply different background color. + final bool highlighted; + + /// Normally the person name. + final Widget title; + + /// Normally the begining of the chat message. + final Widget subtitle; + + /// Normally [ZetaAvatar]. + final Widget leading; + + /// The time when the message is sent. It applies default date format - [timeFormat]. + final DateTime? time; + + /// The dafault date format. + final DateFormat? timeFormat; + + /// Whether to show warning icon. + final bool enabledWarningIcon; + + /// Whether to show notification icon. + final bool enabledNotificationIcon; + + /// Optional icons to be displayed on the top right corder next to warning and notification icons. + final List additionalIcons; + + /// Count displayed on the top right corder. + final int? count; + + /// Callback to call when tap on the list tile. + final VoidCallback? onTap; + + /// Whether the chat list is starred. + final bool starred; + + /// Callback for slidable action - menu more. + final VoidCallback? onMenuMoreTap; + + /// Callback for slidable action - call. + final VoidCallback? onCallTap; + + /// Callback for slidable action - delete. + final VoidCallback? onDeleteTap; + + /// Callback for slidable action - ptt. + final VoidCallback? onPttTap; + + DateFormat get _dateFormat => timeFormat ?? DateFormat('hh:mm a'); + String? get _count => count != null && count! > 99 ? '99+' : count?.toString(); + + double _getSlidableExtend({ + required int slidableActionsCount, + required double maxWidth, + }) { + if (slidableActionsCount == 0) return 0.5; + + final actionsExtend = slidableActionsCount * ZetaSpacing.x20; + final extend = actionsExtend / maxWidth; + + return extend > 1 ? 1 : extend; + } + + @override + Widget build(BuildContext context) { + final colors = Zeta.of(context).colors; + + final slidableActions = [ + if (onMenuMoreTap != null) + _ZetaSlidableAction( + onPressed: onMenuMoreTap, + backgroundColor: colors.purple.shade10, + foregroundColor: colors.purple.shade60, + icon: ZetaIcons.more_vertical_round, + ), + if (onCallTap != null) + _ZetaSlidableAction( + onPressed: onCallTap, + backgroundColor: colors.green.shade10, + foregroundColor: colors.positive, + icon: Icons.call, + ), + if (onPttTap != null) + _ZetaSlidableAction( + onPressed: onPttTap, + backgroundColor: colors.blue.shade10, + foregroundColor: colors.primary, + icon: ZetaIcons.ptt_round, + ), + if (onDeleteTap != null) + _ZetaSlidableAction( + onPressed: onDeleteTap, + backgroundColor: colors.red.shade10, + foregroundColor: colors.negative, + icon: ZetaIcons.delete_round, + ), + ]; + + return LayoutBuilder( + builder: (context, constraints) { + return Slidable( + enabled: slidableActions.isNotEmpty, + endActionPane: ActionPane( + extentRatio: _getSlidableExtend( + slidableActionsCount: slidableActions.length, + maxWidth: constraints.maxWidth, + ), + motion: const ScrollMotion(), + children: slidableActions, + ), + child: ColoredBox( + color: highlighted ? colors.blue.shade10 : colors.surfacePrimary, + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: onTap, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: ZetaSpacing.s, + vertical: ZetaSpacing.xs, + ), + child: Row( + children: [ + leading, + Flexible( + child: Padding( + padding: const EdgeInsets.only(left: ZetaSpacing.s), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + if (highlighted) + Container( + margin: const EdgeInsets.only( + right: ZetaSpacing.xxs, + ), + height: ZetaSpacing.x2, + width: ZetaSpacing.x2, + decoration: BoxDecoration( + color: colors.primary, + shape: BoxShape.circle, + ), + ), + Flexible( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: DefaultTextStyle( + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: (highlighted ? ZetaTextStyles.labelLarge : ZetaTextStyles.bodyMedium) + .copyWith( + color: colors.textDefault, + ), + child: title, + ), + ), + Row( + children: [ + if (time != null) + Text( + _dateFormat.format(time!), + style: ZetaTextStyles.bodyXSmall, + ), + IconTheme( + data: const IconThemeData( + size: ZetaSpacing.x4, + ), + child: Row( + children: [ + ...additionalIcons, + if (enabledWarningIcon) + Padding( + padding: const EdgeInsets.only( + left: ZetaSpacing.xxs, + ), + child: Icon( + ZetaIcons.info_round, + color: colors.cool.shade70, + ), + ), + if (enabledWarningIcon) + Padding( + padding: const EdgeInsets.only( + left: ZetaSpacing.xxs, + ), + child: Icon( + Icons.circle_notifications, + color: colors.negative, + ), + ), + if (_count != null) + Container( + margin: const EdgeInsets.only( + left: ZetaSpacing.xxs, + ), + padding: const EdgeInsets.symmetric( + horizontal: ZetaSpacing.x2, + ), + decoration: BoxDecoration( + color: colors.primary, + borderRadius: ZetaRadius.full, + ), + child: Text( + _count!, + style: ZetaTextStyles.labelSmall.copyWith( + color: colors.textInverse, + ), + ), + ), + ], + ), + ), + ], + ), + ], + ), + ), + ], + ), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Flexible( + child: DefaultTextStyle( + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: ZetaTextStyles.bodySmall.copyWith( + color: colors.textSubtle, + ), + child: subtitle, + ), + ), + Padding( + padding: const EdgeInsets.only( + left: ZetaSpacing.xxs, + ), + child: Icon( + starred ? ZetaIcons.star_sharp : ZetaIcons.star_outline_sharp, + color: starred ? colors.yellow.shade60 : null, + ), + ), + ], + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ), + ); + }, + ); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('highlighted', highlighted)) + ..add(DiagnosticsProperty('time', time)) + ..add(DiagnosticsProperty('timeFormat', timeFormat)) + ..add(DiagnosticsProperty('enabledWarningIcon', enabledWarningIcon)) + ..add( + DiagnosticsProperty( + 'enabledNotificationIcon', + enabledNotificationIcon, + ), + ) + ..add(IntProperty('count', count)) + ..add(ObjectFlagProperty.has('onTap', onTap)) + ..add(DiagnosticsProperty('starred', starred)) + ..add( + ObjectFlagProperty.has('onMenuMoreTap', onMenuMoreTap), + ) + ..add(ObjectFlagProperty.has('onCallTap', onCallTap)) + ..add(ObjectFlagProperty.has('onDeleteTap', onDeleteTap)) + ..add(ObjectFlagProperty.has('onPttTap', onPttTap)); + } +} + +class _ZetaSlidableAction extends StatelessWidget { + const _ZetaSlidableAction({ + required this.onPressed, + required this.icon, + required this.foregroundColor, + required this.backgroundColor, + }); + + final VoidCallback? onPressed; + final IconData icon; + final Color foregroundColor; + final Color backgroundColor; + + @override + Widget build(BuildContext context) { + return Expanded( + child: SizedBox.expand( + child: Padding( + padding: const EdgeInsets.only(left: ZetaSpacing.xxs), + child: IconButton( + onPressed: () => onPressed, + style: IconButton.styleFrom( + backgroundColor: backgroundColor, + foregroundColor: foregroundColor, + shape: const RoundedRectangleBorder( + borderRadius: ZetaRadius.minimal, + ), + side: BorderSide.none, + ), + icon: Icon( + icon, + color: foregroundColor, + size: ZetaSpacing.x8, + ), + ), + ), + ), + ); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(ObjectFlagProperty.has('onPressed', onPressed)) + ..add(DiagnosticsProperty('icon', icon)) + ..add(ColorProperty('foregroundColor', foregroundColor)) + ..add(ColorProperty('backgroundColor', backgroundColor)) + ..add(DiagnosticsProperty('icon', icon)); + } +} diff --git a/lib/zeta_flutter.dart b/lib/zeta_flutter.dart index 48a3c902..70b9e30c 100644 --- a/lib/zeta_flutter.dart +++ b/lib/zeta_flutter.dart @@ -21,6 +21,7 @@ export 'src/components/buttons/button_group.dart'; export 'src/components/buttons/button_style.dart'; export 'src/components/buttons/fab.dart'; export 'src/components/buttons/icon_button.dart'; +export 'src/components/chat_item/chat_item.dart'; export 'src/components/checkbox/checkbox.dart'; export 'src/components/chips/chip.dart'; export 'src/components/contact_item/contact_item.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 52c07e53..e0ed7648 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -23,6 +23,7 @@ environment: dependencies: flutter: sdk: flutter + flutter_slidable: ^3.1.0 intl: ^0.19.0 mask_text_input_formatter: ^2.9.0 From 7872fb8bd048a9b6916c40ffb3b27f344eb669a7 Mon Sep 17 00:00:00 2001 From: atanasyordanov21 <63714308+atanasyordanov21@users.noreply.github.com> Date: Tue, 30 Apr 2024 19:15:41 +0300 Subject: [PATCH 7/7] fix: Update banners (#41) * Component dialog (#18) * create showZetaDialog * finished dialog for DeviceType.mobilePortrait * dialog variant for bigger screens * create widgetbook; add Zeta parameter, also in ZetaButton * useRootNavigator: false * feat(main): AppBar (#19) * feat(main): AppBar * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * Component dialog (#22) * chore: update contributing * fix: Fix button group immutability (#1) * Fix errors * fix copywith function * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * [automated commit] lint format and import sort * update on-main to push to firebase (#3) * ci: move firebase to flutter main host for qa (#4) * feat: Add List Item (#5) * feat: Add List Item * [automated commit] lint format and import sort --------- Co-authored-by: Simeon Dimitrov Co-authored-by: github-actions * fix(main): ListItem disabled color (#8) * fix(main): ListItem disabled color * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * feat : Dropdown menu (#7) * Create dropdown * Add sizes * create stoyrybook and add size * Fix errrs and respond to comments * Fix issues * [automated commit] lint format and import sort * Alter isLarge * Fix spacing * [automated commit] lint format and import sort * Alter leading styles * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * Component ZetaSwitch (#6) * create ZetaSwitch * ZetaSwitch using MaterialSwitch * widgetbook for ZetaSwitch * remove hover; fix initState * add showHover parameter * add comments 'Zeta change' in material_switch.dart * remove size parameter and factory constructors * fix example and widgetbook * Component Zeta Radio Button (#9) * create component Zeta Radio Button * remove hover color * fix label line height * feat(main): SnackBar (#10) * add snackbar example * Add snackbar widgetbook * feat(main): SnackBar * [automated commit] lint format and import sort * remove view icon * Add view icon * Add widgetbook icon helper * [automated commit] lint format and import sort * fix alphabetical imports * Fix delete and error background color --------- Co-authored-by: github-actions * feat(main): Tabs (#11) * feat(main): Tabs * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * chore: Update text styles (#13) * fix: switch on web (#14) * Component date input (#12) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * create showZetaDialog * finished dialog for DeviceType.mobilePortrait * Component date input (#16) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * fix Typography of Date Input * restore * remove text line height * dialog variant for bigger screens * create widgetbook; add Zeta parameter, also in ZetaButton * useRootNavigator: false * add iconKnob in Dialog widgetbook * final iconData = iconKnob --------- Co-authored-by: Luke Co-authored-by: ahmed-osman3 <99483750+ahmed-osman3@users.noreply.github.com> Co-authored-by: Osman Co-authored-by: github-actions Co-authored-by: Luke Walton Co-authored-by: Simeon Dimitrov Co-authored-by: sd-athlon <163880004+sd-athlon@users.noreply.github.com> * Component phone input (#21) * chore: update contributing * fix: Fix button group immutability (#1) * Fix errors * fix copywith function * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * [automated commit] lint format and import sort * update on-main to push to firebase (#3) * ci: move firebase to flutter main host for qa (#4) * feat: Add List Item (#5) * feat: Add List Item * [automated commit] lint format and import sort --------- Co-authored-by: Simeon Dimitrov Co-authored-by: github-actions * fix(main): ListItem disabled color (#8) * fix(main): ListItem disabled color * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * feat : Dropdown menu (#7) * Create dropdown * Add sizes * create stoyrybook and add size * Fix errrs and respond to comments * Fix issues * [automated commit] lint format and import sort * Alter isLarge * Fix spacing * [automated commit] lint format and import sort * Alter leading styles * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * Component ZetaSwitch (#6) * create ZetaSwitch * ZetaSwitch using MaterialSwitch * widgetbook for ZetaSwitch * remove hover; fix initState * add showHover parameter * add comments 'Zeta change' in material_switch.dart * remove size parameter and factory constructors * fix example and widgetbook * Component Zeta Radio Button (#9) * create component Zeta Radio Button * remove hover color * fix label line height * feat(main): SnackBar (#10) * add snackbar example * Add snackbar widgetbook * feat(main): SnackBar * [automated commit] lint format and import sort * remove view icon * Add view icon * Add widgetbook icon helper * [automated commit] lint format and import sort * fix alphabetical imports * Fix delete and error background color --------- Co-authored-by: github-actions * feat(main): Tabs (#11) * feat(main): Tabs * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * chore: Update text styles (#13) * fix: switch on web (#14) * Component date input (#12) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * Component date input (#16) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * fix Typography of Date Input * restore * remove text line height * ZetaPhoneInput initial commit * complete ZetaPhoneInput; add flags * create phoneInputUseCase in Widgetbook * refactor phone input to use native alert dialog --------- Co-authored-by: Luke Co-authored-by: ahmed-osman3 <99483750+ahmed-osman3@users.noreply.github.com> Co-authored-by: Osman Co-authored-by: github-actions Co-authored-by: Luke Walton Co-authored-by: Simeon Dimitrov Co-authored-by: sd-athlon <163880004+sd-athlon@users.noreply.github.com> * restore main.dart in example (#23) * Component phone input (#24) * chore: update contributing * fix: Fix button group immutability (#1) * Fix errors * fix copywith function * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * [automated commit] lint format and import sort * update on-main to push to firebase (#3) * ci: move firebase to flutter main host for qa (#4) * feat: Add List Item (#5) * feat: Add List Item * [automated commit] lint format and import sort --------- Co-authored-by: Simeon Dimitrov Co-authored-by: github-actions * fix(main): ListItem disabled color (#8) * fix(main): ListItem disabled color * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * feat : Dropdown menu (#7) * Create dropdown * Add sizes * create stoyrybook and add size * Fix errrs and respond to comments * Fix issues * [automated commit] lint format and import sort * Alter isLarge * Fix spacing * [automated commit] lint format and import sort * Alter leading styles * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * Component ZetaSwitch (#6) * create ZetaSwitch * ZetaSwitch using MaterialSwitch * widgetbook for ZetaSwitch * remove hover; fix initState * add showHover parameter * add comments 'Zeta change' in material_switch.dart * remove size parameter and factory constructors * fix example and widgetbook * Component Zeta Radio Button (#9) * create component Zeta Radio Button * remove hover color * fix label line height * feat(main): SnackBar (#10) * add snackbar example * Add snackbar widgetbook * feat(main): SnackBar * [automated commit] lint format and import sort * remove view icon * Add view icon * Add widgetbook icon helper * [automated commit] lint format and import sort * fix alphabetical imports * Fix delete and error background color --------- Co-authored-by: github-actions * feat(main): Tabs (#11) * feat(main): Tabs * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * chore: Update text styles (#13) * fix: switch on web (#14) * Component date input (#12) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * Component date input (#16) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * fix Typography of Date Input * restore * remove text line height * ZetaPhoneInput initial commit * complete ZetaPhoneInput; add flags * create phoneInputUseCase in Widgetbook * refactor phone input to use native alert dialog * don't use root navigator in widgetbook --------- Co-authored-by: Luke Co-authored-by: ahmed-osman3 <99483750+ahmed-osman3@users.noreply.github.com> Co-authored-by: Osman Co-authored-by: github-actions Co-authored-by: Luke Walton Co-authored-by: Simeon Dimitrov Co-authored-by: sd-athlon <163880004+sd-athlon@users.noreply.github.com> * Component phone input (#25) * chore: update contributing * fix: Fix button group immutability (#1) * Fix errors * fix copywith function * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * [automated commit] lint format and import sort * update on-main to push to firebase (#3) * ci: move firebase to flutter main host for qa (#4) * feat: Add List Item (#5) * feat: Add List Item * [automated commit] lint format and import sort --------- Co-authored-by: Simeon Dimitrov Co-authored-by: github-actions * fix(main): ListItem disabled color (#8) * fix(main): ListItem disabled color * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * feat : Dropdown menu (#7) * Create dropdown * Add sizes * create stoyrybook and add size * Fix errrs and respond to comments * Fix issues * [automated commit] lint format and import sort * Alter isLarge * Fix spacing * [automated commit] lint format and import sort * Alter leading styles * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * Component ZetaSwitch (#6) * create ZetaSwitch * ZetaSwitch using MaterialSwitch * widgetbook for ZetaSwitch * remove hover; fix initState * add showHover parameter * add comments 'Zeta change' in material_switch.dart * remove size parameter and factory constructors * fix example and widgetbook * Component Zeta Radio Button (#9) * create component Zeta Radio Button * remove hover color * fix label line height * feat(main): SnackBar (#10) * add snackbar example * Add snackbar widgetbook * feat(main): SnackBar * [automated commit] lint format and import sort * remove view icon * Add view icon * Add widgetbook icon helper * [automated commit] lint format and import sort * fix alphabetical imports * Fix delete and error background color --------- Co-authored-by: github-actions * feat(main): Tabs (#11) * feat(main): Tabs * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * chore: Update text styles (#13) * fix: switch on web (#14) * Component date input (#12) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * Component date input (#16) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * fix Typography of Date Input * restore * remove text line height * ZetaPhoneInput initial commit * complete ZetaPhoneInput; add flags * create phoneInputUseCase in Widgetbook * refactor phone input to use native alert dialog * don't use root navigator in widgetbook * pass parameter useRootNavigator * restore some missing countries in the list --------- Co-authored-by: Luke Co-authored-by: ahmed-osman3 <99483750+ahmed-osman3@users.noreply.github.com> Co-authored-by: Osman Co-authored-by: github-actions Co-authored-by: Luke Walton Co-authored-by: Simeon Dimitrov Co-authored-by: sd-athlon <163880004+sd-athlon@users.noreply.github.com> * Navigation rail (#27) * create NavigationRail * restore main.dart in example * navigation rail example * create widgetbook * add SafeArea; rename parameter wordWrap * add MouseRegion & SelectionContainer.disabled * Component tooltip (#31) * create tooltip * create Widgetbook for ZetaTooltip * add LayoutBuilder * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * feat(main): Segmented control (#26) * feat(main): AppBar * Add segmented control * [automated commit] lint format and import sort * Fix mouse cursor, disable selection container and tap area * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * Component phone input (#28) * chore: update contributing * fix: Fix button group immutability (#1) * Fix errors * fix copywith function * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * [automated commit] lint format and import sort * update on-main to push to firebase (#3) * ci: move firebase to flutter main host for qa (#4) * feat: Add List Item (#5) * feat: Add List Item * [automated commit] lint format and import sort --------- Co-authored-by: Simeon Dimitrov Co-authored-by: github-actions * fix(main): ListItem disabled color (#8) * fix(main): ListItem disabled color * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * feat : Dropdown menu (#7) * Create dropdown * Add sizes * create stoyrybook and add size * Fix errrs and respond to comments * Fix issues * [automated commit] lint format and import sort * Alter isLarge * Fix spacing * [automated commit] lint format and import sort * Alter leading styles * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * Component ZetaSwitch (#6) * create ZetaSwitch * ZetaSwitch using MaterialSwitch * widgetbook for ZetaSwitch * remove hover; fix initState * add showHover parameter * add comments 'Zeta change' in material_switch.dart * remove size parameter and factory constructors * fix example and widgetbook * Component Zeta Radio Button (#9) * create component Zeta Radio Button * remove hover color * fix label line height * feat(main): SnackBar (#10) * add snackbar example * Add snackbar widgetbook * feat(main): SnackBar * [automated commit] lint format and import sort * remove view icon * Add view icon * Add widgetbook icon helper * [automated commit] lint format and import sort * fix alphabetical imports * Fix delete and error background color --------- Co-authored-by: github-actions * feat(main): Tabs (#11) * feat(main): Tabs * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * chore: Update text styles (#13) * fix: switch on web (#14) * Component date input (#12) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * Component date input (#16) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * fix Typography of Date Input * restore * remove text line height * ZetaPhoneInput initial commit * complete ZetaPhoneInput; add flags * create phoneInputUseCase in Widgetbook * refactor phone input to use native alert dialog * don't use root navigator in widgetbook * pass parameter useRootNavigator * restore some missing countries in the list * countries search * add searchHint * fix comments --------- Co-authored-by: Luke Co-authored-by: ahmed-osman3 <99483750+ahmed-osman3@users.noreply.github.com> Co-authored-by: Osman Co-authored-by: github-actions Co-authored-by: Luke Walton Co-authored-by: Simeon Dimitrov Co-authored-by: sd-athlon <163880004+sd-athlon@users.noreply.github.com> * use ZetaWidgetSize from enums.dart (#32) * Component tooltip (#33) * create tooltip * create Widgetbook for ZetaTooltip * add LayoutBuilder * [automated commit] lint format and import sort * fix text overflow when there is horizontal arrow and maxWidth is null --------- Co-authored-by: github-actions * Component search bar (#29) * create ZetaSearchBar with examples * create Widgetbook for ZetaSearchBar * rename callback * full instead of stadium shape * default hint Search * add parameters showLeadingIcon & showSpeechToText * fix comments * use ZetaWidgetBorder & ZetaWidgetSize from enums.dart * Component phone input (#34) * chore: update contributing * fix: Fix button group immutability (#1) * Fix errors * fix copywith function * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * [automated commit] lint format and import sort * update on-main to push to firebase (#3) * ci: move firebase to flutter main host for qa (#4) * feat: Add List Item (#5) * feat: Add List Item * [automated commit] lint format and import sort --------- Co-authored-by: Simeon Dimitrov Co-authored-by: github-actions * fix(main): ListItem disabled color (#8) * fix(main): ListItem disabled color * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * feat : Dropdown menu (#7) * Create dropdown * Add sizes * create stoyrybook and add size * Fix errrs and respond to comments * Fix issues * [automated commit] lint format and import sort * Alter isLarge * Fix spacing * [automated commit] lint format and import sort * Alter leading styles * [automated commit] lint format and import sort --------- Co-authored-by: Osman Co-authored-by: github-actions * Component ZetaSwitch (#6) * create ZetaSwitch * ZetaSwitch using MaterialSwitch * widgetbook for ZetaSwitch * remove hover; fix initState * add showHover parameter * add comments 'Zeta change' in material_switch.dart * remove size parameter and factory constructors * fix example and widgetbook * Component Zeta Radio Button (#9) * create component Zeta Radio Button * remove hover color * fix label line height * feat(main): SnackBar (#10) * add snackbar example * Add snackbar widgetbook * feat(main): SnackBar * [automated commit] lint format and import sort * remove view icon * Add view icon * Add widgetbook icon helper * [automated commit] lint format and import sort * fix alphabetical imports * Fix delete and error background color --------- Co-authored-by: github-actions * feat(main): Tabs (#11) * feat(main): Tabs * [automated commit] lint format and import sort --------- Co-authored-by: github-actions * chore: Update text styles (#13) * fix: switch on web (#14) * Component date input (#12) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * Component date input (#16) * create ZetaDateInput * create different ZetaDateInput variants * fix show error style * date validation and input mask; documentation for ZetaDateInput properties * create widgetbook * changes according to comments * fix Typography of Date Input * restore * remove text line height * ZetaPhoneInput initial commit * complete ZetaPhoneInput; add flags * create phoneInputUseCase in Widgetbook * refactor phone input to use native alert dialog * don't use root navigator in widgetbook * pass parameter useRootNavigator * restore some missing countries in the list * countries search * add searchHint * fix comments * use ZetaSearchBar in CountriesDialog --------- Co-authored-by: Luke Co-authored-by: ahmed-osman3 <99483750+ahmed-osman3@users.noreply.github.com> Co-authored-by: Osman Co-authored-by: github-actions Co-authored-by: Luke Walton Co-authored-by: Simeon Dimitrov Co-authored-by: sd-athlon <163880004+sd-athlon@users.noreply.github.com> * Select input (#35) * create ZetaSelectInput * add extra parameters; improve * leading icon; error handling in example * _MenuPosition * widgetbook * restore all menu items on each open * fix setState * initialize _menuSize with Size.zero * Filter Selection (#36) * Filter Selection * use divide * ZetaScreenHeaderBar (#39) * fix & update banners * refactor Priority Pill * local size in ZetaPriorityPill * add Padding around all UseCase in Widgetbook * ZetaPriorityPillSize * remove ZetaWorkcloudIndicator --------- Co-authored-by: Luke Co-authored-by: github-actions Co-authored-by: ahmed-osman3 <99483750+ahmed-osman3@users.noreply.github.com> Co-authored-by: Osman Co-authored-by: Luke Walton Co-authored-by: Simeon Dimitrov Co-authored-by: sd-athlon <163880004+sd-athlon@users.noreply.github.com> --- .../lib/pages/components/badges_example.dart | 105 +++++------ example/test/priority_pill_test.dart | 2 +- example/test/workcloud_indicator_test.dart | 15 -- example/widgetbook/main.dart | 2 - .../pages/components/badges_widgetbook.dart | 136 ++++++++------- lib/src/components/badges/badge.dart | 2 +- lib/src/components/badges/priority_pill.dart | 124 ++++++++++--- lib/src/components/badges/status_label.dart | 2 +- .../badges/workcloud_indicator.dart | 164 ------------------ lib/src/utils/enums.dart | 2 +- lib/zeta_flutter.dart | 1 - 11 files changed, 224 insertions(+), 331 deletions(-) delete mode 100644 example/test/workcloud_indicator_test.dart delete mode 100644 lib/src/components/badges/workcloud_indicator.dart diff --git a/example/lib/pages/components/badges_example.dart b/example/lib/pages/components/badges_example.dart index 76d28534..839d6e1f 100644 --- a/example/lib/pages/components/badges_example.dart +++ b/example/lib/pages/components/badges_example.dart @@ -20,14 +20,14 @@ class BadgesExample extends StatelessWidget { _StatusLabel(), _DividingText('Priority Pill'), _PriorityPill(), + const SizedBox(height: ZetaSpacing.l), + _PriorityPill(size: ZetaPriorityPillSize.small), _DividingText('Badge'), _Badge(), _DividingText('Indicators'), _Indicators(), _DividingText('Tags'), _Tags(), - _DividingText('WorkCloud indicators'), - _WorkcloudIndicators(), ], ), ), @@ -56,8 +56,10 @@ class _StatusLabel extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, children: [ + ZetaStatusLabel(label: 'Label', status: type), + ZetaStatusLabel(label: 'Label', status: type, customIcon: ZetaIcons.star_round), ZetaStatusLabel(label: 'Label', status: type, rounded: false), - ZetaStatusLabel(label: 'Label', status: type, rounded: true), + ZetaStatusLabel(label: 'Label', status: type, rounded: false, customIcon: ZetaIcons.star_sharp), ], ); } @@ -77,16 +79,45 @@ class _StatusLabel extends StatelessWidget { } class _PriorityPill extends StatelessWidget { - const _PriorityPill(); + const _PriorityPill({ + this.size = ZetaPriorityPillSize.large, + }); + + final ZetaPriorityPillSize size; @override Widget build(BuildContext context) { return Row( - mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - ZetaPriorityPill(index: 1000, priority: 'Rounded', rounded: true), - ZetaPriorityPill(index: 2, priority: 'Sharp', rounded: false), - ].divide(const SizedBox.square(dimension: ZetaSpacing.m)).toList(), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ZetaPriorityPill(size: size, isBadge: true), + ZetaPriorityPill(size: size, isBadge: true, type: ZetaPriorityPillType.high), + ZetaPriorityPill(size: size, isBadge: true, type: ZetaPriorityPillType.medium), + ZetaPriorityPill(size: size, isBadge: true, type: ZetaPriorityPillType.low), + ].divide(const SizedBox.square(dimension: ZetaSpacing.m)).toList(), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ZetaPriorityPill(size: size), + ZetaPriorityPill(size: size, type: ZetaPriorityPillType.high), + ZetaPriorityPill(size: size, type: ZetaPriorityPillType.medium), + ZetaPriorityPill(size: size, type: ZetaPriorityPillType.low), + ].divide(const SizedBox.square(dimension: ZetaSpacing.m)).toList(), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ZetaPriorityPill(size: size, rounded: false), + ZetaPriorityPill(size: size, rounded: false, type: ZetaPriorityPillType.high), + ZetaPriorityPill(size: size, rounded: false, type: ZetaPriorityPillType.medium), + ZetaPriorityPill(size: size, rounded: false, type: ZetaPriorityPillType.low), + ].divide(const SizedBox.square(dimension: ZetaSpacing.m)).toList(), + ), + ], ); } } @@ -99,8 +130,8 @@ class _Badge extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, children: [ - ZetaBadge(label: 'Label', status: type, rounded: false), ZetaBadge(label: 'Label', status: type), + ZetaBadge(label: 'Label', status: type, rounded: false), ], ); } @@ -130,7 +161,8 @@ class _Indicators extends StatelessWidget { Column( children: [ Text( - 'ZetaIndicator.icon', + 'ZetaIndicator\nicon', + textAlign: TextAlign.center, style: TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 15), @@ -164,7 +196,11 @@ class _Indicators extends StatelessWidget { const SizedBox.square(dimension: ZetaSpacing.xl), Column( children: [ - Text('ZetaIndicator.notification', style: TextStyle(fontWeight: FontWeight.bold)), + Text( + 'ZetaIndicator\nnotification', + textAlign: TextAlign.center, + style: TextStyle(fontWeight: FontWeight.bold), + ), const SizedBox(height: 15), Row( mainAxisSize: MainAxisSize.min, @@ -216,50 +252,3 @@ class _Tags extends StatelessWidget { ); } } - -class _WorkcloudIndicators extends StatelessWidget { - const _WorkcloudIndicators(); - - Widget workcloudIndicatorExampleRow(ZetaWorkcloudIndicatorType type) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - ZetaWorkcloudIndicator( - priorityType: type, - prioritySize: ZetaWidgetSize.large, - icon: ZetaIcons.star_half_round, - label: 'Label', - ), - ZetaWorkcloudIndicator( - prioritySize: ZetaWidgetSize.medium, - index: '14', - priorityType: type, - label: 'Label!', - ), - ZetaWorkcloudIndicator( - priorityType: type, - ), - ], - ); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - SizedBox(height: 30), - Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [Text('Medium'), Text('Small'), Text('X-Small')], - ), - ...List.generate(10, (index) { - return workcloudIndicatorExampleRow(ZetaWorkcloudIndicatorType.values[index]); - }), - ].divide(const SizedBox.square(dimension: ZetaSpacing.s)).toList(), - ).paddingAll(ZetaSpacing.m) - ], - ); - } -} diff --git a/example/test/priority_pill_test.dart b/example/test/priority_pill_test.dart index b2bd6557..6c24544a 100644 --- a/example/test/priority_pill_test.dart +++ b/example/test/priority_pill_test.dart @@ -9,7 +9,7 @@ void main() { TestWidget( widget: ZetaPriorityPill( priority: 'High', - index: 2, + index: '2', ), ), ); diff --git a/example/test/workcloud_indicator_test.dart b/example/test/workcloud_indicator_test.dart deleted file mode 100644 index 5d7ee833..00000000 --- a/example/test/workcloud_indicator_test.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:zeta_flutter/zeta_flutter.dart'; - -import 'test_components.dart'; - -void main() { - testWidgets('ZetaWorkcloud creates priority pill', (WidgetTester tester) async { - await tester.pumpWidget( - TestWidget( - widget: ZetaWorkcloudIndicator(index: '1'), - ), - ); - expect(find.text('1'), findsOneWidget); - }); -} diff --git a/example/widgetbook/main.dart b/example/widgetbook/main.dart index 3b53bea0..1f9b73d1 100644 --- a/example/widgetbook/main.dart +++ b/example/widgetbook/main.dart @@ -79,8 +79,6 @@ class HotReload extends StatelessWidget { WidgetbookUseCase(name: 'Badge', builder: (context) => badgeUseCase(context)), WidgetbookUseCase(name: 'Indicators', builder: (context) => indicatorsUseCase(context)), WidgetbookUseCase(name: 'Tags', builder: (context) => tagsUseCase(context)), - WidgetbookUseCase( - name: 'Workcloud Indicators', builder: (context) => workcloudIndicatorsUseCase(context)), ], ), WidgetbookUseCase(name: 'Avatar', builder: (context) => avatarUseCase(context)), diff --git a/example/widgetbook/pages/components/badges_widgetbook.dart b/example/widgetbook/pages/components/badges_widgetbook.dart index 4b17f1bd..3585d9d1 100644 --- a/example/widgetbook/pages/components/badges_widgetbook.dart +++ b/example/widgetbook/pages/components/badges_widgetbook.dart @@ -9,35 +9,55 @@ Widget statusLabelUseCase(BuildContext context) { final bool rounded = roundedKnob(context); return WidgetbookTestWidget( - widget: ZetaStatusLabel( - label: context.knobs.string(label: 'Label', initialValue: 'Label'), - rounded: rounded, - status: context.knobs.list( - label: 'Status', - labelBuilder: enumLabelBuilder, - options: ZetaWidgetStatus.values, + widget: Padding( + padding: const EdgeInsets.all(ZetaSpacing.m), + child: ZetaStatusLabel( + label: context.knobs.string(label: 'Label', initialValue: 'Label'), + rounded: rounded, + status: context.knobs.list( + label: 'Status', + labelBuilder: enumLabelBuilder, + options: ZetaWidgetStatus.values, + ), + customIcon: iconKnob(context, rounded: rounded), ), - customIcon: iconKnob(context, rounded: rounded), ), ); } Widget priorityPillUseCase(BuildContext context) => WidgetbookTestWidget( - widget: ZetaPriorityPill( - index: context.knobs.int.slider(label: 'Index'), - priority: context.knobs.string(label: 'Priority', initialValue: 'Priority'), - rounded: roundedKnob(context), + widget: Padding( + padding: const EdgeInsets.all(ZetaSpacing.m), + child: ZetaPriorityPill( + index: context.knobs.string(label: 'Index'), + priority: context.knobs.string(label: 'Label'), + size: context.knobs.list( + label: 'Size', + options: ZetaPriorityPillSize.values, + labelBuilder: (value) => value.name.capitalize(), + ), + type: context.knobs.list( + label: 'Priority', + options: ZetaPriorityPillType.values, + labelBuilder: (value) => value.name.capitalize(), + ), + rounded: context.knobs.boolean(label: 'Rounded', initialValue: true), + isBadge: context.knobs.boolean(label: 'Badge'), + ), ), ); Widget badgeUseCase(BuildContext context) => WidgetbookTestWidget( - widget: ZetaBadge( - label: context.knobs.string(label: 'Label', initialValue: 'Label'), - rounded: roundedKnob(context), - status: context.knobs.list( - label: 'Status', - options: ZetaWidgetStatus.values, - labelBuilder: enumLabelBuilder, + widget: Padding( + padding: const EdgeInsets.all(ZetaSpacing.m), + child: ZetaBadge( + label: context.knobs.string(label: 'Label', initialValue: 'Label'), + rounded: roundedKnob(context), + status: context.knobs.list( + label: 'Status', + options: ZetaWidgetStatus.values, + labelBuilder: enumLabelBuilder, + ), ), ), ); @@ -46,59 +66,43 @@ Widget indicatorsUseCase(BuildContext context) { final bool rounded = roundedKnob(context); return WidgetbookTestWidget( - widget: ZetaIndicator( - type: context.knobs.list( - label: 'Type', - options: ZetaIndicatorType.values, - labelBuilder: enumLabelBuilder, - ), - icon: iconKnob(context, rounded: rounded), - inverse: context.knobs.boolean(label: 'Inverse Border'), - size: context.knobs.list( - label: 'Size', - labelBuilder: enumLabelBuilder, - options: ZetaWidgetSize.values, + widget: Padding( + padding: const EdgeInsets.all(ZetaSpacing.m), + child: ZetaIndicator( + type: context.knobs.list( + label: 'Type', + options: ZetaIndicatorType.values, + labelBuilder: enumLabelBuilder, + ), + icon: iconKnob(context, rounded: rounded), + inverse: context.knobs.boolean(label: 'Inverse Border'), + size: context.knobs.list( + label: 'Size', + labelBuilder: enumLabelBuilder, + options: ZetaWidgetSize.values, + ), + value: context.knobs.int.slider(label: 'Value'), ), - value: context.knobs.int.slider(label: 'Value'), ), ); } Widget tagsUseCase(BuildContext context) => WidgetbookTestWidget( - widget: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ZetaTag( - label: context.knobs.string(label: 'Label', initialValue: 'Tag'), - rounded: roundedKnob(context), - direction: context.knobs.list( - label: 'Direction', - options: ZetaTagDirection.values, - labelBuilder: enumLabelBuilder, + widget: Padding( + padding: const EdgeInsets.all(ZetaSpacing.m), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ZetaTag( + label: context.knobs.string(label: 'Label', initialValue: 'Tag'), + rounded: roundedKnob(context), + direction: context.knobs.list( + label: 'Direction', + options: ZetaTagDirection.values, + labelBuilder: enumLabelBuilder, + ), ), - ), - ], + ], + ), ), ); - -Widget workcloudIndicatorsUseCase(BuildContext context) { - final bool rounded = roundedKnob(context); - - return WidgetbookTestWidget( - widget: ZetaWorkcloudIndicator( - index: context.knobs.string(label: 'Index', initialValue: '1'), - label: context.knobs.string(label: 'Label', initialValue: 'Label'), - prioritySize: context.knobs.list( - label: 'Size', - labelBuilder: enumLabelBuilder, - options: ZetaWidgetSize.values, - ), - priorityType: context.knobs.list( - label: 'Type', - labelBuilder: enumLabelBuilder, - options: ZetaWorkcloudIndicatorType.values, - ), - icon: iconKnob(context, rounded: rounded, nullable: true), - ), - ); -} diff --git a/lib/src/components/badges/badge.dart b/lib/src/components/badges/badge.dart index b4fa87d8..f32419b5 100644 --- a/lib/src/components/badges/badge.dart +++ b/lib/src/components/badges/badge.dart @@ -36,7 +36,7 @@ class ZetaBadge extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: ZetaSpacing.x1, vertical: ZetaSpacing.x0_5), decoration: BoxDecoration( color: backgroundColor, - borderRadius: rounded ? ZetaRadius.minimal : ZetaRadius.minimal, + borderRadius: rounded ? ZetaRadius.minimal : ZetaRadius.none, ), child: Text( label, diff --git a/lib/src/components/badges/priority_pill.dart b/lib/src/components/badges/priority_pill.dart index 8e027667..6d5974af 100644 --- a/lib/src/components/badges/priority_pill.dart +++ b/lib/src/components/badges/priority_pill.dart @@ -3,15 +3,58 @@ import 'package:flutter/material.dart'; import '../../../zeta_flutter.dart'; -///Zeta Priority Pill. +/// The type of [ZetaPriorityPill]. +enum ZetaPriorityPillType { + /// sets the color to a shade of red + urgent, + + /// sets the color to a shade of orange + high, + + /// sets the color to a shade of blue + medium, + + /// sets the color to a shade of green + low, +} + +/// The size of [ZetaPriorityPill]. +enum ZetaPriorityPillSize { + /// large + large, + + /// small + small, +} + +extension on ZetaPriorityPillType { + ZetaColorSwatch color(BuildContext context) { + final colors = Zeta.of(context).colors; + switch (this) { + case ZetaPriorityPillType.urgent: + return colors.red; + case ZetaPriorityPillType.high: + return colors.orange; + case ZetaPriorityPillType.medium: + return colors.blue; + case ZetaPriorityPillType.low: + return colors.green; + } + } +} + +/// Zeta Priority Pill. /// /// This badge is used to indicate the order of importance. class ZetaPriorityPill extends StatelessWidget { ///Constructs [ZetaPriorityPill] const ZetaPriorityPill({ - required this.index, - required this.priority, + this.index, + this.priority, this.rounded = true, + this.isBadge = false, + this.type = ZetaPriorityPillType.urgent, + this.size = ZetaPriorityPillSize.large, super.key, }); @@ -19,43 +62,79 @@ class ZetaPriorityPill extends StatelessWidget { final bool rounded; /// Leading number in component. - final int index; + final String? index; /// Text in main part of component. - final String priority; + final String? priority; + + /// Indicates if it is badge or lozenge. + /// + /// Default is `false` (lozenge). + final bool isBadge; + + /// The type of [ZetaPriorityPill]. + /// + /// Default is 'ZetaPriorityPillType.urgent' + final ZetaPriorityPillType type; + + /// The size of [ZetaPriorityPill]. + /// + /// Default is `ZetaWidgetSize.large`. + final ZetaPriorityPillSize size; @override Widget build(BuildContext context) { - final theme = Zeta.of(context); - final backgroundColor = theme.colors.primary; - final Color foregroundColor = backgroundColor.onColor; + final color = type.color(context); + final size = this.size == ZetaPriorityPillSize.small ? ZetaSpacing.x5 : ZetaSpacing.x7; return DecoratedBox( decoration: BoxDecoration( borderRadius: rounded ? ZetaRadius.full : ZetaRadius.none, - color: backgroundColor.shade10, + color: color.shade10, ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( alignment: Alignment.center, - height: ZetaSpacing.x7, - width: ZetaSpacing.x7, + height: size, + width: size, decoration: BoxDecoration( shape: rounded ? BoxShape.circle : BoxShape.rectangle, - color: backgroundColor, + color: color, ), - child: Text(index.formatMaxChars(), style: ZetaTextStyles.bodySmall.apply(color: foregroundColor)), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: ZetaSpacing.x2, vertical: ZetaSpacing.x1), child: Text( - priority, - style: ZetaTextStyles.bodySmall, - overflow: TextOverflow.ellipsis, + (index?.isEmpty ?? true) + ? (type == ZetaPriorityPillType.urgent + ? type.name.substring(0, 1).capitalize() + : type.index.toString()) + : index!.substring(0, 1).capitalize(), + style: this.size == ZetaPriorityPillSize.small + ? ZetaTextStyles.labelSmall.copyWith( + fontSize: 11, + height: 1.1, + color: color.onColor, + ) + : ZetaTextStyles.labelMedium.apply(color: color.onColor), ), ), + if (!isBadge) + Padding( + padding: const EdgeInsets.symmetric( + horizontal: ZetaSpacing.x2, + vertical: ZetaSpacing.x1, + ), + child: Text( + (priority?.isEmpty ?? true) ? type.name.capitalize() : priority!, + style: this.size == ZetaPriorityPillSize.small + ? ZetaTextStyles.bodyXSmall.copyWith( + fontSize: 11, + height: 1.1, + ) + : ZetaTextStyles.bodySmall, + overflow: TextOverflow.ellipsis, + ), + ), ], ), ); @@ -65,8 +144,11 @@ class ZetaPriorityPill extends StatelessWidget { void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(IntProperty('index', index)) + ..add(StringProperty('index', index)) ..add(StringProperty('priority', priority)) - ..add(DiagnosticsProperty('rounded', rounded)); + ..add(DiagnosticsProperty('rounded', rounded)) + ..add(DiagnosticsProperty('isBadge', isBadge)) + ..add(EnumProperty('type', type)) + ..add(EnumProperty('size', size)); } } diff --git a/lib/src/components/badges/status_label.dart b/lib/src/components/badges/status_label.dart index acb035d5..a31837c7 100644 --- a/lib/src/components/badges/status_label.dart +++ b/lib/src/components/badges/status_label.dart @@ -36,7 +36,7 @@ class ZetaStatusLabel extends StatelessWidget { decoration: BoxDecoration( color: colors.shade10, border: Border.all(color: colors.border), - borderRadius: rounded ? ZetaRadius.full : ZetaRadius.minimal, + borderRadius: rounded ? ZetaRadius.full : ZetaRadius.none, ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: ZetaSpacing.x2, vertical: ZetaSpacing.x0_5), diff --git a/lib/src/components/badges/workcloud_indicator.dart b/lib/src/components/badges/workcloud_indicator.dart deleted file mode 100644 index c1efefde..00000000 --- a/lib/src/components/badges/workcloud_indicator.dart +++ /dev/null @@ -1,164 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -import '../../../zeta_flutter.dart'; - -///Indicator Type -enum ZetaWorkcloudIndicatorType { - /// Red. - one, - - /// Orange - two, - - /// Blue. - three, - - /// Green. - four, - - /// Purple. - five, - - /// Pink. - six, - - /// Yellow. - seven, - - /// Teal - eight, - - /// Cool grey. - nine, - - /// Warn grey. - ten, -} - -extension on ZetaWorkcloudIndicatorType { - ZetaColorSwatch color(BuildContext context) { - final colors = Zeta.of(context).colors; - switch (this) { - case ZetaWorkcloudIndicatorType.one: - return colors.red; - case ZetaWorkcloudIndicatorType.two: - return colors.orange; - case ZetaWorkcloudIndicatorType.three: - return colors.blue; - case ZetaWorkcloudIndicatorType.four: - return colors.green; - case ZetaWorkcloudIndicatorType.five: - return colors.purple; - case ZetaWorkcloudIndicatorType.six: - return colors.pink; - case ZetaWorkcloudIndicatorType.seven: - return colors.yellow; - case ZetaWorkcloudIndicatorType.eight: - return colors.teal; - case ZetaWorkcloudIndicatorType.nine: - return colors.cool; - case ZetaWorkcloudIndicatorType.ten: - return colors.warm; - } - } -} - -/// Zeta Workcloud Indicator. -/// -/// There are 10 available levels in which ether the values 1 through 10 can be used, -/// or icons can be passed. -class ZetaWorkcloudIndicator extends StatelessWidget { - ///Constructs [ZetaWorkcloudIndicator] - const ZetaWorkcloudIndicator({ - super.key, - this.priorityType = ZetaWorkcloudIndicatorType.one, - this.prioritySize = ZetaWidgetSize.small, - this.label, - this.index, - this.icon, - }); - - /// The type of priority. - final ZetaWorkcloudIndicatorType priorityType; - - /// The size of Priority Pill. - /// - /// Defaults to 'small'. - final ZetaWidgetSize prioritySize; - - /// Text label. Not shown when [prioritySize] is [ZetaWidgetSize.small] - final String? label; - - /// Index value. Typically a number. - /// - /// If null, and no icon is provided, the index will match the [priorityType]. - /// - /// It is recommended to not exceed 2 characters here. - final String? index; - - /// Custom icon. If not null, this will replace the index text. - final IconData? icon; - - @override - Widget build(BuildContext context) { - final ZetaColorSwatch color = priorityType.color(context); - final textStyle = prioritySize == ZetaWidgetSize.large ? ZetaTextStyles.bodySmall : ZetaTextStyles.bodyXSmall; - - return DecoratedBox( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(ZetaSpacing.l), - color: color.shade20, - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox.square( - dimension: prioritySize == ZetaWidgetSize.large ? ZetaSpacing.x6 : ZetaSpacing.x5, - child: Container( - alignment: Alignment.center, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: priorityType == ZetaWorkcloudIndicatorType.nine ? color.shade80 : color, - ), - child: Center( - child: icon != null - ? Icon( - icon, - size: prioritySize == ZetaWidgetSize.large ? ZetaSpacing.x4 : ZetaSpacing.x3_5, - color: color.onColor, - ) - : Text( - index ?? (priorityType.index + 1).toString(), - style: textStyle.apply(color: color.onColor), - textAlign: TextAlign.center, - ), - ), - ), - ), - if (prioritySize != ZetaWidgetSize.small) - Container( - constraints: const BoxConstraints(minWidth: ZetaSpacing.x9), - padding: const EdgeInsets.symmetric(horizontal: ZetaSpacing.xs), - child: Text(label ?? '', style: textStyle, overflow: TextOverflow.ellipsis), - ), - ], - ), - ); - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add( - EnumProperty('priorityType', priorityType), - ) - ..add( - EnumProperty('prioritySize', prioritySize), - ) - ..add(StringProperty('label', label)) - ..add(StringProperty('index', index)) - ..add(DiagnosticsProperty('icon', icon)); - } -} diff --git a/lib/src/utils/enums.dart b/lib/src/utils/enums.dart index 787a4f24..1597c56f 100644 --- a/lib/src/utils/enums.dart +++ b/lib/src/utils/enums.dart @@ -12,7 +12,7 @@ enum ZetaWidgetBorder { full, } -/// Size options for [ZetaIndicator], [ZetaWorkcloudIndicator], [ZetaButton], [ZetaPasswordInput]. +/// Size options for [ZetaIndicator], [ZetaButton], [ZetaPasswordInput]. enum ZetaWidgetSize { /// large large, diff --git a/lib/zeta_flutter.dart b/lib/zeta_flutter.dart index 70b9e30c..612034ee 100644 --- a/lib/zeta_flutter.dart +++ b/lib/zeta_flutter.dart @@ -10,7 +10,6 @@ export 'src/components/badges/indicator.dart'; export 'src/components/badges/priority_pill.dart'; export 'src/components/badges/status_label.dart'; export 'src/components/badges/tag.dart'; -export 'src/components/badges/workcloud_indicator.dart'; export 'src/components/banners/in_page_banner.dart'; export 'src/components/banners/system_banner.dart'; export 'src/components/bottom sheets/bottom_sheet.dart';