From ced9c294f598a9f205a809b7a7a231e829158cd0 Mon Sep 17 00:00:00 2001 From: "Michael W. Aziz" Date: Sat, 18 May 2024 15:24:48 +0300 Subject: [PATCH] [SYNC] Working on the Buttons --- README.md | 5 +- lib/astromic_mobile_elements.dart | 2 +- lib/src/Buttons/buttons.astromic.dart | 118 ++++++++++++ lib/src/Buttons/src/base.dart | 154 +++++++++++++++ lib/src/Buttons/src/icon_button.dart | 156 +++++++++++++++ lib/src/Buttons/src/link_button.dart | 119 ++++++++++++ lib/src/Buttons/src/sizing_strategy.enum.dart | 5 + lib/src/Buttons/src/state_button.dart | 180 ++++++++++++++++++ lib/src/Buttons/src/style.dart | 61 ++++++ 9 files changed, 798 insertions(+), 2 deletions(-) create mode 100644 lib/src/Buttons/buttons.astromic.dart create mode 100644 lib/src/Buttons/src/base.dart create mode 100644 lib/src/Buttons/src/icon_button.dart create mode 100644 lib/src/Buttons/src/link_button.dart create mode 100644 lib/src/Buttons/src/sizing_strategy.enum.dart create mode 100644 lib/src/Buttons/src/state_button.dart create mode 100644 lib/src/Buttons/src/style.dart diff --git a/README.md b/README.md index b042863..9fc5cfd 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ Developed, Maintained, and is property of Michael W. Aziz (Micazi) - Selectors - Radio ☑️ - Chip ☑️ +- Toggles + - Checkbox ☑️ + - Switcher ☑️ + - Custom ☑️ - Buttons - Fields -- Toggles diff --git a/lib/astromic_mobile_elements.dart b/lib/astromic_mobile_elements.dart index 5c55712..36162cc 100644 --- a/lib/astromic_mobile_elements.dart +++ b/lib/astromic_mobile_elements.dart @@ -5,8 +5,8 @@ export './src/Spacing/spacing.astromic.dart'; export './src/Widgets/widgets.astromic.dart'; export './src/Selectors/selectors.astromic.dart'; export './src/Toggles/toggles.astromic.dart'; +export './src/Buttons/buttons.astromic.dart'; -// export './src/2.Buttons/buttons.astromic.dart'; // export './src/5.Fields/fields.astromic.dart'; // // // export './src/styles/styles.astromic.dart'; diff --git a/lib/src/Buttons/buttons.astromic.dart b/lib/src/Buttons/buttons.astromic.dart new file mode 100644 index 0000000..93462e2 --- /dev/null +++ b/lib/src/Buttons/buttons.astromic.dart @@ -0,0 +1,118 @@ +//s2 Core Packages Imports +import 'package:flutter/material.dart'; +// +import 'src/icon_button.dart'; +import 'src/link_button.dart'; +import 'src/state_button.dart'; +// +import 'src/sizing_strategy.enum.dart'; +import 'src/style.dart'; +// +export 'src/sizing_strategy.enum.dart'; +export 'src/style.dart'; + +class AstromicButtons { + //S1 -- State + static Widget state({ + // + void Function(VoidCallback start, VoidCallback stop)? onTap, + Function(VoidCallback start, VoidCallback stop)? onHold, + // + bool? isEnabled, + bool? withFeedback, + bool? withHighlightChange, + // + SizingStrategy? widthSizingStrategy, + SizingStrategy? heightSizingStrategy, + InteractiveInkFeatureFactory? splashFactory, + required AstromicButtonStyle Function(bool isEnabled, bool isHighlighted, bool isLoading) style, + // + Widget? loadingContent, + required Widget Function(bool isEnabled, bool isHighlighted) content, + }) => + AstromicStateButton( + onTap: onTap, + onHold: onHold, + // + isEnabled: isEnabled, + withFeedback: withFeedback, + withHighlightChange: withHighlightChange, + // + widthSizingStrategy: widthSizingStrategy, + heightSizingStrategy: heightSizingStrategy, + splashFactory: splashFactory, + style: style, + // + loadingContent: loadingContent, + content: content, + ); + + //S1 -- Icon + static Widget icon({ + // + void Function(VoidCallback start, VoidCallback stop)? onTap, + Function(VoidCallback start, VoidCallback stop)? onHold, + // + bool? isEnabled, + bool? withFeedback, + bool? withHighlightChange, + // + bool? isCircular, + InteractiveInkFeatureFactory? splashFactory, + required AstromicButtonStyle Function(bool isEnabled, bool isHighlighted, bool isLoading) style, + // + Widget? loadingContent, + required Widget Function(bool isEnabled, bool isHighlighted) icon, + }) => + AstromicIconButton( + onTap: onTap, + onHold: onHold, + // + isEnabled: isEnabled, + withFeedback: withFeedback, + withHighlightChange: withHighlightChange, + // + isCircular: isCircular, + splashFactory: splashFactory, + style: style, + // + loadingContent: loadingContent, + icon: icon, + ); + + //S1 -- Link + static Widget link({ + // + VoidCallback? onTap, + VoidCallback? onHold, + // + bool? isEnabled, + TextDirection? textDirection, + + // + bool? isUnderlined, + EdgeInsetsGeometry? contentPadding, + required TextStyle Function(bool isEnabled)? style, + // + String? text, + Widget? textWidget, + Widget? prefix, + Widget? suffix, + }) => + AstromicLinkButton( + onTap: onTap, + onHold: onHold, + // + isEnabled: isEnabled, + textDirection: textDirection, + // + isUnderlined: isUnderlined, + contentPadding: contentPadding, + style: style, + // + text: text, + textWidget: textWidget, + prefix: prefix, + suffix: suffix, + ); +} diff --git a/lib/src/Buttons/src/base.dart b/lib/src/Buttons/src/base.dart new file mode 100644 index 0000000..5b99677 --- /dev/null +++ b/lib/src/Buttons/src/base.dart @@ -0,0 +1,154 @@ +//SECTION - Imports +// +//s1 PACKAGES +//--------------- +//s2 CORE +import 'package:flutter/material.dart'; +//s2 3RD-PARTY +// +//s1 DEPENDENCIES +//---------------- +//s2 SERVICES +//s2 MODELS +import 'style.dart'; +//s2 MISC +//!SECTION - Imports +// +//SECTION - Exports +//!SECTION - Exports +// + +class AstromicButtonBase extends StatefulWidget { + //SECTION - Widget Arguments + //s1 -- Functionality + final VoidCallback? onTap; + final VoidCallback? onHold; + //s1 -- Configurations + final bool isEnabled; + final bool withFeedback; + final bool withHighlightChange; + final bool isFullWidth; + //s1 -- Style + final InteractiveInkFeatureFactory? splashFactory; + final AstromicButtonStyle Function(bool isEnabled, bool isHighlighted) style; + //s1 -- Content + final Widget Function(bool isEnabled, bool isHighlighted) child; + //!SECTION + // + const AstromicButtonBase({ + Key? key, + // + this.onTap, + this.onHold, + // + this.isEnabled = true, + this.withFeedback = true, + this.withHighlightChange = true, + this.isFullWidth = false, + //s1 -- Style + this.splashFactory, + required this.style, + //s1 -- Content + required this.child, + }) : super( + key: key, + ); + + @override + State createState() => _AstromicButtonBaseState(); +} + +class _AstromicButtonBaseState extends State { + // + //SECTION - State Variables + //s1 --Controllers + //s1 --Controllers + // + //s1 --State + late bool _isHighlighted; + //s1 --State + // + //s1 --Constants + //s1 --Constants + //!SECTION + + @override + void initState() { + super.initState(); + // + //SECTION - State Variables initializations & Listeners + //s1 --Controllers & Listeners + //s1 --Controllers & Listeners + // + //s1 --State + _isHighlighted = false; + //s1 --State + // + //s1 --Late & Async Initializers + //s1 --Late & Async Initializers + //!SECTION + } + + @override + Widget build(BuildContext context) { + //SECTION - Build Setup + //s1 -Values + //s1 -Values + // + //s1 -Widgets + //s1 -Widgets + //!SECTION + + //SECTION - Build Return + return Material( + color: widget.style(widget.isEnabled, _isHighlighted).backgroundGradient != null ? Colors.transparent : widget.style(widget.isEnabled, _isHighlighted).backgroundColor, + shape: OutlineInputBorder( + borderSide: BorderSide( + width: widget.style(widget.isEnabled, _isHighlighted).borderWidth ?? 1, + color: widget.style(widget.isEnabled, _isHighlighted).borderColor ?? Colors.black, + ), + borderRadius: widget.style(widget.isEnabled, _isHighlighted).borderRadius?.resolve(null) ?? BorderRadius.circular(0), + ), + child: Ink( + decoration: BoxDecoration( + borderRadius: widget.style(widget.isEnabled, _isHighlighted).borderRadius?.resolve(null) ?? BorderRadius.circular(0), + gradient: widget.style(widget.isEnabled, _isHighlighted).backgroundGradient, + ), + child: InkWell( + splashFactory: widget.splashFactory, + splashColor: widget.withFeedback ? widget.style(widget.isEnabled, _isHighlighted).splashColor : Colors.transparent, + hoverColor: widget.withFeedback ? widget.style(widget.isEnabled, _isHighlighted).hoverColor : Colors.transparent, + highlightColor: widget.withFeedback ? widget.style(widget.isEnabled, _isHighlighted).highlightColor : Colors.transparent, + // + borderRadius: widget.style(widget.isEnabled, _isHighlighted).borderRadius?.resolve(null) ?? BorderRadius.circular(0), + // + onTap: widget.isEnabled && widget.onTap != null ? widget.onTap : null, + onLongPress: widget.isEnabled && widget.onHold != null ? widget.onHold : null, + onHighlightChanged: widget.withHighlightChange + ? (v) { + setState(() { + _isHighlighted = v; + }); + } + : null, + // + child: Padding( + padding: widget.style(widget.isEnabled, _isHighlighted).contentPadding ?? EdgeInsets.zero, + child: widget.child( + widget.isEnabled, + _isHighlighted, + ), + ), + ), + ), + ); + //!SECTION + } + + @override + void dispose() { + //SECTION - Disposable variables + //!SECTION + super.dispose(); + } +} diff --git a/lib/src/Buttons/src/icon_button.dart b/lib/src/Buttons/src/icon_button.dart new file mode 100644 index 0000000..8b083f8 --- /dev/null +++ b/lib/src/Buttons/src/icon_button.dart @@ -0,0 +1,156 @@ +//SECTION - Imports +// +//s1 PACKAGES +//--------------- +//s2 CORE +import 'package:flutter/material.dart'; +import 'base.dart'; +import 'style.dart'; +//s2 3RD-PARTY +// +//s1 DEPENDENCIES +//--------------- +//s2 SERVICES +//s2 MODELS +//s2 MISC +//!SECTION - Imports +// +//SECTION - Exports +//!SECTION - Exports +// + +class AstromicIconButton extends StatefulWidget { + //SECTION - Widget Arguments + //s1 -- Functionality + final void Function(VoidCallback start, VoidCallback stop)? onTap; + final Function(VoidCallback start, VoidCallback stop)? onHold; + //s1 -- Configurations + final bool? isEnabled; + final bool? withFeedback; + final bool? withHighlightChange; + //s1 -- Style + final InteractiveInkFeatureFactory? splashFactory; + final bool? isCircular; + final AstromicButtonStyle Function(bool isEnabled, bool isHighlighted, bool isLoading)? style; + //s1 -- Content + final Widget? loadingContent; + final Widget Function(bool isEnabled, bool isHighlighted) icon; + //!SECTION + // + const AstromicIconButton({ + super.key, + // + this.onTap, + this.onHold, + // + this.isEnabled = true, + this.withFeedback = true, + this.withHighlightChange = true, + //s1 -- Style + this.splashFactory, + this.isCircular = true, + this.style, + //s1 -- Content + this.loadingContent, + required this.icon, + }); + + @override + State createState() => _AstromicIconButtonState(); +} + +class _AstromicIconButtonState extends State { + // + //SECTION - State Variables + //s1 --Controllers + //s1 --Controllers + // + //s1 --State + late bool isLoading; + late bool isHighlighted; + //s1 --State + // + //s1 --Constants + //s1 --Constants + //!SECTION + + @override + void initState() { + super.initState(); + // + //SECTION - State Variables initializations & Listeners + //s1 --Controllers & Listeners + //s1 --Controllers & Listeners + // + //s1 --State + isLoading = false; + isHighlighted = false; + //s1 --State + // + //s1 --Late & Async Initializers + //s1 --Late & Async Initializers + //!SECTION + } + + @override + Widget build(BuildContext context) { + //SECTION - Build Setup + //s1 -Values + //s1 -Values + // + //s1 -Widgets + //----- + Widget baseChild = AstromicButtonBase( + isEnabled: widget.isEnabled!, + withFeedback: widget.withFeedback!, + withHighlightChange: widget.withHighlightChange!, + isFullWidth: false, + // + onTap: !isLoading && context.mounted && widget.onTap != null + ? () { + widget.onTap!(() { + setState(() { + isLoading = true; + }); + }, () { + setState(() { + isLoading = false; + }); + }); + } + : null, + onHold: !isLoading && context.mounted && widget.onHold != null + ? () { + widget.onHold!(() { + setState(() { + isLoading = true; + }); + }, () { + setState(() { + isLoading = false; + }); + }); + } + : null, + splashFactory: widget.splashFactory, + style: (enabled, highlighted) => widget.style!(enabled, highlighted, isLoading).copyWith( + borderRadius: widget.isCircular! ? BorderRadiusDirectional.circular(100000000000) : null, + ), + child: (enabled, highlighted) => isLoading && widget.loadingContent != null ? widget.loadingContent! : widget.icon(enabled, highlighted), + ); + + //s1 -Widgets + //!SECTION + + //SECTION - Build Return + return baseChild; + //!SECTION + } + + @override + void dispose() { + //SECTION - Disposable variables + //!SECTION + super.dispose(); + } +} diff --git a/lib/src/Buttons/src/link_button.dart b/lib/src/Buttons/src/link_button.dart new file mode 100644 index 0000000..4ce26b0 --- /dev/null +++ b/lib/src/Buttons/src/link_button.dart @@ -0,0 +1,119 @@ +//SECTION - Imports +// +//s1 PACKAGES +//--------------- +//s2 CORE +import 'package:flutter/widgets.dart'; +//s2 3RD-PARTY +// +//s1 DEPENDENCIES +//--------------- +//s2 SERVICES +//s2 MODELS +//s2 MISC +import 'base.dart'; +import 'style.dart'; + +//!SECTION - Imports +// +//SECTION - Exports +//!SECTION - Exports +// +class AstromicLinkButton extends StatelessWidget { + //SECTION - Widget Arguments + //s1 -- Functionality + final VoidCallback? onTap; + final VoidCallback? onHold; + //s1 -- Configurations + final bool? isEnabled; + final TextDirection? textDirection; + //s1 -- Style + final bool? isUnderlined; + final EdgeInsetsGeometry? contentPadding; + final TextStyle Function(bool isEnabled)? style; + // + //s1 -- Content + final String? text; + final Widget? textWidget; + final Widget? prefix; + final Widget? suffix; + //!SECTION + // + const AstromicLinkButton({ + Key? key, + //s1 -- Functionality + this.onTap, + this.onHold, + //s1 -- Configurations + this.isEnabled = true, + this.textDirection, + //s1 -- Style + this.isUnderlined = true, + this.contentPadding, + this.style, + //s1 -- Content + this.text, + this.textWidget, + this.prefix, + this.suffix, + }) : assert(text != null || textWidget != null, 'You need to provide either a text or text widget.'), + super( + key: key, + ); + + @override + Widget build(BuildContext context) { + //SECTION - Build Setup + //s1 -Values + //s1 -Values + // + //s1 -Widgets + //s1 -Widgets + //!SECTION + + //SECTION - Build Return + return Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + textDirection: textDirection, + children: [ + AstromicButtonBase( + onTap: onTap, + onHold: onHold, + // + isEnabled: isEnabled!, + withFeedback: false, + withHighlightChange: false, + isFullWidth: false, + // + style: (isEnabled, isHighlighted) => AstromicButtonStyle( + contentPadding: contentPadding, + ), + // + child: (isEnabled, isHighlighted) => Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (prefix != null) prefix!, + textWidget != null + ? textWidget! + : Text( + text!, + style: style != null + ? style!(isEnabled).copyWith( + decoration: isUnderlined! ? TextDecoration.underline : TextDecoration.none, + ) + : TextStyle( + decoration: isUnderlined! ? TextDecoration.underline : TextDecoration.none, + ), + ), + if (suffix != null) suffix!, + ], + ), + ) + ], + ); + //!SECTION + } +} diff --git a/lib/src/Buttons/src/sizing_strategy.enum.dart b/lib/src/Buttons/src/sizing_strategy.enum.dart new file mode 100644 index 0000000..71eede4 --- /dev/null +++ b/lib/src/Buttons/src/sizing_strategy.enum.dart @@ -0,0 +1,5 @@ +enum SizingStrategy { + hug, + fill, + fixed, +} diff --git a/lib/src/Buttons/src/state_button.dart b/lib/src/Buttons/src/state_button.dart new file mode 100644 index 0000000..b312568 --- /dev/null +++ b/lib/src/Buttons/src/state_button.dart @@ -0,0 +1,180 @@ +//SECTION - Imports +// +//s1 PACKAGES +//--------------- +//s2 CORE +import 'package:flutter/material.dart'; +import 'base.dart'; +//s2 3RD-PARTY +// +//s1 DEPENDENCIES +//--------------- +//s2 SERVICES +//s2 MODELS +import 'sizing_strategy.enum.dart'; +import 'style.dart'; +//s2 MISC +//!SECTION - Imports +// +//SECTION - Exports +//!SECTION - Exports +// + +class AstromicStateButton extends StatefulWidget { + //SECTION - Widget Arguments + //s1 -- Functionality + final void Function(VoidCallback start, VoidCallback stop)? onTap; + final Function(VoidCallback start, VoidCallback stop)? onHold; + //s1 -- Configurations + final bool? isEnabled; + final bool? withFeedback; + final bool? withHighlightChange; + //s1 -- Style + final SizingStrategy? widthSizingStrategy; + final SizingStrategy? heightSizingStrategy; + final InteractiveInkFeatureFactory? splashFactory; + final AstromicButtonStyle Function(bool isEnabled, bool isHighlighted, bool isLoading)? style; + //s1 -- Content + final Widget? loadingContent; + final Widget Function(bool isEnabled, bool isHighlighted) content; + //!SECTION + // + const AstromicStateButton({ + super.key, + // + this.onTap, + this.onHold, + // + this.isEnabled = true, + this.withFeedback = true, + this.withHighlightChange = true, + //s1 -- Style + this.widthSizingStrategy = SizingStrategy.hug, + this.heightSizingStrategy = SizingStrategy.hug, + this.splashFactory, + this.style, + //s1 -- Content + this.loadingContent, + required this.content, + }); + + @override + State createState() => _AstromicStateButtonState(); +} + +class _AstromicStateButtonState extends State { + // + //SECTION - State Variables + //s1 --Controllers + //s1 --Controllers + // + //s1 --State + late bool isLoading; + late bool isHighlighted; + //s1 --State + // + //s1 --Constants + //s1 --Constants + //!SECTION + + @override + void initState() { + super.initState(); + // + //SECTION - State Variables initializations & Listeners + //s1 --Controllers & Listeners + //s1 --Controllers & Listeners + // + //s1 --State + isLoading = false; + isHighlighted = false; + //s1 --State + // + //s1 --Late & Async Initializers + //s1 --Late & Async Initializers + //!SECTION + } + + @override + Widget build(BuildContext context) { + //SECTION - Build Setup + //s1 -Values + //s1 -Values + // + //s1 -Widgets + //----- + Widget baseChild = AstromicButtonBase( + isEnabled: widget.isEnabled!, + withFeedback: widget.withFeedback!, + withHighlightChange: widget.withHighlightChange!, + isFullWidth: false, + // + onTap: !isLoading && context.mounted && widget.onTap != null + ? () { + widget.onTap!(() { + setState(() { + isLoading = true; + }); + }, () { + setState(() { + isLoading = false; + }); + }); + } + : null, + onHold: !isLoading && context.mounted && widget.onHold != null + ? () { + widget.onHold!(() { + setState(() { + isLoading = true; + }); + }, () { + setState(() { + isLoading = false; + }); + }); + } + : null, + splashFactory: widget.splashFactory, + style: (enabled, highlighted) => widget.style!(enabled, highlighted, isLoading).copyWith( + contentPadding: widget.heightSizingStrategy == SizingStrategy.fixed && widget.widthSizingStrategy == SizingStrategy.fixed + ? EdgeInsets.zero + : widget.heightSizingStrategy == SizingStrategy.fixed && widget.widthSizingStrategy != SizingStrategy.fixed + ? EdgeInsets.symmetric( + horizontal: widget.style!(enabled, highlighted, isLoading).contentPadding?.horizontal ?? 0, + vertical: 0, + ) + : widget.widthSizingStrategy == SizingStrategy.fixed && widget.heightSizingStrategy != SizingStrategy.fixed + ? EdgeInsets.symmetric( + vertical: widget.style!(enabled, highlighted, isLoading).contentPadding?.vertical ?? 0, + horizontal: 0, + ) + : null, + ), + child: (enabled, highlighted) => isLoading && widget.loadingContent != null ? widget.loadingContent! : widget.content(enabled, highlighted), + ); + + //s1 -Widgets + //!SECTION + + //SECTION - Build Return + return SizedBox( + width: widget.widthSizingStrategy == SizingStrategy.fixed && widget.style != null ? widget.style!(widget.isEnabled!, isHighlighted, isLoading).fixedWidth : null, + height: widget.heightSizingStrategy == SizingStrategy.fixed && widget.style != null ? widget.style!(widget.isEnabled!, isHighlighted, isLoading).fixedHeight : null, + child: Row( + mainAxisSize: widget.widthSizingStrategy == SizingStrategy.hug ? MainAxisSize.min : MainAxisSize.max, + children: [ + baseChild, + ], + ), + ); + //!SECTION + } + + @override + void dispose() { + //SECTION - Disposable variables + //!SECTION + super.dispose(); + } +} diff --git a/lib/src/Buttons/src/style.dart b/lib/src/Buttons/src/style.dart new file mode 100644 index 0000000..f0ba198 --- /dev/null +++ b/lib/src/Buttons/src/style.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; + +class AstromicButtonStyle { + //s1 -- Colors + final Color? backgroundColor; + final Gradient? backgroundGradient; + final Color? hoverColor; + final Color? splashColor; + final Color? highlightColor; + final Color? borderColor; + // + //s1 -- Spacing & Insets + final double? fixedHeight; + final double? fixedWidth; + final EdgeInsetsGeometry? contentPadding; + final BorderRadiusGeometry? borderRadius; + final double? borderWidth; + const AstromicButtonStyle({ + this.backgroundColor = Colors.transparent, + this.backgroundGradient, + this.hoverColor, + this.splashColor, + this.highlightColor, + this.borderColor, + // + this.fixedHeight, + this.fixedWidth, + this.contentPadding, + this.borderRadius, + this.borderWidth, + // + }); + + AstromicButtonStyle copyWith({ + Color? backgroundColor, + Gradient? backgroundGradient, + Color? hoverColor, + Color? splashColor, + Color? highlightColor, + Color? borderColor, + double? fixedHeight, + double? fixedWidth, + EdgeInsetsGeometry? contentPadding, + BorderRadiusGeometry? borderRadius, + double? borderWidth, + }) { + return AstromicButtonStyle( + backgroundGradient: backgroundGradient ?? this.backgroundGradient, + backgroundColor: backgroundColor ?? this.backgroundColor, + hoverColor: hoverColor ?? this.hoverColor, + splashColor: splashColor ?? this.splashColor, + highlightColor: highlightColor ?? this.highlightColor, + borderColor: borderColor ?? this.borderColor, + fixedHeight: fixedHeight == -1 ? null : fixedHeight ?? this.fixedHeight, + fixedWidth: fixedWidth == -1 ? null : fixedWidth ?? this.fixedWidth, + contentPadding: contentPadding ?? this.contentPadding, + borderRadius: borderRadius ?? this.borderRadius, + borderWidth: borderWidth ?? this.borderWidth, + ); + } +}