diff --git a/example/lib/src/storybook/stories/accordion.dart b/example/lib/src/storybook/stories/accordion.dart index 949476dc..36dbc713 100644 --- a/example/lib/src/storybook/stories/accordion.dart +++ b/example/lib/src/storybook/stories/accordion.dart @@ -1,22 +1,27 @@ import 'package:example/src/storybook/common/options.dart'; +import 'package:example/src/storybook/common/widgets/text_divider.dart'; import 'package:flutter/material.dart'; import 'package:moon_design/moon_design.dart'; import 'package:storybook_flutter/storybook_flutter.dart'; +enum AccordionItems { first, second } + +AccordionItems? currentlyOpenAccordionItem = AccordionItems.first; + class AccordionStory extends Story { AccordionStory() : super( name: "Accordion", builder: (context) { final accordionSizesKnob = context.knobs.options( - label: "MoonAccordionSize", + label: "MoonAccordionItemSize", description: "Accordion size variants.", - initial: MoonAccordionSize.md, + initial: MoonAccordionItemSize.md, options: const [ - Option(label: "sm", value: MoonAccordionSize.sm), - Option(label: "md", value: MoonAccordionSize.md), - Option(label: "lg", value: MoonAccordionSize.lg), - Option(label: "xl", value: MoonAccordionSize.xl) + Option(label: "sm", value: MoonAccordionItemSize.sm), + Option(label: "md", value: MoonAccordionItemSize.md), + Option(label: "lg", value: MoonAccordionItemSize.lg), + Option(label: "xl", value: MoonAccordionItemSize.xl) ], ); @@ -62,29 +67,93 @@ class AccordionStory extends Story { return Directionality( textDirection: setRtlModeKnob ? TextDirection.rtl : TextDirection.ltr, - child: Center( - child: SizedBox( - height: 245, - child: Column( + child: StatefulBuilder( + builder: (context, setState) { + return ListView( + clipBehavior: Clip.none, children: [ - MoonAccordion( + const SizedBox(height: 64), + const TextDivider(text: "Grouped accordion"), + const SizedBox(height: 24), + MoonAccordionItem( + identityValue: AccordionItems.first, + groupIdentityValue: currentlyOpenAccordionItem, + accordionSize: accordionSizesKnob, + backgroundColor: backgroundColor, + expandedBackgroundColor: expandedBackgroundColor, + showBorder: showBorderKnob, + showDivider: showDividerKnob, + shadows: showShadowKnob == true ? null : [], + childrenPadding: const EdgeInsets.all(12), + onExpansionChanged: (value) => setState(() => currentlyOpenAccordionItem = value), + title: const Text("Grouped accordion item #1"), + children: const [ + Text( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + ), + ], + ), + const SizedBox(height: 8), + MoonAccordionItem( + identityValue: AccordionItems.second, + groupIdentityValue: currentlyOpenAccordionItem, + initiallyExpanded: true, + accordionSize: accordionSizesKnob, + backgroundColor: backgroundColor, + expandedBackgroundColor: expandedBackgroundColor, + showBorder: showBorderKnob, + showDivider: showDividerKnob, + shadows: showShadowKnob == true ? null : [], + childrenPadding: const EdgeInsets.all(12), + onExpansionChanged: (value) => setState(() => currentlyOpenAccordionItem = value), + title: const Text("Grouped accordion item #2"), + children: const [ + Text( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + ), + ], + ), + const SizedBox(height: 40), + const TextDivider(text: "Ungrouped accordion with content outside"), + const SizedBox(height: 24), + MoonAccordionItem( + accordionSize: accordionSizesKnob, + backgroundColor: backgroundColor, + initiallyExpanded: true, + hasContentOutside: true, + expandedBackgroundColor: expandedBackgroundColor, + showBorder: showBorderKnob, + showDivider: showDividerKnob, + shadows: showShadowKnob == true ? null : [], + childrenPadding: const EdgeInsets.symmetric(vertical: 12), + title: const Text("Ungrouped accordion item #1"), + children: const [ + Text( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + ), + ], + ), + const SizedBox(height: 8), + MoonAccordionItem( accordionSize: accordionSizesKnob, backgroundColor: backgroundColor, + hasContentOutside: true, expandedBackgroundColor: expandedBackgroundColor, showBorder: showBorderKnob, showDivider: showDividerKnob, shadows: showShadowKnob == true ? null : [], - childrenPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - title: const Text("Accordion title"), + childrenPadding: const EdgeInsets.symmetric(vertical: 12), + title: const Text("Ungrouped accordion item #2"), children: const [ Text( "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", ), ], ), + const SizedBox(height: 64), ], - ), - ), + ); + }, ), ); }, diff --git a/example/lib/src/storybook/storybook.dart b/example/lib/src/storybook/storybook.dart index d8c8f85b..f16f6595 100644 --- a/example/lib/src/storybook/storybook.dart +++ b/example/lib/src/storybook/storybook.dart @@ -37,7 +37,7 @@ class StorybookPage extends StatelessWidget { return Stack( children: [ Storybook( - initialStory: "Modal", + initialStory: "Accordion", plugins: _plugins, wrapperBuilder: (context, child) => MaterialApp( title: "Moon Design for Flutter", diff --git a/lib/moon_design.dart b/lib/moon_design.dart index 5a9989b8..e4535d40 100644 --- a/lib/moon_design.dart +++ b/lib/moon_design.dart @@ -29,7 +29,7 @@ export 'package:moon_design/src/utils/extensions.dart'; export 'package:moon_design/src/utils/measure_size.dart'; export 'package:moon_design/src/utils/widget_surveyor.dart'; -export 'package:moon_design/src/widgets/accordion/accordion.dart'; +export 'package:moon_design/src/widgets/accordion/accordion_item.dart'; export 'package:moon_design/src/widgets/avatar/avatar.dart'; export 'package:moon_design/src/widgets/buttons/button.dart'; export 'package:moon_design/src/widgets/buttons/ghost_button.dart'; diff --git a/lib/src/theme/accordion/accordion_colors.dart b/lib/src/theme/accordion/accordion_item_colors.dart similarity index 77% rename from lib/src/theme/accordion/accordion_colors.dart rename to lib/src/theme/accordion/accordion_item_colors.dart index 07adfd81..d79796f2 100644 --- a/lib/src/theme/accordion/accordion_colors.dart +++ b/lib/src/theme/accordion/accordion_item_colors.dart @@ -4,8 +4,8 @@ import 'package:flutter/material.dart'; import 'package:moon_design/src/theme/colors.dart'; @immutable -class MoonAccordionColors extends ThemeExtension with DiagnosticableTreeMixin { - static final light = MoonAccordionColors( +class MoonAccordionItemColors extends ThemeExtension with DiagnosticableTreeMixin { + static final light = MoonAccordionItemColors( backgroundColor: MoonColors.light.gohan, expandedBackgroundColor: MoonColors.light.gohan, borderColor: MoonColors.light.beerus, @@ -14,7 +14,7 @@ class MoonAccordionColors extends ThemeExtension with Diagn iconColor: MoonColors.light.trunks, ); - static final dark = MoonAccordionColors( + static final dark = MoonAccordionItemColors( backgroundColor: MoonColors.dark.gohan, expandedBackgroundColor: MoonColors.dark.gohan, borderColor: MoonColors.dark.beerus, @@ -23,25 +23,25 @@ class MoonAccordionColors extends ThemeExtension with Diagn expandedIconColor: MoonColors.dark.bulma, ); - /// Accordion background color. + /// Accordion item background color. final Color backgroundColor; - /// Expanded accordion background color. + /// Expanded accordion item background color. final Color expandedBackgroundColor; - /// Accordion border color. + /// Accordion item border color. final Color borderColor; - /// Accordion divider color. + /// Accordion item divider color. final Color dividerColor; - /// Accordion icon color. + /// Accordion item icon color. final Color iconColor; - /// Expanded accordion icon color. + /// Expanded accordion item icon color. final Color expandedIconColor; - const MoonAccordionColors({ + const MoonAccordionItemColors({ required this.backgroundColor, required this.expandedBackgroundColor, required this.borderColor, @@ -51,7 +51,7 @@ class MoonAccordionColors extends ThemeExtension with Diagn }); @override - MoonAccordionColors copyWith({ + MoonAccordionItemColors copyWith({ Color? backgroundColor, Color? expandedBackgroundColor, Color? borderColor, @@ -59,7 +59,7 @@ class MoonAccordionColors extends ThemeExtension with Diagn Color? expandedIconColor, Color? iconColor, }) { - return MoonAccordionColors( + return MoonAccordionItemColors( backgroundColor: backgroundColor ?? this.backgroundColor, expandedBackgroundColor: expandedBackgroundColor ?? this.expandedBackgroundColor, borderColor: borderColor ?? this.borderColor, @@ -70,10 +70,10 @@ class MoonAccordionColors extends ThemeExtension with Diagn } @override - MoonAccordionColors lerp(ThemeExtension? other, double t) { - if (other is! MoonAccordionColors) return this; + MoonAccordionItemColors lerp(ThemeExtension? other, double t) { + if (other is! MoonAccordionItemColors) return this; - return MoonAccordionColors( + return MoonAccordionItemColors( backgroundColor: Color.lerp(backgroundColor, other.backgroundColor, t)!, expandedBackgroundColor: Color.lerp(expandedBackgroundColor, other.expandedBackgroundColor, t)!, borderColor: Color.lerp(borderColor, other.borderColor, t)!, @@ -87,7 +87,7 @@ class MoonAccordionColors extends ThemeExtension with Diagn void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty("type", "MoonAccordionColors")) + ..add(DiagnosticsProperty("type", "MoonAccordionItemColors")) ..add(ColorProperty("backgroundColor", backgroundColor)) ..add(ColorProperty("expandedBackgroundColor", expandedBackgroundColor)) ..add(ColorProperty("borderColor", borderColor)) diff --git a/lib/src/theme/accordion/accordion_properties.dart b/lib/src/theme/accordion/accordion_item_properties.dart similarity index 70% rename from lib/src/theme/accordion/accordion_properties.dart rename to lib/src/theme/accordion/accordion_item_properties.dart index 2437c3cd..da574c9f 100644 --- a/lib/src/theme/accordion/accordion_properties.dart +++ b/lib/src/theme/accordion/accordion_item_properties.dart @@ -5,8 +5,8 @@ import 'package:flutter/material.dart'; import 'package:moon_design/src/theme/borders.dart'; @immutable -class MoonAccordionProperties extends ThemeExtension with DiagnosticableTreeMixin { - static final properties = MoonAccordionProperties( +class MoonAccordionItemProperties extends ThemeExtension with DiagnosticableTreeMixin { + static final properties = MoonAccordionItemProperties( transitionDuration: const Duration(milliseconds: 200), transitionCurve: Curves.easeInOutCubic, borderRadius: SmoothBorderRadius.all( @@ -17,28 +17,28 @@ class MoonAccordionProperties extends ThemeExtension wi ), ); - /// Accordion transition duration. + /// Accordion item transition duration. final Duration transitionDuration; - /// Accordion transition curve. + /// Accordion item transition curve. final Curve transitionCurve; - /// Accordion border radius. + /// Accordion item border radius. final SmoothBorderRadius borderRadius; - const MoonAccordionProperties({ + const MoonAccordionItemProperties({ required this.borderRadius, required this.transitionDuration, required this.transitionCurve, }); @override - MoonAccordionProperties copyWith({ + MoonAccordionItemProperties copyWith({ Duration? transitionDuration, Curve? transitionCurve, SmoothBorderRadius? borderRadius, }) { - return MoonAccordionProperties( + return MoonAccordionItemProperties( transitionDuration: transitionDuration ?? this.transitionDuration, transitionCurve: transitionCurve ?? this.transitionCurve, borderRadius: borderRadius ?? this.borderRadius, @@ -46,10 +46,10 @@ class MoonAccordionProperties extends ThemeExtension wi } @override - MoonAccordionProperties lerp(ThemeExtension? other, double t) { - if (other is! MoonAccordionProperties) return this; + MoonAccordionItemProperties lerp(ThemeExtension? other, double t) { + if (other is! MoonAccordionItemProperties) return this; - return MoonAccordionProperties( + return MoonAccordionItemProperties( transitionDuration: lerpDuration(transitionDuration, other.transitionDuration, t), transitionCurve: other.transitionCurve, borderRadius: SmoothBorderRadius.lerp(borderRadius, other.borderRadius, t)!, @@ -60,7 +60,7 @@ class MoonAccordionProperties extends ThemeExtension wi void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties - ..add(DiagnosticsProperty("type", "MoonAccordionProperties")) + ..add(DiagnosticsProperty("type", "MoonAccordionItemProperties")) ..add(DiagnosticsProperty("transitionDuration", transitionDuration)) ..add(DiagnosticsProperty("transitionCurve", transitionCurve)) ..add(DiagnosticsProperty("borderRadius", borderRadius)); diff --git a/lib/src/theme/accordion/accordion_item_shadows.dart b/lib/src/theme/accordion/accordion_item_shadows.dart new file mode 100644 index 00000000..39418bd7 --- /dev/null +++ b/lib/src/theme/accordion/accordion_item_shadows.dart @@ -0,0 +1,46 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +import 'package:moon_design/src/theme/shadows.dart'; + +@immutable +class MoonAccordionItemShadows extends ThemeExtension with DiagnosticableTreeMixin { + static final light = MoonAccordionItemShadows( + shadows: MoonShadows.light.sm, + ); + + static final dark = MoonAccordionItemShadows( + shadows: MoonShadows.dark.sm, + ); + + /// Accordion item shadows. + final List shadows; + + const MoonAccordionItemShadows({ + required this.shadows, + }); + + @override + MoonAccordionItemShadows copyWith({List? shadows}) { + return MoonAccordionItemShadows( + shadows: shadows ?? this.shadows, + ); + } + + @override + MoonAccordionItemShadows lerp(ThemeExtension? other, double t) { + if (other is! MoonAccordionItemShadows) return this; + + return MoonAccordionItemShadows( + shadows: BoxShadow.lerpList(shadows, other.shadows, t)!, + ); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty("type", "MoonAccordionItemShadows")) + ..add(DiagnosticsProperty>("shadows", shadows)); + } +} diff --git a/lib/src/theme/accordion/accordion_size_properties.dart b/lib/src/theme/accordion/accordion_item_size_properties.dart similarity index 76% rename from lib/src/theme/accordion/accordion_size_properties.dart rename to lib/src/theme/accordion/accordion_item_size_properties.dart index 528d6be4..35612a85 100644 --- a/lib/src/theme/accordion/accordion_size_properties.dart +++ b/lib/src/theme/accordion/accordion_item_size_properties.dart @@ -7,29 +7,30 @@ import 'package:moon_design/src/theme/sizes.dart'; import 'package:moon_design/src/theme/typography/text_styles.dart'; @immutable -class MoonAccordionSizeProperties extends ThemeExtension with DiagnosticableTreeMixin { - static final sm = MoonAccordionSizeProperties( +class MoonAccordionItemSizeProperties extends ThemeExtension + with DiagnosticableTreeMixin { + static final sm = MoonAccordionItemSizeProperties( headerHeight: MoonSizes.sizes.sm, iconSizeValue: MoonSizes.sizes.x2s, headerPadding: EdgeInsets.symmetric(horizontal: MoonSizes.sizes.x4s), textStyle: MoonTextStyles.heading.text12, ); - static final md = MoonAccordionSizeProperties( + static final md = MoonAccordionItemSizeProperties( headerHeight: MoonSizes.sizes.md, iconSizeValue: MoonSizes.sizes.xs, headerPadding: EdgeInsets.symmetric(horizontal: MoonSizes.sizes.x3s), textStyle: MoonTextStyles.heading.text14, ); - static final lg = MoonAccordionSizeProperties( + static final lg = MoonAccordionItemSizeProperties( headerHeight: MoonSizes.sizes.lg, iconSizeValue: MoonSizes.sizes.xs, headerPadding: EdgeInsets.symmetric(horizontal: MoonSizes.sizes.x3s), textStyle: MoonTextStyles.heading.text14, ); - static final xl = MoonAccordionSizeProperties( + static final xl = MoonAccordionItemSizeProperties( headerHeight: MoonSizes.sizes.xl, iconSizeValue: MoonSizes.sizes.xs, headerPadding: EdgeInsets.symmetric(horizontal: MoonSizes.sizes.x2s), @@ -48,7 +49,7 @@ class MoonAccordionSizeProperties extends ThemeExtension? other, double t) { - if (other is! MoonAccordionSizeProperties) return this; + MoonAccordionItemSizeProperties lerp(ThemeExtension? other, double t) { + if (other is! MoonAccordionItemSizeProperties) return this; - return MoonAccordionSizeProperties( + return MoonAccordionItemSizeProperties( headerHeight: lerpDouble(headerHeight, other.headerHeight, t)!, iconSizeValue: lerpDouble(iconSizeValue, other.iconSizeValue, t)!, headerPadding: EdgeInsets.lerp(headerPadding, other.headerPadding, t)!, @@ -86,7 +87,7 @@ class MoonAccordionSizeProperties extends ThemeExtension("headerPadding", headerPadding)) diff --git a/lib/src/theme/accordion/accordion_item_sizes.dart b/lib/src/theme/accordion/accordion_item_sizes.dart new file mode 100644 index 00000000..6dffcb78 --- /dev/null +++ b/lib/src/theme/accordion/accordion_item_sizes.dart @@ -0,0 +1,71 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +import 'package:moon_design/src/theme/accordion/accordion_item_size_properties.dart'; + +@immutable +class MoonAccordionItemSizes extends ThemeExtension with DiagnosticableTreeMixin { + static final sizes = MoonAccordionItemSizes( + sm: MoonAccordionItemSizeProperties.sm, + md: MoonAccordionItemSizeProperties.md, + lg: MoonAccordionItemSizeProperties.lg, + xl: MoonAccordionItemSizeProperties.xl, + ); + + /// Small accordion item properties. + final MoonAccordionItemSizeProperties sm; + + /// Medium accordion item properties. + final MoonAccordionItemSizeProperties md; + + /// Large accordion item properties. + final MoonAccordionItemSizeProperties lg; + + /// Extra large accordion item properties. + final MoonAccordionItemSizeProperties xl; + + const MoonAccordionItemSizes({ + required this.sm, + required this.md, + required this.lg, + required this.xl, + }); + + @override + MoonAccordionItemSizes copyWith({ + MoonAccordionItemSizeProperties? sm, + MoonAccordionItemSizeProperties? md, + MoonAccordionItemSizeProperties? lg, + MoonAccordionItemSizeProperties? xl, + }) { + return MoonAccordionItemSizes( + sm: sm ?? this.sm, + md: md ?? this.md, + lg: lg ?? this.lg, + xl: xl ?? this.xl, + ); + } + + @override + MoonAccordionItemSizes lerp(ThemeExtension? other, double t) { + if (other is! MoonAccordionItemSizes) return this; + + return MoonAccordionItemSizes( + sm: sm.lerp(other.sm, t), + md: md.lerp(other.md, t), + lg: lg.lerp(other.lg, t), + xl: xl.lerp(other.xl, t), + ); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty("type", "MoonAccordionItemSizes")) + ..add(DiagnosticsProperty("sm", sm)) + ..add(DiagnosticsProperty("md", md)) + ..add(DiagnosticsProperty("lg", lg)) + ..add(DiagnosticsProperty("xl", xl)); + } +} diff --git a/lib/src/theme/accordion/accordion_shadows.dart b/lib/src/theme/accordion/accordion_shadows.dart deleted file mode 100644 index 0462b93e..00000000 --- a/lib/src/theme/accordion/accordion_shadows.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -import 'package:moon_design/src/theme/shadows.dart'; - -@immutable -class MoonAccordionShadows extends ThemeExtension with DiagnosticableTreeMixin { - static final light = MoonAccordionShadows( - accordionShadows: MoonShadows.light.sm, - ); - - static final dark = MoonAccordionShadows( - accordionShadows: MoonShadows.dark.sm, - ); - - /// Accordion shadows. - final List accordionShadows; - - const MoonAccordionShadows({ - required this.accordionShadows, - }); - - @override - MoonAccordionShadows copyWith({List? accordionShadows}) { - return MoonAccordionShadows( - accordionShadows: accordionShadows ?? this.accordionShadows, - ); - } - - @override - MoonAccordionShadows lerp(ThemeExtension? other, double t) { - if (other is! MoonAccordionShadows) return this; - - return MoonAccordionShadows( - accordionShadows: BoxShadow.lerpList(accordionShadows, other.accordionShadows, t)!, - ); - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty("type", "MoonAccordionShadows")) - ..add(DiagnosticsProperty>("shadows", accordionShadows)); - } -} diff --git a/lib/src/theme/accordion/accordion_sizes.dart b/lib/src/theme/accordion/accordion_sizes.dart deleted file mode 100644 index 33d72ca8..00000000 --- a/lib/src/theme/accordion/accordion_sizes.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -import 'package:moon_design/src/theme/accordion/accordion_size_properties.dart'; - -@immutable -class MoonAccordionSizes extends ThemeExtension with DiagnosticableTreeMixin { - static final sizes = MoonAccordionSizes( - sm: MoonAccordionSizeProperties.sm, - md: MoonAccordionSizeProperties.md, - lg: MoonAccordionSizeProperties.lg, - xl: MoonAccordionSizeProperties.xl, - ); - - /// Small accordion properties. - final MoonAccordionSizeProperties sm; - - /// Medium accordion properties. - final MoonAccordionSizeProperties md; - - /// Large accordion properties. - final MoonAccordionSizeProperties lg; - - /// Extra large accordion properties. - final MoonAccordionSizeProperties xl; - - const MoonAccordionSizes({ - required this.sm, - required this.md, - required this.lg, - required this.xl, - }); - - @override - MoonAccordionSizes copyWith({ - MoonAccordionSizeProperties? sm, - MoonAccordionSizeProperties? md, - MoonAccordionSizeProperties? lg, - MoonAccordionSizeProperties? xl, - }) { - return MoonAccordionSizes( - sm: sm ?? this.sm, - md: md ?? this.md, - lg: lg ?? this.lg, - xl: xl ?? this.xl, - ); - } - - @override - MoonAccordionSizes lerp(ThemeExtension? other, double t) { - if (other is! MoonAccordionSizes) return this; - - return MoonAccordionSizes( - sm: sm.lerp(other.sm, t), - md: md.lerp(other.md, t), - lg: lg.lerp(other.lg, t), - xl: xl.lerp(other.xl, t), - ); - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties - ..add(DiagnosticsProperty("type", "MoonAccordionSizes")) - ..add(DiagnosticsProperty("sm", sm)) - ..add(DiagnosticsProperty("md", md)) - ..add(DiagnosticsProperty("lg", lg)) - ..add(DiagnosticsProperty("xl", xl)); - } -} diff --git a/lib/src/theme/accordion/accordion_theme.dart b/lib/src/theme/accordion/accordion_theme.dart index f4f1ecd2..fe316018 100644 --- a/lib/src/theme/accordion/accordion_theme.dart +++ b/lib/src/theme/accordion/accordion_theme.dart @@ -1,58 +1,58 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:moon_design/src/theme/accordion/accordion_colors.dart'; -import 'package:moon_design/src/theme/accordion/accordion_properties.dart'; -import 'package:moon_design/src/theme/accordion/accordion_shadows.dart'; -import 'package:moon_design/src/theme/accordion/accordion_sizes.dart'; +import 'package:moon_design/src/theme/accordion/accordion_item_colors.dart'; +import 'package:moon_design/src/theme/accordion/accordion_item_properties.dart'; +import 'package:moon_design/src/theme/accordion/accordion_item_shadows.dart'; +import 'package:moon_design/src/theme/accordion/accordion_item_sizes.dart'; @immutable class MoonAccordionTheme extends ThemeExtension with DiagnosticableTreeMixin { static final light = MoonAccordionTheme( - colors: MoonAccordionColors.light, - properties: MoonAccordionProperties.properties, - sizes: MoonAccordionSizes.sizes, - shadows: MoonAccordionShadows.light, + itemColors: MoonAccordionItemColors.light, + itemProperties: MoonAccordionItemProperties.properties, + itemSizes: MoonAccordionItemSizes.sizes, + itemShadows: MoonAccordionItemShadows.light, ); static final dark = MoonAccordionTheme( - colors: MoonAccordionColors.dark, - properties: MoonAccordionProperties.properties, - sizes: MoonAccordionSizes.sizes, - shadows: MoonAccordionShadows.dark, + itemColors: MoonAccordionItemColors.dark, + itemProperties: MoonAccordionItemProperties.properties, + itemSizes: MoonAccordionItemSizes.sizes, + itemShadows: MoonAccordionItemShadows.dark, ); - /// Accordion colors. - final MoonAccordionColors colors; + /// Accordion item colors. + final MoonAccordionItemColors itemColors; - /// Accordion properties. - final MoonAccordionProperties properties; + /// Accordion item properties. + final MoonAccordionItemProperties itemProperties; - /// Accordion sizes. - final MoonAccordionSizes sizes; + /// Accordion item sizes. + final MoonAccordionItemSizes itemSizes; - /// Accordion shadows. - final MoonAccordionShadows shadows; + /// Accordion item shadows. + final MoonAccordionItemShadows itemShadows; const MoonAccordionTheme({ - required this.colors, - required this.properties, - required this.sizes, - required this.shadows, + required this.itemColors, + required this.itemProperties, + required this.itemSizes, + required this.itemShadows, }); @override MoonAccordionTheme copyWith({ - MoonAccordionColors? colors, - MoonAccordionProperties? properties, - MoonAccordionSizes? sizes, - MoonAccordionShadows? shadows, + MoonAccordionItemColors? itemColors, + MoonAccordionItemProperties? itemProperties, + MoonAccordionItemSizes? itemSizes, + MoonAccordionItemShadows? itemShadows, }) { return MoonAccordionTheme( - colors: colors ?? this.colors, - properties: properties ?? this.properties, - sizes: sizes ?? this.sizes, - shadows: shadows ?? this.shadows, + itemColors: itemColors ?? this.itemColors, + itemProperties: itemProperties ?? this.itemProperties, + itemSizes: itemSizes ?? this.itemSizes, + itemShadows: itemShadows ?? this.itemShadows, ); } @@ -61,10 +61,10 @@ class MoonAccordionTheme extends ThemeExtension with Diagnos if (other is! MoonAccordionTheme) return this; return MoonAccordionTheme( - colors: colors.lerp(other.colors, t), - properties: properties.lerp(other.properties, t), - sizes: sizes.lerp(other.sizes, t), - shadows: shadows.lerp(other.shadows, t), + itemColors: itemColors.lerp(other.itemColors, t), + itemProperties: itemProperties.lerp(other.itemProperties, t), + itemSizes: itemSizes.lerp(other.itemSizes, t), + itemShadows: itemShadows.lerp(other.itemShadows, t), ); } @@ -73,9 +73,9 @@ class MoonAccordionTheme extends ThemeExtension with Diagnos super.debugFillProperties(diagnosticProperties); diagnosticProperties ..add(DiagnosticsProperty("type", "MoonAccordionTheme")) - ..add(DiagnosticsProperty("colors", colors)) - ..add(DiagnosticsProperty("properties", properties)) - ..add(DiagnosticsProperty("sizes", sizes)) - ..add(DiagnosticsProperty("shadows", shadows)); + ..add(DiagnosticsProperty("itemColors", itemColors)) + ..add(DiagnosticsProperty("itemProperties", itemProperties)) + ..add(DiagnosticsProperty("itemSizes", itemSizes)) + ..add(DiagnosticsProperty("itemShadows", itemShadows)); } } diff --git a/lib/src/widgets/accordion/accordion.dart b/lib/src/widgets/accordion/accordion_item.dart similarity index 72% rename from lib/src/widgets/accordion/accordion.dart rename to lib/src/widgets/accordion/accordion_item.dart index 4dab79be..df1cf99d 100644 --- a/lib/src/widgets/accordion/accordion.dart +++ b/lib/src/widgets/accordion/accordion_item.dart @@ -1,7 +1,7 @@ import 'package:figma_squircle/figma_squircle.dart'; import 'package:flutter/material.dart'; -import 'package:moon_design/src/theme/accordion/accordion_size_properties.dart'; +import 'package:moon_design/src/theme/accordion/accordion_item_size_properties.dart'; import 'package:moon_design/src/theme/borders.dart'; import 'package:moon_design/src/theme/colors.dart'; import 'package:moon_design/src/theme/effects/focus_effects.dart'; @@ -11,26 +11,38 @@ import 'package:moon_design/src/theme/theme.dart'; import 'package:moon_design/src/widgets/common/effects/focus_effect.dart'; import 'package:moon_design/src/widgets/common/icons/icons.dart'; -enum MoonAccordionSize { +enum MoonAccordionItemSize { sm, md, lg, xl, } -class MoonAccordion extends StatefulWidget { +class MoonAccordionItem extends StatefulWidget { + /// The identity value represented by this accordion. + final T? identityValue; + + /// The currently selected identity value for a group of accordions. + /// + /// This accordion is considered selected if its [identityValue] matches the + /// [groupIdentityValue]. + final T? groupIdentityValue; + /// Called when the accordion expands or collapses. /// - /// When the accordion starts expanding, this function is called with the value - /// true. When the accordion starts collapsing, this function is called with - /// the value false. - final ValueChanged? onExpansionChanged; + /// When the accordion expansion changes, this function is called with the [identityValue]. + final ValueChanged? onExpansionChanged; /// Specifies if the accordion is initially expanded (true) or collapsed (false, the default). + /// + /// If [identityValue] matches [groupIdentityValue], this parameter is ignored. final bool initiallyExpanded; + /// Whether the accordion content is outside + final bool hasContentOutside; + /// The size of the accordion. - final MoonAccordionSize? accordionSize; + final MoonAccordionItemSize? accordionSize; /// The background color of the accordion when expanded. final Color? backgroundColor; @@ -148,10 +160,13 @@ class MoonAccordion extends StatefulWidget { final List children; /// MDS accordion widget. - const MoonAccordion({ + const MoonAccordionItem({ super.key, + this.identityValue, + this.groupIdentityValue, this.onExpansionChanged, this.initiallyExpanded = false, + this.hasContentOutside = false, this.accordionSize, this.borderColor, this.backgroundColor, @@ -185,11 +200,13 @@ class MoonAccordion extends StatefulWidget { 'are aligned in a column, not a row. Try to use another constant.', ); + bool get _selected => identityValue != null && identityValue == groupIdentityValue; + @override - State createState() => _MoonAccordionState(); + State> createState() => _MoonAccordionItemState(); } -class _MoonAccordionState extends State with SingleTickerProviderStateMixin { +class _MoonAccordionItemState extends State> with SingleTickerProviderStateMixin { static final Animatable _halfTween = Tween(begin: 0.0, end: 0.5); late final Map> _actions = { @@ -244,21 +261,25 @@ class _MoonAccordionState extends State with SingleTickerProvider } PageStorage.maybeOf(context)?.writeState(context, _isExpanded); }); - widget.onExpansionChanged?.call(_isExpanded); + + widget.onExpansionChanged?.call(_isExpanded ? widget.identityValue : null); } - MoonAccordionSizeProperties _getMoonAccordionSize(BuildContext context, MoonAccordionSize? moonAccordionSize) { - switch (moonAccordionSize) { - case MoonAccordionSize.sm: - return context.moonTheme?.accordionTheme.sizes.sm ?? MoonAccordionSizeProperties.sm; - case MoonAccordionSize.md: - return context.moonTheme?.accordionTheme.sizes.md ?? MoonAccordionSizeProperties.md; - case MoonAccordionSize.lg: - return context.moonTheme?.accordionTheme.sizes.lg ?? MoonAccordionSizeProperties.lg; - case MoonAccordionSize.xl: - return context.moonTheme?.accordionTheme.sizes.xl ?? MoonAccordionSizeProperties.xl; + MoonAccordionItemSizeProperties _getMoonAccordionItemSize( + BuildContext context, + MoonAccordionItemSize? moonAccordionItemSize, + ) { + switch (moonAccordionItemSize) { + case MoonAccordionItemSize.sm: + return context.moonTheme?.accordionTheme.itemSizes.sm ?? MoonAccordionItemSizeProperties.sm; + case MoonAccordionItemSize.md: + return context.moonTheme?.accordionTheme.itemSizes.md ?? MoonAccordionItemSizeProperties.md; + case MoonAccordionItemSize.lg: + return context.moonTheme?.accordionTheme.itemSizes.lg ?? MoonAccordionItemSizeProperties.lg; + case MoonAccordionItemSize.xl: + return context.moonTheme?.accordionTheme.itemSizes.xl ?? MoonAccordionItemSizeProperties.xl; default: - return context.moonTheme?.accordionTheme.sizes.md ?? MoonAccordionSizeProperties.md; + return context.moonTheme?.accordionTheme.itemSizes.md ?? MoonAccordionItemSizeProperties.md; } } @@ -279,11 +300,44 @@ class _MoonAccordionState extends State with SingleTickerProvider void initState() { super.initState(); - _isExpanded = PageStorage.maybeOf(context)?.readState(context) as bool? ?? widget.initiallyExpanded; + _isExpanded = + PageStorage.maybeOf(context)?.readState(context) as bool? ?? widget.initiallyExpanded || widget._selected; + + WidgetsBinding.instance.addPostFrameCallback((_) { + if (!mounted) return; + + if (_isExpanded) { + _animationController!.value = 1.0; + } + }); + } - if (_isExpanded) { - _animationController!.value = 1.0; + @override + void didUpdateWidget(MoonAccordionItem oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.identityValue == null && widget.groupIdentityValue == null) return; + + if (widget._selected) { + _isExpanded = true; + } else { + _isExpanded = false; } + + setState(() { + if (_isExpanded) { + _animationController!.forward(); + } else { + _animationController!.reverse().then((void value) { + if (!mounted) { + return; + } + setState(() { + // Rebuild without widget.children. + }); + }); + } + PageStorage.maybeOf(context)?.writeState(context, _isExpanded); + }); } @override @@ -293,17 +347,19 @@ class _MoonAccordionState extends State with SingleTickerProvider } Widget? _buildIcon(BuildContext context) { - final double iconSize = _getMoonAccordionSize(context, widget.accordionSize).iconSizeValue; + final double iconSize = _getMoonAccordionItemSize(context, widget.accordionSize).iconSizeValue; - final Color effectiveBackgroundColor = - widget.backgroundColor ?? context.moonTheme?.accordionTheme.colors.backgroundColor ?? MoonColors.light.gohan; + final Color effectiveBackgroundColor = widget.backgroundColor ?? + context.moonTheme?.accordionTheme.itemColors.backgroundColor ?? + MoonColors.light.gohan; final Color effectiveIconColor = widget.iconColor ?? - context.moonTheme?.accordionTheme.colors.iconColor ?? + context.moonTheme?.accordionTheme.itemColors.iconColor ?? _getTextColor(context, effectiveBackgroundColor: effectiveBackgroundColor); - final Color effectiveExpandedIconColor = - widget.expandedIconColor ?? context.moonTheme?.accordionTheme.colors.expandedIconColor ?? effectiveIconColor; + final Color effectiveExpandedIconColor = widget.expandedIconColor ?? + context.moonTheme?.accordionTheme.itemColors.expandedIconColor ?? + effectiveIconColor; _iconColorAnimation = ColorTween(begin: effectiveIconColor, end: effectiveExpandedIconColor).animate(_curvedAnimation!); @@ -318,33 +374,36 @@ class _MoonAccordionState extends State with SingleTickerProvider } Widget _buildChildren(BuildContext context, Widget? child) { - final Color effectiveBackgroundColor = - widget.backgroundColor ?? context.moonTheme?.accordionTheme.colors.backgroundColor ?? MoonColors.light.gohan; + final Color effectiveBackgroundColor = widget.backgroundColor ?? + context.moonTheme?.accordionTheme.itemColors.backgroundColor ?? + MoonColors.light.gohan; final Color effectiveExpandedBackgroundColor = widget.expandedBackgroundColor ?? - context.moonTheme?.accordionTheme.colors.expandedBackgroundColor ?? + context.moonTheme?.accordionTheme.itemColors.expandedBackgroundColor ?? MoonColors.light.gohan; final Color effectiveTextColor = _getTextColor(context, effectiveBackgroundColor: _backgroundColorAnimation?.value ?? Colors.transparent); final Color effectiveBorderColor = - widget.borderColor ?? context.moonTheme?.accordionTheme.colors.borderColor ?? MoonColors.light.beerus; + widget.borderColor ?? context.moonTheme?.accordionTheme.itemColors.borderColor ?? MoonColors.light.beerus; - final MoonAccordionSizeProperties effectiveMoonAccordionSize = _getMoonAccordionSize(context, widget.accordionSize); + final MoonAccordionItemSizeProperties effectiveMoonAccordionSize = + _getMoonAccordionItemSize(context, widget.accordionSize); final double effectiveHeaderHeight = widget.headerHeight ?? effectiveMoonAccordionSize.headerHeight; final EdgeInsets effectiveHeaderPadding = widget.headerPadding ?? effectiveMoonAccordionSize.headerPadding; final List effectiveShadows = - widget.shadows ?? context.moonTheme?.accordionTheme.shadows.accordionShadows ?? MoonShadows.light.sm; + widget.shadows ?? context.moonTheme?.accordionTheme.itemShadows.shadows ?? MoonShadows.light.sm; final Duration effectiveTransitionDuration = widget.transitionDuration ?? - context.moonTheme?.accordionTheme.properties.transitionDuration ?? + context.moonTheme?.accordionTheme.itemProperties.transitionDuration ?? const Duration(milliseconds: 200); - final Curve effectiveTransitionCurve = - widget.transitionCurve ?? context.moonTheme?.accordionTheme.properties.transitionCurve ?? Curves.easeInOutCubic; + final Curve effectiveTransitionCurve = widget.transitionCurve ?? + context.moonTheme?.accordionTheme.itemProperties.transitionCurve ?? + Curves.easeInOutCubic; final double effectiveFocusEffectExtent = context.moonEffects?.controlFocusEffect.effectExtent ?? MoonFocusEffects.lightFocusEffect.effectExtent; @@ -368,7 +427,7 @@ class _MoonAccordionState extends State with SingleTickerProvider context.moonEffects?.controlHoverEffect.hoverDuration ?? MoonHoverEffects.lightHoverEffect.hoverDuration; final SmoothBorderRadius effectiveBorderRadius = widget.borderRadius ?? - context.moonTheme?.accordionTheme.properties.borderRadius ?? + context.moonTheme?.accordionTheme.itemProperties.borderRadius ?? SmoothBorderRadius.all( SmoothRadius( cornerRadius: MoonBorders.borders.interactiveSm.topLeft.x, @@ -408,14 +467,16 @@ class _MoonAccordionState extends State with SingleTickerProvider duration: effectiveHoverEffectDuration, curve: effectiveHoverEffectCurve, clipBehavior: widget.clipBehavior ?? Clip.none, - decoration: ShapeDecoration( - color: resolvedBackgroundColor, - shadows: effectiveShadows, - shape: SmoothRectangleBorder( - side: widget.showBorder ? BorderSide(color: effectiveBorderColor) : BorderSide.none, - borderRadius: effectiveBorderRadius, - ), - ), + decoration: !widget.hasContentOutside + ? ShapeDecoration( + color: resolvedBackgroundColor, + shadows: effectiveShadows, + shape: SmoothRectangleBorder( + side: widget.showBorder ? BorderSide(color: effectiveBorderColor) : BorderSide.none, + borderRadius: effectiveBorderRadius, + ), + ) + : null, child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -424,9 +485,21 @@ class _MoonAccordionState extends State with SingleTickerProvider child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: _handleTap, - child: Container( + child: AnimatedContainer( height: effectiveHeaderHeight, padding: effectiveHeaderPadding, + duration: effectiveHoverEffectDuration, + curve: effectiveHoverEffectCurve, + decoration: widget.hasContentOutside + ? ShapeDecoration( + color: resolvedBackgroundColor, + shadows: effectiveShadows, + shape: SmoothRectangleBorder( + side: widget.showBorder ? BorderSide(color: effectiveBorderColor) : BorderSide.none, + borderRadius: effectiveBorderRadius, + ), + ) + : null, child: Row( children: [ if (widget.leading != null) widget.leading!, @@ -465,7 +538,7 @@ class _MoonAccordionState extends State with SingleTickerProvider @override Widget build(BuildContext context) { final Duration effectiveTransitionDuration = widget.transitionDuration ?? - context.moonTheme?.accordionTheme.properties.transitionDuration ?? + context.moonTheme?.accordionTheme.itemProperties.transitionDuration ?? const Duration(milliseconds: 200); _animationController ??= AnimationController(duration: effectiveTransitionDuration, vsync: this); @@ -474,7 +547,7 @@ class _MoonAccordionState extends State with SingleTickerProvider final bool shouldRemoveChildren = closed && !widget.maintainState; final Color effectiveDividerColor = - widget.dividerColor ?? context.moonTheme?.accordionTheme.colors.dividerColor ?? MoonColors.light.beerus; + widget.dividerColor ?? context.moonTheme?.accordionTheme.itemColors.dividerColor ?? MoonColors.light.beerus; final Widget result = Offstage( offstage: closed, @@ -482,7 +555,7 @@ class _MoonAccordionState extends State with SingleTickerProvider enabled: !closed, child: Column( children: [ - if (widget.showDivider) Container(height: 1, color: effectiveDividerColor), + if (widget.showDivider && !widget.hasContentOutside) Container(height: 1, color: effectiveDividerColor), Padding( padding: widget.childrenPadding ?? EdgeInsets.zero, child: Column(