feat(ui): add pricing card
continuous-integration/drone/pr Build is failing

This commit is contained in:
2023-04-28 18:40:40 +02:00
parent 01a5619dc5
commit bfbeabe7ec
15 changed files with 766 additions and 6 deletions
@@ -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';
@@ -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 <https://www.gnu.org/licenses/>.
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<TextStyle, TextStyle>(
[
titleStyle,
Theme.of(context).extension<CardThemeExtension>()?.titleStyle,
CardThemeExtensionDefault.from(Theme.of(context)).titleStyle,
],
);
final themePricingStyle = ThemeHelper.maybeGetElement<TextStyle, TextStyle>(
[
pricing?.price.style,
context.textTheme.bodyMedium
?.copyWith(fontSize: 50, fontWeight: FontWeight.bold),
],
);
final themePeriodStyle = ThemeHelper.maybeGetElement<TextStyle, TextStyle>(
[
subtitleStyle,
context.textTheme.bodyMedium?.copyWith(
fontSize: 20,
fontWeight: FontWeight.w600,
color: WyattColors.gray1,
),
],
);
final themeBodyStyle = ThemeHelper.maybeGetElement<TextStyle, TextStyle>(
[
bodyStyle,
Theme.of(context).extension<CardThemeExtension>()?.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!),
],
],
),
);
}
}
@@ -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<PricingLine>? 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<PricingLine>? 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);
}
@@ -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 <https://www.gnu.org/licenses/>.
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<PricingLine>? 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(),
);
}
@@ -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 <https://www.gnu.org/licenses/>.
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,
)
],
],
),
),
],
);
}
}
@@ -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,