From 946c4d8fdab2ad6a87bbf72fb3def47cbd06947c Mon Sep 17 00:00:00 2001 From: Prajapati Chintan Date: Sat, 11 Feb 2023 16:12:39 +0530 Subject: [PATCH 1/4] Added: help and secondary colour swatches --- lib/vaahextendflutter/base/base_theme.dart | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/vaahextendflutter/base/base_theme.dart b/lib/vaahextendflutter/base/base_theme.dart index 6c85de60..3804bd94 100644 --- a/lib/vaahextendflutter/base/base_theme.dart +++ b/lib/vaahextendflutter/base/base_theme.dart @@ -3,10 +3,12 @@ import 'package:flutter/material.dart'; class BaseTheme { static const Map colors = { 'primary': _primary, + 'secondary': _secondary, 'info': _info, 'success': _success, 'warning': _warning, 'danger': _danger, + 'help': _help, 'white': _white, 'black': _black, }; @@ -28,6 +30,22 @@ const MaterialColor _primary = MaterialColor( }, ); +const MaterialColor _secondary = MaterialColor( + 0xFF787878, + { + 50: Color(0xFFF8F8F8), + 100: Color(0xFFF8F8F8), + 200: Color(0xFFF1F1F1), + 300: Color(0xFFD6D6D6), + 400: Color(0xFFAEAEAE), + 500: Color(0xFF787878), + 600: Color(0xFF675759), + 700: Color(0xFF563C40), + 800: Color(0xFF45262E), + 900: Color(0xFF391723), + }, +); + const MaterialColor _success = MaterialColor( 0xFF4FB52D, { @@ -92,6 +110,22 @@ const MaterialColor _danger = MaterialColor( }, ); +const MaterialColor _help = MaterialColor( + 0xFFBE00FF, + { + 50: Color(0xFFFFCCFD), + 100: Color(0xFFFFCCFD), + 200: Color(0xFFFA99FF), + 300: Color(0xFFED66FF), + 400: Color(0xFFDB3FFF), + 500: Color(0xFFBE00FF), + 600: Color(0xFF9300DB), + 700: Color(0xFF6E00B7), + 800: Color(0xFF4E0093), + 900: Color(0xFF38007A), + }, +); + const MaterialColor _white = MaterialColor( 0xFFFFFFFF, { From ebbc915e9cccf269fc8d7111d07e06731f2dcbe5 Mon Sep 17 00:00:00 2001 From: Prajapati Chintan Date: Wed, 15 Feb 2023 13:44:59 +0530 Subject: [PATCH 2/4] Added: Buttons --- lib/vaahextendflutter/helpers/enums.dart | 2 + .../widgets/atoms/buttons.dart | 403 ++++++++++++++++++ .../pages/ui/components/buttons/buttons.dart | 120 ++++++ .../pages/ui/components/buttons/elevated.dart | 161 +++++++ .../pages/ui/components/buttons/extra.dart | 144 +++++++ .../pages/ui/components/buttons/icon.dart | 99 +++++ .../ui/components/buttons/icon_and_label.dart | 93 ++++ .../pages/ui/components/buttons/outlined.dart | 161 +++++++ .../pages/ui/components/buttons/text.dart | 161 +++++++ lib/views/pages/ui/index.dart | 3 + 10 files changed, 1347 insertions(+) create mode 100644 lib/vaahextendflutter/widgets/atoms/buttons.dart create mode 100644 lib/views/pages/ui/components/buttons/buttons.dart create mode 100644 lib/views/pages/ui/components/buttons/elevated.dart create mode 100644 lib/views/pages/ui/components/buttons/extra.dart create mode 100644 lib/views/pages/ui/components/buttons/icon.dart create mode 100644 lib/views/pages/ui/components/buttons/icon_and_label.dart create mode 100644 lib/views/pages/ui/components/buttons/outlined.dart create mode 100644 lib/views/pages/ui/components/buttons/text.dart diff --git a/lib/vaahextendflutter/helpers/enums.dart b/lib/vaahextendflutter/helpers/enums.dart index 36cff3c1..7463e537 100644 --- a/lib/vaahextendflutter/helpers/enums.dart +++ b/lib/vaahextendflutter/helpers/enums.dart @@ -1 +1,3 @@ enum InputSize { extraSmall, small, medium, large, extraLarge } + +enum ButtonType { primary, secondary, info, success, danger, warning, help, white } diff --git a/lib/vaahextendflutter/widgets/atoms/buttons.dart b/lib/vaahextendflutter/widgets/atoms/buttons.dart new file mode 100644 index 00000000..5db34ded --- /dev/null +++ b/lib/vaahextendflutter/widgets/atoms/buttons.dart @@ -0,0 +1,403 @@ +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + +import '../../app_theme.dart'; +import '../../helpers/constants.dart'; +import '../../helpers/enums.dart'; + +typedef OnPressed = void Function()?; + +Color? getColorForButtonType(ButtonType type) { + switch (type) { + case ButtonType.primary: + return AppTheme.colors['primary']; + case ButtonType.secondary: + return AppTheme.colors['secondary']; + case ButtonType.info: + return AppTheme.colors['info']; + case ButtonType.success: + return AppTheme.colors['success']; + case ButtonType.danger: + return AppTheme.colors['danger']; + case ButtonType.warning: + return AppTheme.colors['warning']; + case ButtonType.help: + return AppTheme.colors['help']; + default: + return null; + } +} + +class ButtonElevated extends StatelessWidget { + final OnPressed onPressed; + final String text; + final ButtonStyle? style; + final ButtonType? buttonType; + final Color? backgroundColor; + final Color? foregroundColor; + final double? fontSize; + final double? borderRadius; + final EdgeInsets? padding; + + const ButtonElevated({ + Key? key, + required this.onPressed, + required this.text, + this.style, + this.buttonType, + this.backgroundColor, + this.foregroundColor, + this.fontSize, + this.borderRadius, + this.padding, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + // assert(!(buttonType != null && backgroundColor != null)); + return ElevatedButton( + onPressed: onPressed, + style: style ?? + ElevatedButton.styleFrom( + backgroundColor: + backgroundColor ?? (buttonType == null ? null : getColorForButtonType(buttonType!)), + foregroundColor: foregroundColor, + shape: borderRadius == null + ? null + : RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius!), + ), + padding: padding, + ), + child: Text( + text, + style: TextStyle( + fontSize: fontSize, + ), + ), + ); + } +} + +class ButtonElevatedWithIcon extends StatelessWidget { + final OnPressed onPressed; + final String text; + final IconData? iconData; + final ButtonStyle? style; + final ButtonType? buttonType; + final Color? backgroundColor; + final Color? foregroundColor; + final double? fontSize; + final double? iconSize; + final double? borderRadius; + final EdgeInsets? padding; + + const ButtonElevatedWithIcon({ + Key? key, + required this.onPressed, + required this.text, + required this.iconData, + this.style, + this.buttonType, + this.backgroundColor, + this.foregroundColor, + this.fontSize, + this.iconSize = 16, + this.borderRadius, + this.padding, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ElevatedButton.icon( + onPressed: onPressed, + style: style ?? + ElevatedButton.styleFrom( + backgroundColor: + backgroundColor ?? (buttonType == null ? null : getColorForButtonType(buttonType!)), + foregroundColor: foregroundColor, + shape: borderRadius == null + ? null + : RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius!), + ), + padding: padding, + ), + label: Text( + text, + style: TextStyle( + fontSize: fontSize, + ), + ), + icon: FaIcon( + iconData, + size: iconSize, + ), + ); + } +} + +class ButtonOutlined extends StatelessWidget { + final OnPressed onPressed; + final String text; + final ButtonStyle? style; + final ButtonType? buttonType; + final Color? foregroundColor; + final double? fontSize; + final double? borderRadius; + final EdgeInsets? padding; + + const ButtonOutlined({ + Key? key, + required this.onPressed, + required this.text, + this.style, + this.buttonType, + this.foregroundColor, + this.fontSize, + this.borderRadius, + this.padding, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return OutlinedButton( + onPressed: onPressed, + style: style ?? + OutlinedButton.styleFrom( + side: BorderSide( + width: 2.0, + color: foregroundColor ?? getColorForButtonType(buttonType ?? ButtonType.primary)!, + ), + foregroundColor: + foregroundColor ?? (buttonType == null ? null : getColorForButtonType(buttonType!)), + shape: borderRadius == null + ? null + : RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius!), + ), + padding: padding, + ), + child: Text( + text, + style: TextStyle( + fontSize: fontSize, + ), + ), + ); + } +} + +class ButtonOutlinedWithIcon extends StatelessWidget { + final OnPressed onPressed; + final String text; + final IconData? iconData; + final ButtonStyle? style; + final ButtonType? buttonType; + final Color? foregroundColor; + final double? fontSize; + final double? iconSize; + final double? borderRadius; + final EdgeInsets? padding; + + const ButtonOutlinedWithIcon({ + Key? key, + required this.onPressed, + required this.text, + required this.iconData, + this.style, + this.buttonType, + this.foregroundColor, + this.fontSize, + this.iconSize = 16, + this.borderRadius, + this.padding, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return OutlinedButton.icon( + onPressed: onPressed, + style: style ?? + OutlinedButton.styleFrom( + side: BorderSide( + width: 2.0, + color: foregroundColor ?? getColorForButtonType(buttonType ?? ButtonType.primary)!, + ), + foregroundColor: + foregroundColor ?? (buttonType == null ? null : getColorForButtonType(buttonType!)), + shape: borderRadius == null + ? null + : RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius!), + ), + padding: padding, + ), + label: Text( + text, + style: TextStyle( + fontSize: fontSize, + ), + ), + icon: FaIcon( + iconData, + size: iconSize, + ), + ); + } +} + +class ButtonText extends StatelessWidget { + final OnPressed onPressed; + final String text; + final ButtonStyle? style; + final ButtonType? buttonType; + final Color? foregroundColor; + final double? fontSize; + final double? borderRadius; + final EdgeInsets? padding; + + const ButtonText({ + Key? key, + required this.onPressed, + required this.text, + this.style, + this.buttonType, + this.foregroundColor, + this.fontSize, + this.borderRadius, + this.padding, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return TextButton( + onPressed: onPressed, + style: style ?? + TextButton.styleFrom( + foregroundColor: + foregroundColor ?? (buttonType == null ? null : getColorForButtonType(buttonType!)), + shape: borderRadius == null + ? null + : RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius!), + ), + padding: padding, + ), + child: Text( + text, + style: TextStyle( + fontSize: fontSize, + ), + ), + ); + } +} + +class ButtonTextWithIcon extends StatelessWidget { + final OnPressed onPressed; + final String text; + final IconData? iconData; + final ButtonStyle? style; + final ButtonType? buttonType; + final Color? foregroundColor; + final double? fontSize; + final double? iconSize; + final double? borderRadius; + final EdgeInsets? padding; + + const ButtonTextWithIcon({ + Key? key, + required this.onPressed, + required this.text, + required this.iconData, + this.style, + this.buttonType, + this.foregroundColor, + this.fontSize, + this.iconSize = 16, + this.borderRadius, + this.padding, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return TextButton.icon( + onPressed: onPressed, + style: style ?? + TextButton.styleFrom( + foregroundColor: + foregroundColor ?? (buttonType == null ? null : getColorForButtonType(buttonType!)), + shape: borderRadius == null + ? null + : RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius!), + ), + padding: padding, + ), + label: Text( + text, + style: TextStyle( + fontSize: fontSize, + ), + ), + icon: FaIcon( + iconData, + size: iconSize, + ), + ); + } +} + +class ButtonIcon extends StatelessWidget { + final OnPressed onPressed; + final IconData? iconData; + final ButtonType? buttonType; + final Color? color; + final double? iconSize; + final bool enableBorder; + final double? borderRadius; + final EdgeInsets? padding; + + const ButtonIcon({ + Key? key, + required this.onPressed, + required this.iconData, + this.buttonType, + this.color, + this.iconSize = 20, + this.enableBorder = true, + this.borderRadius, + this.padding, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Material( + type: MaterialType.transparency, + child: Ink( + decoration: BoxDecoration( + border: !enableBorder + ? null + : Border.all( + width: 2.0, + color: color ?? getColorForButtonType(buttonType ?? ButtonType.primary)!, + ), + shape: BoxShape.circle, + ), + child: InkWell( + borderRadius: BorderRadius.circular(borderRadius ?? 1000.0), + onTap: onPressed, + child: Padding( + padding: padding ?? allPadding8, + child: FaIcon( + iconData, + size: iconSize, + color: color ?? getColorForButtonType(buttonType ?? ButtonType.primary), + ), + ), + ), + ), + ); + } +} diff --git a/lib/views/pages/ui/components/buttons/buttons.dart b/lib/views/pages/ui/components/buttons/buttons.dart new file mode 100644 index 00000000..7e07a819 --- /dev/null +++ b/lib/views/pages/ui/components/buttons/buttons.dart @@ -0,0 +1,120 @@ +import 'package:flutter/material.dart'; + +import './elevated.dart'; +import './extra.dart'; +import './icon.dart'; +import './icon_and_label.dart'; +import './outlined.dart'; +import './text.dart'; +import '../../../../../vaahextendflutter/app_theme.dart'; +import '../../../../../vaahextendflutter/helpers/constants.dart'; +import '../../../../../vaahextendflutter/helpers/styles.dart'; +import '../../../../../vaahextendflutter/widgets/atoms/app_expansion_panel.dart'; +import '../../../../../vaahextendflutter/widgets/atoms/container_with_rounded_border.dart'; +import '../../../../../vaahextendflutter/widgets/atoms/tab_options.dart'; + +class Buttons extends StatelessWidget { + const Buttons({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ContainerWithRoundedBorder( + width: double.infinity, + color: AppTheme.colors['white']!, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Buttons', style: TextStyles.semiBold7), + verticalMargin24, + TabOptions( + tabs: [ + TabOption( + name: 'Preview', + tab: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + ButtonElevatedPreview(), + Divider(height: defaultPadding * 2), + ButtonTextPreview(), + Divider(height: defaultPadding * 2), + ButtonOutlinedPreview(), + Divider(height: defaultPadding * 2), + ButtonIconPreview(), + Divider(height: defaultPadding * 2), + ButtonIconLabelPreview(), + Divider(height: defaultPadding * 2), + ButtonExtrasPreview(), + ], + ), + ), + TabOption( + name: 'Code', + tab: Column( + children: const [ + ExpansionPanelWrap( + title: 'Elevated Buttons', + child: ButtonElevatedCode(), + ), + verticalMargin8, + ExpansionPanelWrap( + title: 'Text Buttons', + child: ButtonTextCode(), + ), + verticalMargin8, + ExpansionPanelWrap( + title: 'Outlined Buttons', + child: ButtonOutlinedCode(), + ), + verticalMargin8, + ExpansionPanelWrap( + title: 'Icon Buttons', + child: ButtonIconCode(), + ), + verticalMargin8, + ExpansionPanelWrap( + title: 'Label and Icon Buttons', + child: ButtonIconLabelCode(), + ), + verticalMargin8, + ExpansionPanelWrap( + title: 'Button Properties', + child: ButtonExtrasCode(), + ), + ], + ), + ), + ], + ), + ], + ), + ); + } +} + +@immutable +class ExpansionPanelWrap extends StatelessWidget { + const ExpansionPanelWrap({ + Key? key, + required this.title, + required this.child, + }) : super(key: key); + + final String title; + final Widget child; + + @override + Widget build(BuildContext context) { + return Padding( + padding: bottomPadding1, + child: AppExpansionPanel( + padding: allPadding16, + textStyle: TextStyles.regular4?.copyWith(color: AppTheme.colors['primary']), + heading: title, + children: [ + child, + ], + ), + ); + } +} diff --git a/lib/views/pages/ui/components/buttons/elevated.dart b/lib/views/pages/ui/components/buttons/elevated.dart new file mode 100644 index 00000000..636d83b8 --- /dev/null +++ b/lib/views/pages/ui/components/buttons/elevated.dart @@ -0,0 +1,161 @@ +import 'package:flutter/material.dart'; + +import '../../../../../vaahextendflutter/helpers/constants.dart'; +import '../../../../../vaahextendflutter/helpers/enums.dart'; +import '../../../../../vaahextendflutter/helpers/styles.dart'; +import '../../../../../vaahextendflutter/widgets/atoms/buttons.dart'; +import '../code_preview.dart'; +import '../commons.dart'; + +class ButtonElevatedPreview extends StatelessWidget { + const ButtonElevatedPreview({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Elevated Buttons', style: heading), + verticalMargin16, + Wrap( + spacing: defaultPadding / 2, + children: [ + ButtonElevated( + onPressed: () {}, + text: "Info", + buttonType: ButtonType.info, + ), + ButtonElevated( + onPressed: () {}, + text: "Success", + buttonType: ButtonType.success, + ), + ButtonElevated( + onPressed: () {}, + text: "Warning", + buttonType: ButtonType.warning, + ), + ButtonElevated( + onPressed: () {}, + text: "Danger", + buttonType: ButtonType.danger, + ), + ButtonElevated( + onPressed: () {}, + text: "Help", + buttonType: ButtonType.help, + ), + ButtonElevated( + onPressed: () {}, + text: "Primary", + buttonType: ButtonType.primary, + ), + ButtonElevated( + onPressed: () {}, + text: "Secondary", + buttonType: ButtonType.secondary, + ), + ], + ), + ], + ); + } +} + +class ButtonElevatedCode extends StatelessWidget { + const ButtonElevatedCode({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Info', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonElevated(",', + ' onPressed: () {},', + ' text: "Info",', + ' buttonType: ButtonType.info,', + '),"' + ], + ), + verticalMargin8, + Text('Success', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonElevated(",', + ' onPressed: () {},', + ' text: "Success",', + ' buttonType: ButtonType.success,', + '),"' + ], + ), + verticalMargin8, + Text('Warning', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonElevated(",', + ' onPressed: () {},', + ' text: "Warning",', + ' buttonType: ButtonType.warning,', + '),"' + ], + ), + verticalMargin8, + Text('Danger', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonElevated(",', + ' onPressed: () {},', + ' text: "Danger",', + ' buttonType: ButtonType.danger,', + '),"' + ], + ), + verticalMargin8, + Text('Help', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonElevated(",', + ' onPressed: () {},', + ' text: "Help",', + ' buttonType: ButtonType.help,', + '),"' + ], + ), + verticalMargin8, + Text('Primary', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonElevated(",', + ' onPressed: () {},', + ' text: "Primary",', + ' buttonType: ButtonType.primary,', + '),"' + ], + ), + verticalMargin8, + Text('Secondary', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonElevated(",', + ' onPressed: () {},', + ' text: "Secondary",', + ' buttonType: ButtonType.secondary,', + '),"' + ], + ), + ], + ); + } +} diff --git a/lib/views/pages/ui/components/buttons/extra.dart b/lib/views/pages/ui/components/buttons/extra.dart new file mode 100644 index 00000000..1d7b3045 --- /dev/null +++ b/lib/views/pages/ui/components/buttons/extra.dart @@ -0,0 +1,144 @@ +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + +import '../../../../../vaahextendflutter/helpers/constants.dart'; +import '../../../../../vaahextendflutter/helpers/styles.dart'; +import '../../../../../vaahextendflutter/widgets/atoms/buttons.dart'; +import '../code_preview.dart'; +import '../commons.dart'; + +class ButtonExtrasPreview extends StatelessWidget { + const ButtonExtrasPreview({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Button Properties', style: heading), + verticalMargin16, + Wrap( + spacing: defaultPadding / 2, + children: [ + ButtonElevated( + onPressed: () {}, + text: "Background Color", + backgroundColor: Colors.pink, + ), + ButtonElevated( + onPressed: () {}, + text: "Foreground Color", + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white, + foregroundColor: Colors.black, + ), + ), + ButtonElevated( + onPressed: () {}, + text: "Button Style", + style: ElevatedButton.styleFrom( + backgroundColor: Colors.purple, + ), + ), + ButtonElevated( + onPressed: () {}, + text: "Border Radius", + borderRadius: 100, + ), + const Divider(), + ButtonElevatedWithIcon( + onPressed: () {}, + text: "Size", + iconData: FontAwesomeIcons.user, + fontSize: 22, + iconSize: 21, + padding: horizontalPadding32 + verticalPadding12, + ), + ], + ), + ], + ); + } +} + +class ButtonExtrasCode extends StatelessWidget { + const ButtonExtrasCode({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Background Color', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonElevated(', + ' onPressed: () {},', + ' text: "Background Color",', + ' backgroundColor: Colors.pink,', + '),', + ], + ), + verticalMargin8, + Text('Foreground Color', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonElevated(', + ' onPressed: () {},', + ' text: "Foreground Color",', + ' style: ElevatedButton.styleFrom(', + ' backgroundColor: Colors.white,', + ' foregroundColor: Colors.black,', + ' ),', + '),', + ], + ), + verticalMargin8, + Text('Button Style', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonElevated(', + ' onPressed: () {},', + ' text: "Button Style",', + ' style: ElevatedButton.styleFrom(', + ' backgroundColor: Colors.purple,', + ' ),', + '),', + ], + ), + verticalMargin8, + Text('Border Radius', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonElevated(', + ' onPressed: () {},', + ' text: "Border Radius",', + ' borderRadius: 100,', + '),', + ], + ), + verticalMargin8, + Text('Button Size', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonElevatedWithIcon(', + ' onPressed: () {},', + ' text: "Size",', + ' iconData: FontAwesomeIcons.user,', + ' fontSize: 22,', + ' iconSize: 21,', + ' padding: horizontalPadding32 + verticalPadding12,', + '),', + ], + ), + ], + ); + } +} diff --git a/lib/views/pages/ui/components/buttons/icon.dart b/lib/views/pages/ui/components/buttons/icon.dart new file mode 100644 index 00000000..0fa9f7dc --- /dev/null +++ b/lib/views/pages/ui/components/buttons/icon.dart @@ -0,0 +1,99 @@ +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + +import '../../../../../vaahextendflutter/helpers/constants.dart'; +import '../../../../../vaahextendflutter/helpers/enums.dart'; +import '../../../../../vaahextendflutter/helpers/styles.dart'; +import '../../../../../vaahextendflutter/widgets/atoms/buttons.dart'; +import '../code_preview.dart'; +import '../commons.dart'; + +class ButtonIconPreview extends StatelessWidget { + const ButtonIconPreview({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Icon Buttons', style: heading), + verticalMargin16, + Wrap( + spacing: defaultPadding / 2, + children: [ + ButtonIcon( + onPressed: () {}, + enableBorder: false, + iconData: FontAwesomeIcons.bell, + ), + ButtonIcon( + onPressed: () {}, + enableBorder: false, + iconData: FontAwesomeIcons.xmark, + ), + ButtonIcon( + onPressed: () {}, + buttonType: ButtonType.success, + iconData: FontAwesomeIcons.user, + ), + ButtonIcon( + onPressed: () {}, + color: Colors.pink, + iconData: FontAwesomeIcons.heart, + ), + ], + ), + ], + ); + } +} + +class ButtonIconCode extends StatelessWidget { + const ButtonIconCode({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Without Border', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonIcon(', + ' onPressed: () {},', + ' enableBorder: false,', + ' iconData: FontAwesomeIcons.xmark,', + '),', + ], + ), + verticalMargin8, + Text('With ButtonType', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonIcon(', + ' onPressed: () {},', + ' buttonType: ButtonType.success,', + ' iconData: FontAwesomeIcons.user,', + '),', + ], + ), + verticalMargin8, + Text('With Color', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonIcon(', + ' onPressed: () {},', + ' color: Colors.pink,', + ' iconData: FontAwesomeIcons.heart,', + '),', + ], + ), + ], + ); + } +} diff --git a/lib/views/pages/ui/components/buttons/icon_and_label.dart b/lib/views/pages/ui/components/buttons/icon_and_label.dart new file mode 100644 index 00000000..60665aed --- /dev/null +++ b/lib/views/pages/ui/components/buttons/icon_and_label.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + +import '../../../../../vaahextendflutter/helpers/constants.dart'; +import '../../../../../vaahextendflutter/helpers/styles.dart'; +import '../../../../../vaahextendflutter/widgets/atoms/buttons.dart'; +import '../code_preview.dart'; +import '../commons.dart'; + +class ButtonIconLabelPreview extends StatelessWidget { + const ButtonIconLabelPreview({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Label and Icon Buttons', style: heading), + verticalMargin16, + Wrap( + spacing: defaultPadding / 2, + children: [ + ButtonElevatedWithIcon( + onPressed: () {}, + text: "Icon Button", + iconData: FontAwesomeIcons.user, + ), + ButtonOutlinedWithIcon( + onPressed: () {}, + text: "Icon Button", + iconData: FontAwesomeIcons.user, + ), + ButtonTextWithIcon( + onPressed: () {}, + text: "Icon Button", + iconData: FontAwesomeIcons.user, + ), + ], + ), + ], + ); + } +} + +class ButtonIconLabelCode extends StatelessWidget { + const ButtonIconLabelCode({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Elevated Button With Icon', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonElevatedWithIcon(', + ' onPressed: () {},', + ' text: "Icon Button",', + ' iconData: FontAwesomeIcons.user,', + '),', + ], + ), + verticalMargin8, + Text('Outlined Button With Icon', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonOutlinedWithIcon(', + ' onPressed: () {},', + ' text: "Icon Button",', + ' iconData: FontAwesomeIcons.user,', + '),', + ], + ), + verticalMargin8, + Text('Text Button With Icon', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonTextWithIcon(', + ' onPressed: () {},', + ' text: "Icon Button",', + ' iconData: FontAwesomeIcons.user,', + '),', + ], + ), + ], + ); + } +} diff --git a/lib/views/pages/ui/components/buttons/outlined.dart b/lib/views/pages/ui/components/buttons/outlined.dart new file mode 100644 index 00000000..80592523 --- /dev/null +++ b/lib/views/pages/ui/components/buttons/outlined.dart @@ -0,0 +1,161 @@ +import 'package:flutter/material.dart'; + +import '../../../../../vaahextendflutter/helpers/constants.dart'; +import '../../../../../vaahextendflutter/helpers/enums.dart'; +import '../../../../../vaahextendflutter/helpers/styles.dart'; +import '../../../../../vaahextendflutter/widgets/atoms/buttons.dart'; +import '../code_preview.dart'; +import '../commons.dart'; + +class ButtonOutlinedPreview extends StatelessWidget { + const ButtonOutlinedPreview({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Outlined Buttons', style: heading), + verticalMargin16, + Wrap( + spacing: defaultPadding / 2, + children: [ + ButtonOutlined( + onPressed: () {}, + text: "Info", + buttonType: ButtonType.info, + ), + ButtonOutlined( + onPressed: () {}, + text: "Success", + buttonType: ButtonType.success, + ), + ButtonOutlined( + onPressed: () {}, + text: "Warning", + buttonType: ButtonType.warning, + ), + ButtonOutlined( + onPressed: () {}, + text: "Danger", + buttonType: ButtonType.danger, + ), + ButtonOutlined( + onPressed: () {}, + text: "Help", + buttonType: ButtonType.help, + ), + ButtonOutlined( + onPressed: () {}, + text: "Primary", + buttonType: ButtonType.primary, + ), + ButtonOutlined( + onPressed: () {}, + text: "Secondary", + buttonType: ButtonType.secondary, + ), + ], + ), + ], + ); + } +} + +class ButtonOutlinedCode extends StatelessWidget { + const ButtonOutlinedCode({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Info', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonOutlined(",', + ' onPressed: () {},', + ' text: "Info",', + ' buttonType: ButtonType.info,', + '),"' + ], + ), + verticalMargin8, + Text('Success', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonOutlined(",', + ' onPressed: () {},', + ' text: "Success",', + ' buttonType: ButtonType.success,', + '),"' + ], + ), + verticalMargin8, + Text('Warning', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonOutlined(",', + ' onPressed: () {},', + ' text: "Warning",', + ' buttonType: ButtonType.warning,', + '),"' + ], + ), + verticalMargin8, + Text('Danger', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonOutlined(",', + ' onPressed: () {},', + ' text: "Danger",', + ' buttonType: ButtonType.danger,', + '),"' + ], + ), + verticalMargin8, + Text('Help', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonOutlined(",', + ' onPressed: () {},', + ' text: "Help",', + ' buttonType: ButtonType.help,', + '),"' + ], + ), + verticalMargin8, + Text('Primary', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonOutlined(",', + ' onPressed: () {},', + ' text: "Primary",', + ' buttonType: ButtonType.primary,', + '),"' + ], + ), + verticalMargin8, + Text('Secondary', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonOutlined(",', + ' onPressed: () {},', + ' text: "Secondary",', + ' buttonType: ButtonType.secondary,', + '),"' + ], + ), + ], + ); + } +} diff --git a/lib/views/pages/ui/components/buttons/text.dart b/lib/views/pages/ui/components/buttons/text.dart new file mode 100644 index 00000000..8143d99d --- /dev/null +++ b/lib/views/pages/ui/components/buttons/text.dart @@ -0,0 +1,161 @@ +import 'package:flutter/material.dart'; + +import '../../../../../vaahextendflutter/helpers/constants.dart'; +import '../../../../../vaahextendflutter/helpers/enums.dart'; +import '../../../../../vaahextendflutter/helpers/styles.dart'; +import '../../../../../vaahextendflutter/widgets/atoms/buttons.dart'; +import '../code_preview.dart'; +import '../commons.dart'; + +class ButtonTextPreview extends StatelessWidget { + const ButtonTextPreview({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Text Buttons', style: heading), + verticalMargin16, + Wrap( + spacing: defaultPadding / 2, + children: [ + ButtonText( + onPressed: () {}, + text: "Info", + buttonType: ButtonType.info, + ), + ButtonText( + onPressed: () {}, + text: "Success", + buttonType: ButtonType.success, + ), + ButtonText( + onPressed: () {}, + text: "Warning", + buttonType: ButtonType.warning, + ), + ButtonText( + onPressed: () {}, + text: "Danger", + buttonType: ButtonType.danger, + ), + ButtonText( + onPressed: () {}, + text: "Help", + buttonType: ButtonType.help, + ), + ButtonText( + onPressed: () {}, + text: "Primary", + buttonType: ButtonType.primary, + ), + ButtonText( + onPressed: () {}, + text: "Secondary", + buttonType: ButtonType.secondary, + ), + ], + ), + ], + ); + } +} + +class ButtonTextCode extends StatelessWidget { + const ButtonTextCode({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Info', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonText(",', + ' onPressed: () {},', + ' text: "Info",', + ' buttonType: ButtonType.info,', + '),"' + ], + ), + verticalMargin8, + Text('Success', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonText(",', + ' onPressed: () {},', + ' text: "Success",', + ' buttonType: ButtonType.success,', + '),"' + ], + ), + verticalMargin8, + Text('Warning', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonText(",', + ' onPressed: () {},', + ' text: "Warning",', + ' buttonType: ButtonType.warning,', + '),"' + ], + ), + verticalMargin8, + Text('Danger', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonText(",', + ' onPressed: () {},', + ' text: "Danger",', + ' buttonType: ButtonType.danger,', + '),"' + ], + ), + verticalMargin8, + Text('Help', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonText(",', + ' onPressed: () {},', + ' text: "Help",', + ' buttonType: ButtonType.help,', + '),"' + ], + ), + verticalMargin8, + Text('Primary', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonText(",', + ' onPressed: () {},', + ' text: "Primary",', + ' buttonType: ButtonType.primary,', + '),"' + ], + ), + verticalMargin8, + Text('Secondary', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + 'ButtonText(",', + ' onPressed: () {},', + ' text: "Secondary",', + ' buttonType: ButtonType.secondary,', + '),"' + ], + ), + ], + ); + } +} diff --git a/lib/views/pages/ui/index.dart b/lib/views/pages/ui/index.dart index b6d227b6..ed6dcf36 100644 --- a/lib/views/pages/ui/index.dart +++ b/lib/views/pages/ui/index.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; +import './components/buttons/buttons.dart'; import './components/inputs/inputs.dart'; import './components/themecolors.dart'; import '../../../vaahextendflutter/app_theme.dart'; @@ -43,6 +44,8 @@ class _UIPageState extends BaseStateful { verticalMargin24, AppInputs(), verticalMargin24, + Buttons(), + verticalMargin24, ], ), ), From 2ff91bbe45a1f926cce8e69d4df59903f8d4e4ea Mon Sep 17 00:00:00 2001 From: Prajapati Chintan Date: Thu, 16 Feb 2023 14:45:03 +0530 Subject: [PATCH 3/4] Added: Radio Button and Check Boxes --- .../widgets/atoms/button_checkbox.dart | 101 ++++++++++++++++++ .../widgets/atoms/button_radio.dart | 87 +++++++++++++++ .../pages/ui/components/buttons/buttons.dart | 8 ++ .../components/buttons/radioandcheckbox.dart | 97 +++++++++++++++++ 4 files changed, 293 insertions(+) create mode 100644 lib/vaahextendflutter/widgets/atoms/button_checkbox.dart create mode 100644 lib/vaahextendflutter/widgets/atoms/button_radio.dart create mode 100644 lib/views/pages/ui/components/buttons/radioandcheckbox.dart diff --git a/lib/vaahextendflutter/widgets/atoms/button_checkbox.dart b/lib/vaahextendflutter/widgets/atoms/button_checkbox.dart new file mode 100644 index 00000000..4d518bd8 --- /dev/null +++ b/lib/vaahextendflutter/widgets/atoms/button_checkbox.dart @@ -0,0 +1,101 @@ +import 'package:flutter/material.dart'; + +import '../../helpers/constants.dart'; + +class CheckboxItem { + final String text; + final bool initialValue; + final T data; + + const CheckboxItem({ + required this.text, + this.initialValue = false, + required this.data, + }); +} + +class ButtonCheckBox extends StatefulWidget { + final List> items; + final void Function(List) onChanged; + final EdgeInsets? padding; + + const ButtonCheckBox({ + Key? key, + required this.items, + required this.onChanged, + this.padding, + }) : super(key: key); + + @override + State> createState() => _ButtonCheckBoxState(); +} + +class _ButtonCheckBoxState extends State> { + final List _values = []; + final List _selectedValues = []; + + @override + void initState() { + assert(widget.items.isNotEmpty); + for (final item in widget.items) { + _values.add(item.initialValue); + if (item.initialValue) { + _selectedValues.add(item.data); + } + } + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Wrap( + children: [ + for (int index = 0; index < widget.items.length; index++) + Padding( + padding: widget.padding ?? rightPadding8 + bottomPadding8, + child: InkWell( + onTap: () { + setState(() { + _values[index] = !_values[index]; + }); + updateSelectedValues(index); + widget.onChanged(_selectedValues); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Checkbox( + value: _values[index], + onChanged: (value) { + if (value == null) return; + setState(() { + _values[index] = value; + }); + updateSelectedValues(index); + widget.onChanged(_selectedValues); + }, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + visualDensity: const VisualDensity( + horizontal: VisualDensity.minimumDensity, + vertical: VisualDensity.minimumDensity, + ), + ), + Flexible( + child: Text(widget.items[index].text), + ), + ], + ), + ), + ), + ], + ); + } + + void updateSelectedValues(int index) { + if (_values[index]) { + _selectedValues.add(widget.items[index].data); + } else { + _selectedValues.remove(widget.items[index].data); + } + } +} diff --git a/lib/vaahextendflutter/widgets/atoms/button_radio.dart b/lib/vaahextendflutter/widgets/atoms/button_radio.dart new file mode 100644 index 00000000..3620fdab --- /dev/null +++ b/lib/vaahextendflutter/widgets/atoms/button_radio.dart @@ -0,0 +1,87 @@ +import 'package:flutter/material.dart'; + +import '../../helpers/constants.dart'; + +class RadioItem { + final String text; + final T data; + + const RadioItem({ + required this.text, + required this.data, + }); +} + +class ButtonRadio extends StatefulWidget { + final T? initialValue; + final List> items; + final void Function(T) onChanged; + final EdgeInsets? padding; + + const ButtonRadio({ + Key? key, + this.initialValue, + required this.items, + required this.onChanged, + this.padding, + }) : super(key: key); + + @override + State> createState() => _ButtonRadioState(); +} + +class _ButtonRadioState extends State> { + late T _selectedValue; + + @override + void initState() { + assert(widget.items.isNotEmpty); + _selectedValue = widget.initialValue ?? widget.items.first.data; + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Wrap( + children: [ + for (final item in widget.items) + Padding( + padding: widget.padding ?? rightPadding8 + bottomPadding8, + child: InkWell( + onTap: () { + setState(() { + _selectedValue = item.data; + }); + widget.onChanged(_selectedValue); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Radio( + value: item.data, + groupValue: _selectedValue, + onChanged: (T? value) { + if (value != null) { + setState(() { + _selectedValue = value; + }); + widget.onChanged(_selectedValue); + } + }, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + visualDensity: const VisualDensity( + horizontal: VisualDensity.minimumDensity, + vertical: VisualDensity.minimumDensity, + ), + ), + Flexible( + child: Text(item.text), + ), + ], + ), + ), + ), + ], + ); + } +} diff --git a/lib/views/pages/ui/components/buttons/buttons.dart b/lib/views/pages/ui/components/buttons/buttons.dart index 7e07a819..1302bde9 100644 --- a/lib/views/pages/ui/components/buttons/buttons.dart +++ b/lib/views/pages/ui/components/buttons/buttons.dart @@ -5,6 +5,7 @@ import './extra.dart'; import './icon.dart'; import './icon_and_label.dart'; import './outlined.dart'; +import './radioandcheckbox.dart'; import './text.dart'; import '../../../../../vaahextendflutter/app_theme.dart'; import '../../../../../vaahextendflutter/helpers/constants.dart'; @@ -45,6 +46,8 @@ class Buttons extends StatelessWidget { ButtonIconLabelPreview(), Divider(height: defaultPadding * 2), ButtonExtrasPreview(), + Divider(height: defaultPadding * 2), + ButtonRadioAndCheckboxPreview(), ], ), ), @@ -81,6 +84,11 @@ class Buttons extends StatelessWidget { title: 'Button Properties', child: ButtonExtrasCode(), ), + verticalMargin8, + ExpansionPanelWrap( + title: 'Radio and Checkbox Buttons', + child: ButtonRadioAndCheckboxCode(), + ), ], ), ), diff --git a/lib/views/pages/ui/components/buttons/radioandcheckbox.dart b/lib/views/pages/ui/components/buttons/radioandcheckbox.dart new file mode 100644 index 00000000..f96a77d1 --- /dev/null +++ b/lib/views/pages/ui/components/buttons/radioandcheckbox.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; + +import '../../../../../vaahextendflutter/helpers/constants.dart'; +import '../../../../../vaahextendflutter/helpers/styles.dart'; +import '../../../../../vaahextendflutter/widgets/atoms/button_checkbox.dart'; +import '../../../../../vaahextendflutter/widgets/atoms/button_radio.dart'; +import '../code_preview.dart'; +import '../commons.dart'; + +class ButtonRadioAndCheckboxPreview extends StatelessWidget { + const ButtonRadioAndCheckboxPreview({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Radio And Checkbox Buttons', style: heading), + verticalMargin16, + Text('Radio Buttons', style: normal), + verticalMargin12, + ButtonRadio( + initialValue: items.last, + items: items.map((e) => RadioItem(text: e.language, data: e)).toList(), + onChanged: (_) {}, + ), + verticalMargin12, + Text('Checkbox Buttons', style: normal), + verticalMargin12, + ButtonCheckBox( + items: items.map((e) => CheckboxItem(text: e.language, data: e)).toList(), + onChanged: (_) {}, + ), + ], + ); + } +} + +class ButtonRadioAndCheckboxCode extends StatelessWidget { + const ButtonRadioAndCheckboxCode({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('x', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: ['x'], + ), + verticalMargin8, + ], + ); + } +} + +enum Difficulty { + easy, + moderate, + hard, +} + +class Language { + final String language; + final Difficulty difficulty; + + const Language({ + required this.language, + required this.difficulty, + }); +} + +const List items = [ + Language( + language: 'c / c++', + difficulty: Difficulty.easy, + ), + Language( + language: 'python', + difficulty: Difficulty.easy, + ), + Language( + language: 'dart', + difficulty: Difficulty.easy, + ), + Language( + language: 'other trash', + difficulty: Difficulty.easy, + ), + Language( + language: 'NA', + difficulty: Difficulty.easy, + ), +]; From b67e5b1df954683f3f54f66cc17223cce7ade99d Mon Sep 17 00:00:00 2001 From: Prajapati Chintan Date: Sat, 11 Mar 2023 12:35:16 +0530 Subject: [PATCH 4/4] Added: Rating bar --- .../widgets/atoms/rating_bar.dart | 302 ++++++++++++++++++ .../pages/ui/components/buttons/buttons.dart | 8 + .../components/buttons/radioandcheckbox.dart | 20 +- .../ui/components/buttons/rating_bar.dart | 55 ++++ 4 files changed, 383 insertions(+), 2 deletions(-) create mode 100644 lib/vaahextendflutter/widgets/atoms/rating_bar.dart create mode 100644 lib/views/pages/ui/components/buttons/rating_bar.dart diff --git a/lib/vaahextendflutter/widgets/atoms/rating_bar.dart b/lib/vaahextendflutter/widgets/atoms/rating_bar.dart new file mode 100644 index 00000000..49a95449 --- /dev/null +++ b/lib/vaahextendflutter/widgets/atoms/rating_bar.dart @@ -0,0 +1,302 @@ +import 'package:flutter/material.dart'; + +enum UpdateRatingMode { + tap, + drag, + tapAndDrag, +} + +class RatingBar extends StatefulWidget { + const RatingBar({ + Key? key, + this.itemBuilder, + this.onRatingUpdate, + this.allowHalfRating = false, + this.unratedColor, + this.ratedColor, + this.size = 28.0, + this.isDisabled = false, + this.padding = EdgeInsets.zero, + this.direction = Axis.horizontal, + this.textDirection, + this.itemCount = 5, + this.initialRating = 0, + this.minRatingAllowed = 0, + this.maxRatingAllowed, + this.updateRatingMode = UpdateRatingMode.tapAndDrag, + }) : super(key: key); + + final IndexedWidgetBuilder? itemBuilder; + final ValueChanged? onRatingUpdate; + final bool allowHalfRating; + final Color? unratedColor; + final Color? ratedColor; + final double size; + final bool isDisabled; + final EdgeInsetsGeometry padding; + final Axis direction; + final TextDirection? textDirection; + final int itemCount; + final double initialRating; + final double minRatingAllowed; + final double? maxRatingAllowed; + final UpdateRatingMode updateRatingMode; + + @override + State createState() => _RatingBarState(); +} + +class _RatingBarState extends State { + late IndexedWidgetBuilder itemBuilder; + + double _rating = 0; + bool _isRTL = false; + double iconRating = 0; + + late double _minRating, _maxRating; + + @override + void initState() { + super.initState(); + _minRating = widget.minRatingAllowed; + _maxRating = widget.maxRatingAllowed ?? widget.itemCount.toDouble(); + _rating = widget.initialRating; + itemBuilder = widget.itemBuilder ?? + (context, _) => Icon( + Icons.star, + color: widget.ratedColor ?? const Color(0xFFFFC130), + ); + } + + @override + void didUpdateWidget(RatingBar oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.initialRating != widget.initialRating) { + _rating = widget.initialRating; + } + _minRating = widget.minRatingAllowed; + _maxRating = widget.maxRatingAllowed ?? widget.itemCount.toDouble(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final textDirection = widget.textDirection ?? Directionality.of(context); + _isRTL = textDirection == TextDirection.rtl; + iconRating = 0.0; + + return Wrap( + textDirection: textDirection, + direction: widget.direction, + children: List.generate( + widget.itemCount, + (index) => _buildRating(itemBuilder, index), + ), + ); + } + + Widget _buildRating(IndexedWidgetBuilder itemBuilder, int index) { + final item = itemBuilder.call(context, index); + final ratingOffset = widget.allowHalfRating ? 0.5 : 1.0; + + late Widget updatedRatingWidget; + + if (index >= _rating) { + updatedRatingWidget = _NoRatingWidget( + size: widget.size, + unratedColor: widget.unratedColor ?? const Color(0xFFFFE182), + child: item, + ); + } else if (index >= _rating - ratingOffset && widget.allowHalfRating) { + updatedRatingWidget = _HalfRatingWidget( + size: widget.size, + rtlMode: _isRTL, + unratedColor: widget.unratedColor ?? const Color(0xFFFFE182), + child: item, + ); + iconRating += 0.5; + } else { + updatedRatingWidget = SizedBox( + width: widget.size, + height: widget.size, + child: FittedBox( + child: item, + ), + ); + iconRating += 1.0; + } + + return IgnorePointer( + ignoring: widget.isDisabled, + child: GestureDetector( + onTapDown: (details) { + if (widget.updateRatingMode == UpdateRatingMode.drag) return; + double value; + if (index == 0 && (_rating == 1 || _rating == 0.5)) { + value = 0; + } else { + final double tappedPosition = details.localPosition.dx; + bool tappedOnFirstHalf = tappedPosition <= widget.size / 2; + if (widget.textDirection != null && widget.textDirection == TextDirection.rtl) { + tappedOnFirstHalf = !tappedOnFirstHalf; + } + value = index + (tappedOnFirstHalf && widget.allowHalfRating ? 0.5 : 1.0); + } + value = value + .clamp( + widget.minRatingAllowed, + widget.maxRatingAllowed ?? widget.itemCount, + ) + .toDouble(); + setState(() { + _rating = value; + }); + if (widget.onRatingUpdate != null) widget.onRatingUpdate!(value); + }, + onHorizontalDragEnd: _isHorizontal ? _onDragEnd : null, + onHorizontalDragUpdate: _isHorizontal ? _onDragUpdate : null, + onVerticalDragEnd: _isHorizontal ? null : _onDragEnd, + onVerticalDragUpdate: _isHorizontal ? null : _onDragUpdate, + child: Padding( + padding: widget.padding, + child: updatedRatingWidget, + ), + ), + ); + } + + bool get _isHorizontal => widget.direction == Axis.horizontal; + + void _onDragUpdate(DragUpdateDetails dragDetails) { + if (widget.updateRatingMode != UpdateRatingMode.tap) { + final box = context.findRenderObject() as RenderBox?; + if (box == null) return; + + final pos = box.globalToLocal(dragDetails.globalPosition); + double i; + if (widget.direction == Axis.horizontal) { + i = pos.dx / (widget.size + widget.padding.horizontal); + } else { + i = pos.dy / (widget.size + widget.padding.vertical); + } + var currentRating = widget.allowHalfRating ? i : i.round().toDouble(); + if (currentRating > widget.itemCount) { + currentRating = widget.itemCount.toDouble(); + } + if (currentRating < 0) { + currentRating = 0.0; + } + if (_isRTL && widget.direction == Axis.horizontal) { + currentRating = widget.itemCount - currentRating; + } + + _rating = currentRating.clamp(_minRating, _maxRating); + if (widget.onRatingUpdate != null) widget.onRatingUpdate!(iconRating); + setState(() {}); + } + } + + void _onDragEnd(DragEndDetails details) { + if (widget.onRatingUpdate != null) widget.onRatingUpdate!(iconRating); + iconRating = 0.0; + } +} + +class _HalfRatingWidget extends StatelessWidget { + const _HalfRatingWidget({ + required this.size, + required this.child, + required this.rtlMode, + required this.unratedColor, + }); + + final Widget child; + final double size; + final bool rtlMode; + final Color unratedColor; + + @override + Widget build(BuildContext context) { + return SizedBox( + height: size, + width: size, + child: Stack( + fit: StackFit.expand, + children: [ + FittedBox( + child: _NoRatingWidget( + size: size, + unratedColor: unratedColor, + child: child, + ), + ), + FittedBox( + child: ClipRect( + clipper: _HalfClipper( + rtlMode: rtlMode, + ), + child: child, + ), + ), + ], + ), + ); + } +} + +class _HalfClipper extends CustomClipper { + _HalfClipper({required this.rtlMode}); + + final bool rtlMode; + + @override + Rect getClip(Size size) => rtlMode + ? Rect.fromLTRB( + size.width / 2, + 0, + size.width, + size.height, + ) + : Rect.fromLTRB( + 0, + 0, + size.width / 2, + size.height, + ); + + @override + bool shouldReclip(CustomClipper oldClipper) => true; +} + +class _NoRatingWidget extends StatelessWidget { + const _NoRatingWidget({ + required this.size, + required this.child, + required this.unratedColor, + }); + + final double size; + final Widget child; + final Color unratedColor; + + @override + Widget build(BuildContext context) { + return SizedBox( + height: size, + width: size, + child: FittedBox( + child: ColorFiltered( + colorFilter: ColorFilter.mode( + unratedColor, + BlendMode.srcIn, + ), + child: child, + ), + ), + ); + } +} diff --git a/lib/views/pages/ui/components/buttons/buttons.dart b/lib/views/pages/ui/components/buttons/buttons.dart index 1302bde9..ba41c301 100644 --- a/lib/views/pages/ui/components/buttons/buttons.dart +++ b/lib/views/pages/ui/components/buttons/buttons.dart @@ -6,6 +6,7 @@ import './icon.dart'; import './icon_and_label.dart'; import './outlined.dart'; import './radioandcheckbox.dart'; +import './rating_bar.dart'; import './text.dart'; import '../../../../../vaahextendflutter/app_theme.dart'; import '../../../../../vaahextendflutter/helpers/constants.dart'; @@ -48,6 +49,8 @@ class Buttons extends StatelessWidget { ButtonExtrasPreview(), Divider(height: defaultPadding * 2), ButtonRadioAndCheckboxPreview(), + Divider(height: defaultPadding * 2), + RatingBarPreview(), ], ), ), @@ -89,6 +92,11 @@ class Buttons extends StatelessWidget { title: 'Radio and Checkbox Buttons', child: ButtonRadioAndCheckboxCode(), ), + verticalMargin8, + ExpansionPanelWrap( + title: 'Rating Bar', + child: RatingBarCode(), + ), ], ), ), diff --git a/lib/views/pages/ui/components/buttons/radioandcheckbox.dart b/lib/views/pages/ui/components/buttons/radioandcheckbox.dart index f96a77d1..942d7d1f 100644 --- a/lib/views/pages/ui/components/buttons/radioandcheckbox.dart +++ b/lib/views/pages/ui/components/buttons/radioandcheckbox.dart @@ -46,12 +46,28 @@ class ButtonRadioAndCheckboxCode extends StatelessWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('x', style: TextStyles.regular2), + Text('Radio Buttons', style: TextStyles.regular2), verticalMargin4, const CodePreview( - code: ['x'], + code: [ + "ButtonRadio(", + " initialValue: items.last,", + " items: items.map((e) => RadioItem(text: e.language, data: e)).toList(),", + " onChanged: (_) {},", + "),", + ], ), verticalMargin8, + Text('Checkbox Buttons', style: TextStyles.regular2), + verticalMargin4, + const CodePreview( + code: [ + "ButtonCheckBox(", + " items: items.map((e) => CheckboxItem(text: e.language, data: e)).toList(),", + " onChanged: (_) {},", + "),", + ], + ), ], ); } diff --git a/lib/views/pages/ui/components/buttons/rating_bar.dart b/lib/views/pages/ui/components/buttons/rating_bar.dart new file mode 100644 index 00000000..18c14310 --- /dev/null +++ b/lib/views/pages/ui/components/buttons/rating_bar.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; + +import '../../../../../vaahextendflutter/helpers/constants.dart'; +import '../../../../../vaahextendflutter/services/logging_library/logging_library.dart'; +import '../../../../../vaahextendflutter/widgets/atoms/rating_bar.dart'; +import '../code_preview.dart'; +import '../commons.dart'; + +class RatingBarPreview extends StatelessWidget { + const RatingBarPreview({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Rating Bar', style: heading), + verticalMargin16, + RatingBar( + unratedColor: Colors.amber.withOpacity(0.3), + ratedColor: Colors.amber, + onRatingUpdate: (_) { + Log.info(_.toString(), disableCloudLogging: true); + }, + ), + ], + ); + } +} + +class RatingBarCode extends StatelessWidget { + const RatingBarCode({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + CodePreview( + code: [ + "RatingBar(", + " unratedColor: Colors.amber.withOpacity(0.3),", + " ratedColor: Colors.amber,", + " onRatingUpdate: (_) {", + " Log.info(_.toString(), disableCloudLogging: true);", + " },", + "),", + ], + ), + ], + ); + } +}