From bfbeabe7ec7bceae717e0a137c0ef0495c3491b0 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Fri, 28 Apr 2023 18:40:40 +0200 Subject: [PATCH] feat(ui): add pricing card --- .../lib/src/core/utils/text_wrapper.dart | 7 + ...symbol_button_theme_extension_default.dart | 3 +- .../lib/src/domain/entities/cards/cards.dart | 1 + .../cards/pricing_card_component.dart | 99 ++++++++++++ .../cards/pricing_card_component.g.dart | 50 ++++++ .../wyatt_ui_kit/example/lib/cards/cards.dart | 3 + .../lib/cards/pricing_card/pricing_cards.dart | 148 ++++++++++++++++++ .../rich_text_builders.dart | 4 +- .../example/lib/theme/constants.dart | 8 + .../lib/src/components/cards/cards.dart | 1 + .../cards/pricing_card/pricing_card.dart | 136 ++++++++++++++++ .../cards/pricing_card/pricing_card.g.dart | 102 ++++++++++++ .../widgets/pricing_card_features.dart | 61 ++++++++ .../widgets/pricing_card_titles.dart | 139 ++++++++++++++++ .../card_theme_extension_impl.dart | 10 +- 15 files changed, 766 insertions(+), 6 deletions(-) create mode 100644 packages/wyatt_ui_components/lib/src/domain/entities/cards/pricing_card_component.dart create mode 100644 packages/wyatt_ui_components/lib/src/domain/entities/cards/pricing_card_component.g.dart create mode 100644 packages/wyatt_ui_kit/example/lib/cards/pricing_card/pricing_cards.dart create mode 100644 packages/wyatt_ui_kit/lib/src/components/cards/pricing_card/pricing_card.dart create mode 100644 packages/wyatt_ui_kit/lib/src/components/cards/pricing_card/pricing_card.g.dart create mode 100644 packages/wyatt_ui_kit/lib/src/components/cards/pricing_card/widgets/pricing_card_features.dart create mode 100644 packages/wyatt_ui_kit/lib/src/components/cards/pricing_card/widgets/pricing_card_titles.dart diff --git a/packages/wyatt_ui_components/lib/src/core/utils/text_wrapper.dart b/packages/wyatt_ui_components/lib/src/core/utils/text_wrapper.dart index bed9dd99..1a1eac95 100644 --- a/packages/wyatt_ui_components/lib/src/core/utils/text_wrapper.dart +++ b/packages/wyatt_ui_components/lib/src/core/utils/text_wrapper.dart @@ -121,4 +121,11 @@ class TextWrapper { /// that, the selection color defaults to [DefaultSelectionStyle.defaultColor] /// (semi-transparent grey). final Color? selectionColor; + + @override + String toString() => 'TextWrapper(data: $data, style: $style, ' + 'gradientColors: $gradientColors, textAlign: $textAlign, ' + 'textDirection: $textDirection, softWrap: $softWrap, ' + 'overflow: $overflow, maxLines: $maxLines, ' + 'selectionColor: $selectionColor)'; } diff --git a/packages/wyatt_ui_components/lib/src/data/theme_extensions/button_theme_extension/symbol_button_theme_extension_default.dart b/packages/wyatt_ui_components/lib/src/data/theme_extensions/button_theme_extension/symbol_button_theme_extension_default.dart index 4a781f13..3a36930f 100644 --- a/packages/wyatt_ui_components/lib/src/data/theme_extensions/button_theme_extension/symbol_button_theme_extension_default.dart +++ b/packages/wyatt_ui_components/lib/src/data/theme_extensions/button_theme_extension/symbol_button_theme_extension_default.dart @@ -40,8 +40,7 @@ class SymbolButtonThemeExtensionDefault extends SymbolButtonThemeExtension { final textColor = MultiColor.single(theme.textTheme.bodyMedium?.color); final style = SymbolButtonStyle( - labelStyle: - theme.textTheme.labelLarge?.copyWith(color: textColor.color), + labelStyle: theme.textTheme.labelLarge?.copyWith(color: textColor.color), dimension: theme.buttonTheme.height * 1.5, radius: (theme.buttonTheme.shape is RoundedRectangleBorder) ? (theme.buttonTheme.shape as RoundedRectangleBorder).borderRadius diff --git a/packages/wyatt_ui_components/lib/src/domain/entities/cards/cards.dart b/packages/wyatt_ui_components/lib/src/domain/entities/cards/cards.dart index a7840347..6a1cc1a5 100644 --- a/packages/wyatt_ui_components/lib/src/domain/entities/cards/cards.dart +++ b/packages/wyatt_ui_components/lib/src/domain/entities/cards/cards.dart @@ -17,5 +17,6 @@ export './card_component.dart'; export './information_card_component.dart'; export './portfolio_card_component.dart'; +export './pricing_card_component.dart'; export './quote_card_component.dart'; export './skill_card_component.dart'; diff --git a/packages/wyatt_ui_components/lib/src/domain/entities/cards/pricing_card_component.dart b/packages/wyatt_ui_components/lib/src/domain/entities/cards/pricing_card_component.dart new file mode 100644 index 00000000..15961dd0 --- /dev/null +++ b/packages/wyatt_ui_components/lib/src/domain/entities/cards/pricing_card_component.dart @@ -0,0 +1,99 @@ +// Copyright (C) 2023 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:flutter/widgets.dart'; +import 'package:wyatt_component_copy_with_extension/wyatt_component_copy_with_extension.dart'; +import 'package:wyatt_ui_components/wyatt_ui_components.dart'; + +part 'pricing_card_component.g.dart'; + +class PricingLine { + const PricingLine({ + required this.data, + this.icon, + }); + + final TextWrapper data; + final Widget? icon; + + @override + String toString() => 'PricingLine(data: $data, icon: $icon)'; +} + +class Pricing { + const Pricing({ + required this.price, + this.period, + this.hasAsterisk = false, + }); + + final TextWrapper price; + final TextWrapper? period; + + /// If [hasAsterisk] is true then the price will be displayed with + /// an asterisk to emphasize that it is a special price. + final bool hasAsterisk; +} + +@ComponentProxyExtension() +abstract class PricingCardComponent extends CardComponent + with CopyWithMixin<$PricingCardComponentCWProxy> { + const PricingCardComponent({ + this.axis, + this.title, + this.pricing, + this.description, + this.features, + this.cta, + super.radius, + super.padding, + super.borderColors, + super.backgroundColors, + super.stroke, + super.minSize, + super.maxSize, + super.shadow, + super.titleStyle, + super.subtitleStyle, + super.bodyStyle, + super.background, + super.key, + }); + + /// Axis of the card + /// + /// If [axis] is [Axis.horizontal] then only the first icon will be displayed. + /// Used in header of the card. + final Axis? axis; + + /// Title of the card + /// + /// If [axis] is [Axis.vertical] then icon of the title will be displayed + /// above the title. + final PricingLine? title; + + /// Pricing of the card + final Pricing? pricing; + + /// Description of the card + final TextWrapper? description; + + /// List of special features of the card + final List? features; + + /// CTA of the card + final Widget? cta; +} diff --git a/packages/wyatt_ui_components/lib/src/domain/entities/cards/pricing_card_component.g.dart b/packages/wyatt_ui_components/lib/src/domain/entities/cards/pricing_card_component.g.dart new file mode 100644 index 00000000..3f39a445 --- /dev/null +++ b/packages/wyatt_ui_components/lib/src/domain/entities/cards/pricing_card_component.g.dart @@ -0,0 +1,50 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'pricing_card_component.dart'; + +// ************************************************************************** +// ComponentProxyGenerator +// ************************************************************************** + +abstract class $PricingCardComponentCWProxy { + PricingCardComponent axis(Axis? axis); + PricingCardComponent title(PricingLine? title); + PricingCardComponent pricing(Pricing? pricing); + PricingCardComponent description(TextWrapper? description); + PricingCardComponent features(List? features); + PricingCardComponent cta(Widget? cta); + PricingCardComponent radius(BorderRadiusGeometry? radius); + PricingCardComponent padding(EdgeInsetsGeometry? padding); + PricingCardComponent borderColors(MultiColor? borderColors); + PricingCardComponent backgroundColors(MultiColor? backgroundColors); + PricingCardComponent stroke(double? stroke); + PricingCardComponent minSize(Size? minSize); + PricingCardComponent maxSize(Size? maxSize); + PricingCardComponent shadow(BoxShadow? shadow); + PricingCardComponent titleStyle(TextStyle? titleStyle); + PricingCardComponent subtitleStyle(TextStyle? subtitleStyle); + PricingCardComponent bodyStyle(TextStyle? bodyStyle); + PricingCardComponent background(Widget? background); + PricingCardComponent key(Key? key); + PricingCardComponent call({ + Axis? axis, + PricingLine? title, + Pricing? pricing, + TextWrapper? description, + List? features, + Widget? cta, + BorderRadiusGeometry? radius, + EdgeInsetsGeometry? padding, + MultiColor? borderColors, + MultiColor? backgroundColors, + double? stroke, + Size? minSize, + Size? maxSize, + BoxShadow? shadow, + TextStyle? titleStyle, + TextStyle? subtitleStyle, + TextStyle? bodyStyle, + Widget? background, + Key? key, + }); +} diff --git a/packages/wyatt_ui_kit/example/lib/cards/cards.dart b/packages/wyatt_ui_kit/example/lib/cards/cards.dart index 9037385f..5f685a09 100644 --- a/packages/wyatt_ui_kit/example/lib/cards/cards.dart +++ b/packages/wyatt_ui_kit/example/lib/cards/cards.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:wyatt_ui_kit_example/cards/information_card/information_cards.dart'; import 'package:wyatt_ui_kit_example/cards/portfolio_card/portfolio_cards.dart'; +import 'package:wyatt_ui_kit_example/cards/pricing_card/pricing_cards.dart'; import 'package:wyatt_ui_kit_example/cards/quote_card/quote_cards.dart'; import 'package:wyatt_ui_kit_example/cards/skill_card/skill_cards.dart'; import 'package:wyatt_ui_kit_example/demo_page.dart'; @@ -24,6 +25,8 @@ class Cards extends DemoPage { ), ), const Gap(20), + const PricingCards(), + const Gap(20), const InformationCards(), const Gap(20), const PortfolioCards(), diff --git a/packages/wyatt_ui_kit/example/lib/cards/pricing_card/pricing_cards.dart b/packages/wyatt_ui_kit/example/lib/cards/pricing_card/pricing_cards.dart new file mode 100644 index 00000000..b7e52425 --- /dev/null +++ b/packages/wyatt_ui_kit/example/lib/cards/pricing_card/pricing_cards.dart @@ -0,0 +1,148 @@ +// Copyright (C) 2023 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:wyatt_ui_components/wyatt_ui_components.dart'; +import 'package:wyatt_ui_kit/wyatt_ui_kit.dart'; +import 'package:wyatt_ui_kit_example/theme/constants.dart'; + +class PricingCards extends StatelessWidget { + const PricingCards({super.key}); + + @override + Widget build(BuildContext context) => Column( + children: [ + Text( + 'Pricing Cards', + style: Theme.of(context).textTheme.titleMedium, + ), + const Gap(20), + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + PricingCard( + title: const PricingLine( + data: TextWrapper('Lorem Ipsum'), + icon: Icon( + Icons.ac_unit_rounded, + ), + ), + pricing: const Pricing( + price: TextWrapper('450€'), + period: TextWrapper('/ jour'), + hasAsterisk: true, + ), + description: const TextWrapper( + 'Cupidatat reprehenderit aliqua eiusmod Lorem. ' + 'Qui ipsum id ea ea nulla labore aute ullamco aute ' + 'quis elit ut amet velit. Incididunt fugiat proident ' + 'proident deserunt tempor Lorem cillum qui do ' + 'ullamco Lorem magna ipsum. Ullamco cupidatat velit '), + features: const [ + PricingLine( + icon: Icon( + Icons.ac_unit_rounded, + ), + data: TextWrapper( + 'Nulla labore aliquip dolor aliqua elit anim quis non officia laboris ad.', + ), + ), + PricingLine( + icon: Icon( + Icons.ac_unit_rounded, + ), + data: TextWrapper( + 'Eu pariatur non et sint minim aute pariatur amet officia.', + ), + ), + ], + cta: context.components.flatButtonComponent.call( + label: const TextWrapper( + 'Contactez-nous', + style: TextStyle(color: Colors.white), + ), + normalStyle: FlatButtonThemeExtensionImpl.dark( + theme: Theme.of(context), + ).normalStyle?.copyWith( + backgroundColors: + const MultiColor(Constants.blueBtnGradient), + borderColors: + const MultiColor(Constants.blueBtnGradient), + ), + ), + ), + const Gap(20), + PricingCard( + title: const PricingLine( + data: TextWrapper('Lorem Ipsum'), + icon: Icon( + Icons.ac_unit_rounded, + ), + ), + pricing: const Pricing( + price: TextWrapper('350€'), + period: TextWrapper('/ jour'), + hasAsterisk: true, + ), + features: const [ + PricingLine( + icon: Icon( + Icons.ac_unit_rounded, + ), + data: TextWrapper( + 'Nulla labore aliquip dolor aliqua elit anim quis non officia laboris ad.', + ), + ), + PricingLine( + icon: Icon( + Icons.ac_unit_rounded, + ), + data: TextWrapper( + 'Eu pariatur non et sint minim aute pariatur amet officia.', + ), + ), + PricingLine( + icon: Icon( + Icons.ac_unit_rounded, + ), + data: TextWrapper( + 'Nulla labore aliquip dolor aliqua elit anim quis non officia laboris ad.', + ), + ), + ], + cta: context.components.flatButtonComponent.call( + label: const TextWrapper( + 'Contactez-nous', + style: TextStyle(color: Colors.white), + ), + normalStyle: FlatButtonThemeExtensionImpl.dark( + theme: Theme.of(context), + ).normalStyle?.copyWith( + backgroundColors: + const MultiColor(Constants.purpleGradient), + borderColors: + const MultiColor(Constants.purpleGradient), + ), + ), + ), + ], + ), + const Gap(20), + ], + ); +} diff --git a/packages/wyatt_ui_kit/example/lib/rich_text_builders/rich_text_builders.dart b/packages/wyatt_ui_kit/example/lib/rich_text_builders/rich_text_builders.dart index ef82c3f5..e85bfcfc 100644 --- a/packages/wyatt_ui_kit/example/lib/rich_text_builders/rich_text_builders.dart +++ b/packages/wyatt_ui_kit/example/lib/rich_text_builders/rich_text_builders.dart @@ -27,7 +27,7 @@ class RichTextBuilders extends DemoPage { @override Widget build(BuildContext context) => SelectionArea( - child: ListView( + child: ListView( cacheExtent: 1000, children: [ const Gap(20), @@ -61,5 +61,5 @@ class RichTextBuilders extends DemoPage { const Gap(20), ], ), - ); + ); } diff --git a/packages/wyatt_ui_kit/example/lib/theme/constants.dart b/packages/wyatt_ui_kit/example/lib/theme/constants.dart index 33152394..bf42912b 100644 --- a/packages/wyatt_ui_kit/example/lib/theme/constants.dart +++ b/packages/wyatt_ui_kit/example/lib/theme/constants.dart @@ -25,6 +25,9 @@ abstract class Constants { static const blue3 = Color(0xFF1A84F7); static const blue4 = Color(0xFF1344E4); + static const blue1Btn = Color(0xFF3C97FB); + static const blue2Btn = Color(0xFF446DF4); + static const grey1 = Color(0xFF60656A); static const grey2 = Color(0xFF383C40); @@ -37,10 +40,15 @@ abstract class Constants { static const red1 = Color(0xFFFB5E3C); static const red2 = Color(0xFFF44464); + static const purple1 = Color(0xFFB79EFF); + static const purple2 = Color(0xFF6865F2); + static const blueGradient = [blue1, blue2]; + static const blueBtnGradient = [blue1Btn, blue2Btn]; static const blueDarkGradient = [blue3, blue4]; static const greyGradient = [grey1, grey2]; static const greyDarkGradient = [grey3, grey4]; static const greenGradient = [green1, green2]; static const redGradient = [red1, red2]; + static const purpleGradient = [purple1, purple2]; } diff --git a/packages/wyatt_ui_kit/lib/src/components/cards/cards.dart b/packages/wyatt_ui_kit/lib/src/components/cards/cards.dart index cfd0f523..2b5be8f3 100644 --- a/packages/wyatt_ui_kit/lib/src/components/cards/cards.dart +++ b/packages/wyatt_ui_kit/lib/src/components/cards/cards.dart @@ -16,5 +16,6 @@ export './information_card/information_card.dart'; export './portfolio_card/portfolio_card.dart'; +export './pricing_card/pricing_card.dart'; export './quote_card/quote_card.dart'; export './skill_card/skill_card.dart'; diff --git a/packages/wyatt_ui_kit/lib/src/components/cards/pricing_card/pricing_card.dart b/packages/wyatt_ui_kit/lib/src/components/cards/pricing_card/pricing_card.dart new file mode 100644 index 00000000..46719484 --- /dev/null +++ b/packages/wyatt_ui_kit/lib/src/components/cards/pricing_card/pricing_card.dart @@ -0,0 +1,136 @@ +// Copyright (C) 2023 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:wyatt_component_copy_with_extension/wyatt_component_copy_with_extension.dart'; +import 'package:wyatt_ui_components/wyatt_ui_components.dart'; +import 'package:wyatt_ui_kit/src/components/cards/pricing_card/widgets/pricing_card_features.dart'; +import 'package:wyatt_ui_kit/src/components/cards/pricing_card/widgets/pricing_card_titles.dart'; +import 'package:wyatt_ui_kit/src/components/cards/widgets/card_text.dart'; +import 'package:wyatt_ui_kit/src/components/cards/widgets/card_wrapper.dart'; +import 'package:wyatt_ui_kit/src/core/design_system/colors.dart'; +import 'package:wyatt_ui_kit/wyatt_ui_kit.dart'; + +part 'pricing_card.g.dart'; + +const _bodyTopSpacing = 25.0; + +@ComponentCopyWithExtension() +class PricingCard extends PricingCardComponent with $PricingCardCWMixin { + const PricingCard({ + super.axis, + super.title, + super.pricing, + super.description, + super.features, + super.cta, + super.radius, + super.padding, + super.borderColors, + super.backgroundColors, + super.stroke, + super.minSize, + super.maxSize, + super.shadow, + super.titleStyle, + super.subtitleStyle, + super.bodyStyle, + super.background, + super.key, + }); + + @override + Widget build(BuildContext context) { + final themeTitleStyle = ThemeHelper.maybeGetElement( + [ + titleStyle, + Theme.of(context).extension()?.titleStyle, + CardThemeExtensionDefault.from(Theme.of(context)).titleStyle, + ], + ); + + final themePricingStyle = ThemeHelper.maybeGetElement( + [ + pricing?.price.style, + context.textTheme.bodyMedium + ?.copyWith(fontSize: 50, fontWeight: FontWeight.bold), + ], + ); + + final themePeriodStyle = ThemeHelper.maybeGetElement( + [ + subtitleStyle, + context.textTheme.bodyMedium?.copyWith( + fontSize: 20, + fontWeight: FontWeight.w600, + color: WyattColors.gray1, + ), + ], + ); + + final themeBodyStyle = ThemeHelper.maybeGetElement( + [ + bodyStyle, + Theme.of(context).extension()?.bodyStyle, + CardThemeExtensionDefault.from(Theme.of(context)).bodyStyle, + ], + ); + + return CardWrapper( + background: background, + padding: padding, + radius: radius, + backgroundColors: backgroundColors, + borderColors: borderColors, + shadow: shadow, + stroke: stroke, + maxSize: maxSize, + minSize: minSize, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + PricingCardTitles( + axis: axis, + title: title, + pricing: pricing, + titleStyle: themeTitleStyle, + pricingStyle: themePricingStyle, + periodStyle: themePeriodStyle, + ), + const Gap(_bodyTopSpacing), + if (description != null) ...[ + CardText.fromWrapper( + description!, + style: themeBodyStyle, + ), + const Gap(_bodyTopSpacing), + ], + if (features != null) ...[ + PricingCardFeatures( + features: features, + bodyStyle: themeBodyStyle, + ), + const Gap(_bodyTopSpacing), + ], + if (cta != null) ...[ + SelectionContainer.disabled(child: cta!), + ], + ], + ), + ); + } +} diff --git a/packages/wyatt_ui_kit/lib/src/components/cards/pricing_card/pricing_card.g.dart b/packages/wyatt_ui_kit/lib/src/components/cards/pricing_card/pricing_card.g.dart new file mode 100644 index 00000000..25c82ed3 --- /dev/null +++ b/packages/wyatt_ui_kit/lib/src/components/cards/pricing_card/pricing_card.g.dart @@ -0,0 +1,102 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'pricing_card.dart'; + +// ************************************************************************** +// ComponentCopyWithGenerator +// ************************************************************************** + +class $PricingCardCWProxyImpl implements $PricingCardComponentCWProxy { + const $PricingCardCWProxyImpl(this._value); + final PricingCard _value; + @override + PricingCard axis(Axis? axis) => this(axis: axis); + @override + PricingCard title(PricingLine? title) => this(title: title); + @override + PricingCard pricing(Pricing? pricing) => this(pricing: pricing); + @override + PricingCard description(TextWrapper? description) => + this(description: description); + @override + PricingCard features(List? features) => this(features: features); + @override + PricingCard cta(Widget? cta) => this(cta: cta); + @override + PricingCard radius(BorderRadiusGeometry? radius) => this(radius: radius); + @override + PricingCard padding(EdgeInsetsGeometry? padding) => this(padding: padding); + @override + PricingCard borderColors(MultiColor? borderColors) => + this(borderColors: borderColors); + @override + PricingCard backgroundColors(MultiColor? backgroundColors) => + this(backgroundColors: backgroundColors); + @override + PricingCard stroke(double? stroke) => this(stroke: stroke); + @override + PricingCard minSize(Size? minSize) => this(minSize: minSize); + @override + PricingCard maxSize(Size? maxSize) => this(maxSize: maxSize); + @override + PricingCard shadow(BoxShadow? shadow) => this(shadow: shadow); + @override + PricingCard titleStyle(TextStyle? titleStyle) => this(titleStyle: titleStyle); + @override + PricingCard subtitleStyle(TextStyle? subtitleStyle) => + this(subtitleStyle: subtitleStyle); + @override + PricingCard bodyStyle(TextStyle? bodyStyle) => this(bodyStyle: bodyStyle); + @override + PricingCard background(Widget? background) => this(background: background); + @override + PricingCard key(Key? key) => this(key: key); + @override + PricingCard call({ + Axis? axis, + PricingLine? title, + Pricing? pricing, + TextWrapper? description, + List? features, + Widget? cta, + BorderRadiusGeometry? radius, + EdgeInsetsGeometry? padding, + MultiColor? borderColors, + MultiColor? backgroundColors, + double? stroke, + Size? minSize, + Size? maxSize, + BoxShadow? shadow, + TextStyle? titleStyle, + TextStyle? subtitleStyle, + TextStyle? bodyStyle, + Widget? background, + Key? key, + }) => + PricingCard( + axis: axis ?? _value.axis, + title: title ?? _value.title, + pricing: pricing ?? _value.pricing, + description: description ?? _value.description, + features: features ?? _value.features, + cta: cta ?? _value.cta, + radius: radius ?? _value.radius, + padding: padding ?? _value.padding, + borderColors: borderColors ?? _value.borderColors, + backgroundColors: backgroundColors ?? _value.backgroundColors, + stroke: stroke ?? _value.stroke, + minSize: minSize ?? _value.minSize, + maxSize: maxSize ?? _value.maxSize, + shadow: shadow ?? _value.shadow, + titleStyle: titleStyle ?? _value.titleStyle, + subtitleStyle: subtitleStyle ?? _value.subtitleStyle, + bodyStyle: bodyStyle ?? _value.bodyStyle, + background: background ?? _value.background, + key: key ?? _value.key, + ); +} + +mixin $PricingCardCWMixin on Component { + $PricingCardComponentCWProxy get copyWith => + $PricingCardCWProxyImpl(this as PricingCard); +} diff --git a/packages/wyatt_ui_kit/lib/src/components/cards/pricing_card/widgets/pricing_card_features.dart b/packages/wyatt_ui_kit/lib/src/components/cards/pricing_card/widgets/pricing_card_features.dart new file mode 100644 index 00000000..390d8bfb --- /dev/null +++ b/packages/wyatt_ui_kit/lib/src/components/cards/pricing_card/widgets/pricing_card_features.dart @@ -0,0 +1,61 @@ +// Copyright (C) 2023 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:wyatt_ui_components/wyatt_ui_components.dart'; +import 'package:wyatt_ui_kit/src/components/cards/widgets/card_text.dart'; + +class PricingCardFeatures extends StatelessWidget { + const PricingCardFeatures({ + super.key, + this.features, + this.bodyStyle, + }); + + /// List of special features of the card + final List? features; + + /// Body style of the card. + final TextStyle? bodyStyle; + + @override + Widget build(BuildContext context) => Column( + children: features! + .map( + (feature) => Padding( + padding: EdgeInsets.only( + top: features?.indexOf(feature) == 0 ? 0 : 10, + ), + child: Row( + children: [ + if (feature.icon != null) ...[ + feature.icon!, + ], + const Gap(10), + Expanded( + child: CardText.fromWrapper( + feature.data, + style: bodyStyle, + ), + ), + ], + ), + ), + ) + .toList(), + ); +} diff --git a/packages/wyatt_ui_kit/lib/src/components/cards/pricing_card/widgets/pricing_card_titles.dart b/packages/wyatt_ui_kit/lib/src/components/cards/pricing_card/widgets/pricing_card_titles.dart new file mode 100644 index 00000000..4ecd2ac2 --- /dev/null +++ b/packages/wyatt_ui_kit/lib/src/components/cards/pricing_card/widgets/pricing_card_titles.dart @@ -0,0 +1,139 @@ +// Copyright (C) 2023 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; +import 'package:wyatt_ui_components/wyatt_ui_components.dart'; +import 'package:wyatt_ui_kit/src/components/cards/widgets/card_text.dart'; + +const _titlesLineSpacing = 5.0; +const _bodyTopSpacing = 25.0; + +class PricingCardTitles extends StatelessWidget { + const PricingCardTitles({ + this.axis, + this.title, + this.pricing, + this.titleStyle, + this.pricingStyle, + this.periodStyle, + super.key, + }); + + /// The axis of the card header. + final Axis? axis; + + /// Title of the card + /// + /// If [axis] is [Axis.vertical] then icon of the title will be displayed + /// above the title. + final PricingLine? title; + + /// Pricing of the card + final Pricing? pricing; + + /// Styles the title of the card. + final TextStyle? titleStyle; + + /// Styles the pricing of the card. + final TextStyle? pricingStyle; + + /// Styles the period of the card. + final TextStyle? periodStyle; + + @override + Widget build(BuildContext context) { + final price = (pricing != null) + ? CardText.fromWrapper( + pricing!.price, + style: pricingStyle, + textType: TextType.title, + ) + : null; + + final period = ((pricing != null) && (pricing?.period != null)) + ? CardText.fromWrapper( + pricing!.period!, + style: periodStyle, + textType: TextType.subtitle, + ) + : null; + + return Column( + children: [ + if (title != null) ...[ + if (axis == Axis.vertical) ...[ + if (title?.icon != null) ...[ + title!.icon!, + const Gap(_titlesLineSpacing), + ], + CardText.fromWrapper( + title!.data, + style: titleStyle, + textType: TextType.title, + ), + const Gap(_bodyTopSpacing), + ] else ...[ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (title?.icon != null) ...[ + title!.icon!, + const Gap(_titlesLineSpacing), + ], + CardText.fromWrapper( + title!.data, + style: titleStyle, + textType: TextType.title, + ), + ], + ), + const Gap(_bodyTopSpacing), + ] + ], + RichText( + text: TextSpan( + children: [ + if (price != null) ...[ + TextSpan( + text: price.text.data, + style: price.text.style, + ) + ], + if (pricing?.hasAsterisk ?? false) ...[ + WidgetSpan( + child: Transform.translate( + offset: const Offset(5, -15), + child: const Text( + '* ', + style: TextStyle(fontSize: 18), + ), + ), + ), + ], + if (period != null) ...[ + TextSpan( + text: period.text.data, + style: period.text.style, + ) + ], + ], + ), + ), + ], + ); + } +} diff --git a/packages/wyatt_ui_kit/lib/src/data/theme_extensions/card_theme_extension_impl.dart b/packages/wyatt_ui_kit/lib/src/data/theme_extensions/card_theme_extension_impl.dart index 294a0d7e..1439087b 100644 --- a/packages/wyatt_ui_kit/lib/src/data/theme_extensions/card_theme_extension_impl.dart +++ b/packages/wyatt_ui_kit/lib/src/data/theme_extensions/card_theme_extension_impl.dart @@ -50,7 +50,10 @@ class CardThemeExtensionImpl extends CardThemeExtension { stroke: 1, backgroundColors: const MultiColor.single(WyattColors.light), borderColors: WyattColors.lightGradient, - titleStyle: theme.textTheme.titleLarge, + titleStyle: theme.textTheme.titleLarge?.copyWith( + fontSize: 26, + fontWeight: FontWeight.w600, + ), subtitleStyle: theme.textTheme.titleMedium, bodyStyle: theme.textTheme.bodyMedium?.copyWith( height: 1.5, @@ -76,7 +79,10 @@ class CardThemeExtensionImpl extends CardThemeExtension { stroke: 1, backgroundColors: WyattColors.grayBgOpacityGradient, borderColors: WyattColors.grayGradient, - titleStyle: theme.textTheme.titleLarge, + titleStyle: theme.textTheme.titleLarge?.copyWith( + fontSize: 26, + fontWeight: FontWeight.w600, + ), subtitleStyle: theme.textTheme.titleMedium, bodyStyle: theme.textTheme.bodyMedium?.copyWith( height: 1.5,