This commit is contained in:
2025-02-11 15:26:44 +02:00
parent 4a0ff120f7
commit 40f17efd30
28 changed files with 580 additions and 781 deletions

View File

@@ -1,9 +1,9 @@
library astromic_elements;
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/Fields/fields.astromic.dart';
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/fields/fields.astromic.dart';
export 'package:flutter_svg/flutter_svg.dart' show SvgPicture, BytesLoader, SvgAssetLoader; //Needed for assets manipulation

View File

@@ -10,7 +10,6 @@ import 'package:flutter/material.dart';
import 'src/icon_button.dart';
import 'src/link_button.dart';
import 'src/state_button.dart';
//
import 'src/enums/enums.exports.dart';
import 'src/models/models.exports.dart';
//s1 Exports
@@ -21,74 +20,56 @@ export 'src/models/models.exports.dart';
class AstromicButtons {
/// an `AstromicStateButton` is a button whoose style is affected by it's state.
static Widget state({
//
void Function(VoidCallback start, VoidCallback stop)? onTap,
Function(VoidCallback start, VoidCallback stop)? onHold,
//
AstromicButtonConfiguration? configuration,
//
AstromicSizingStrategy? widthSizingStrategy,
AstromicSizingStrategy? 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,
//
configuration: configuration,
//
widthSizingStrategy: widthSizingStrategy,
heightSizingStrategy: heightSizingStrategy,
style: style,
//
loadingContent: loadingContent,
content: content,
);
/// an `AstromicIconButton` is a button specifically for mapping to an IconButton.
static Widget icon({
//
void Function(VoidCallback start, VoidCallback stop)? onTap,
Function(VoidCallback start, VoidCallback stop)? onHold,
//
AstromicButtonConfiguration? configuration,
//
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,
//
configuration: configuration,
//
isCircular: isCircular ?? true,
style: style,
//
loadingContent: loadingContent,
icon: icon,
);
/// an `AstromicLinkButton` is a button specifically for mapping to LinkButton.
static Widget link({
//
VoidCallback? onTap,
VoidCallback? onHold,
//
AstromicButtonConfiguration? configuration,
//
bool Function(bool isEnabled)? isUnderlined,
EdgeInsetsGeometry? contentPadding,
TextStyle Function(bool isEnabled)? style,
//
String? text,
Widget? textWidget,
Widget? prefix,
@@ -97,13 +78,10 @@ class AstromicButtons {
AstromicLinkButton(
onTap: onTap,
onHold: onHold,
//
configuration: configuration,
//
isUnderlined: isUnderlined ?? (e) => e,
contentPadding: contentPadding,
style: style,
//
text: text,
textWidget: textWidget,
prefix: prefix,

View File

@@ -2,7 +2,7 @@
//s2 Core Package Imports
import 'package:flutter/material.dart';
//s2 1st-party Package Imports
import '../../../Dependencies/gradient_border/gradient_borders.dart';
import '../../../dependencies/gradient_border/gradient_borders.dart';
//s2 3rd-party Package Imports
//s2 Dependancies Imports
//s3 Routes

View File

@@ -1 +1 @@
export './sizing_strategy.enum.dart';
export 'sizing_strategy.enum.dart';

View File

@@ -1,9 +1,16 @@
//s1 Imports
//s2 Core Package Imports
import 'package:flutter/widgets.dart';
//
//s2 1st-party Package Imports
//s2 3rd-party Package Imports
//s2 Dependancies Imports
//s3 Routes
//s3 Services
//s3 Models
import 'src/text_field.dart';
import 'src/action_field.dart';
import 'src/models/models.exports.dart';
//
//s1 Exports
export 'src/models/models.exports.dart';
export 'src/enums/enums.exports.dart';
@@ -12,20 +19,14 @@ class AstromicFields {
static Widget text({
required TextEditingController controller,
Key? stateKey,
//
void Function(String v)? onChanged,
void Function(String v)? onSubmited,
//
AstromicFieldConfiguration? configuration,
//
AstromicFieldStyle Function(bool isEnabled, bool isFocused)? style,
//
String? hint,
//
Widget? Function(bool isEnabled, bool isFocused, VoidCallback stateSetter)? prefixWidget,
Widget? Function(bool isEnabled, bool isFocused, VoidCallback stateSetter)? suffixWidget,
Widget? Function(bool isEnabled, bool isFocused)? messageBuilder,
//
Iterable<ContextMenuButtonItem>? contextButtons,
}) =>
Column(
@@ -51,19 +52,14 @@ class AstromicFields {
{Key? stateKey,
(T item, String label)? initialValue,
required TextEditingController controller,
//
Future<(T item, String label)?> Function((T item, String label)? currentValue)? onTap,
Future<(T item, String label)?> Function((T item, String label)? currentValue)? onHold,
/// Map the old controller value and the new selected value to how it will look in the field. e.g Adding a new choosen value as a list in the field.
required String Function(String? oldValue, String newValue) onValueChangedMapper,
//
AstromicFieldConfiguration? configuration,
//
AstromicFieldStyle Function(bool isEnabled)? style,
//
String? hint,
//
Widget? Function(bool isEnabled, VoidCallback stateSetter)? prefixWidget,
Widget? Function(bool isEnabled, VoidCallback stateSetter)? suffixWidget,
Widget? Function(bool isEnabled)? messageBuilder}) =>

View File

@@ -10,8 +10,8 @@ import 'package:astromic_extensions/astromic_extensions.dart';
//s3 Routes
//s3 Services
//s3 Models
import '../../../Dependencies/gradient_border/gradient_borders.dart';
import '../../../Dependencies/gradient_border/input_borders/gradient_underline_input_border.dart';
import '../../../dependencies/gradient_border/gradient_borders.dart';
import '../../../dependencies/gradient_border/input_borders/gradient_underline_input_border.dart';
import 'enums/enums.exports.dart';
import 'models/models.exports.dart';
//s1 Exports

View File

@@ -1 +1 @@
export './border_type.enum.dart';
export 'border_type.enum.dart';

View File

@@ -1,2 +1,2 @@
export './style.model.dart';
export './configuration.model.dart';
export 'style.model.dart';
export 'configuration.model.dart';

View File

@@ -1,79 +1,58 @@
//s2 Core Packages Imports
//s1 Imports
//s2 Core Package Imports
import 'package:flutter/widgets.dart';
import 'src/Radio/radio.selector.dart';
import 'src/Chip/chip.selector.dart';
import 'src/Radio/models/configuration.model.dart';
import 'src/Chip/models/configuration.model.dart';
//
export 'src/Radio/models/configuration.model.dart';
export 'src/Chip/models/configuration.model.dart';
//s2 1st-party Package Imports
//s2 3rd-party Package Imports
//s2 Dependancies Imports
//s3 Routes
//s3 Services
//s3 Models
import 'src/radio/radio.selector.dart';
import 'src/chip/chip.selector.dart';
import 'src/radio/models/models.exports.dart';
import 'src/chip/models/models.exports.dart';
//s1 Exports
export 'src/radio/models/models.exports.dart';
export 'src/chip/models/models.exports.dart';
class AstromicSelectors {
//S1 -- Radio
/// A selector that allows only one item to be selected from a group of items.
static Widget radio<T>({
T? initialSelectedValue,
Function(T selectedItem)? onChanged,
//
AstromicRadioSelectorConfiguration? configurations,
//
double? itemSpacing = 4,
//
required Widget Function(T item, bool isEnabled, bool isSelected, VoidCallback? onTap) itemBuilder,
required List<(T item, bool isEnabled)> items,
required Widget Function(T item, bool isEnabled, bool isSelected, VoidCallback? onTap) itemBuilder,
}) =>
AstromicRadioSelector<T>(
initialSelectedValue: initialSelectedValue,
onChanged: onChanged,
//
configuration: configurations ?? const AstromicRadioSelectorConfiguration(),
//
itemSpacing: itemSpacing ?? 8.0,
//
configuration: configurations,
itemSpacing: itemSpacing,
itemBuilder: itemBuilder,
items: items,
);
//S1 -- Chip
/// A selector that allows multible items to be selected from a group of items with the option to unselect and clear.
static Widget chip<T>({
List<T>? initialSelectedValues,
void Function(List<T> selectedItems)? onChanged,
//
AstromicChipSelectorConfiguration? configuration,
bool isCustom = false,
//
double? itemSpacing = 4,
double? runSpacing = 8,
//
required List<(T item, bool isEnabled)> items,
required Widget Function(T item, bool isEnabled, bool isSelected, VoidCallback? onTap, VoidCallback? onClearTapped) itemBuilder,
Widget Function(List<Widget> items)? groupBuilder,
}) {
assert(
(!isCustom || groupBuilder != null),
"You have to provide the group builder in a custom constructor.",
);
return isCustom
? AstromicChipSelector.custom(
initialSelectedValues: initialSelectedValues,
onChanged: onChanged,
//
configuration: configuration,
//
items: items,
itemBuilder: itemBuilder,
groupBuilder: groupBuilder!,
)
: AstromicChipSelector(
initialSelectedValues: initialSelectedValues,
onChanged: onChanged,
//
configuration: configuration,
//
itemSpacing: itemSpacing,
runSpacing: runSpacing,
//
items: items,
itemBuilder: itemBuilder,
);
}
}) =>
AstromicChipSelector(
initialSelectedValues: initialSelectedValues,
onChanged: onChanged,
configuration: configuration,
itemSpacing: itemSpacing,
runSpacing: runSpacing,
items: items,
itemBuilder: itemBuilder,
groupBuilder: groupBuilder!,
);
}

View File

@@ -1,31 +1,23 @@
//SECTION - Imports
//
//s1 PACKAGES
//---------------
//s2 CORE
import 'package:astromic_extensions/astromic_extensions.dart';
//s1 Imports
//s2 Core Package Imports
import 'package:flutter/material.dart';
//s2 3RD-PARTY
//
//s1 DEPENDENCIES
//---------------
//s2 SERVICES
//s2 MODELS
import 'models/configuration.model.dart';
//s2 1st-party Package Imports
import 'package:astromic_extensions/astromic_extensions.dart';
//s2 3rd-party Package Imports
//s2 Dependancies Imports
//s3 Routes
//s3 Services
//s3 Models
import 'models/models.exports.dart';
//s1 Exports
//s2 MISC
//!SECTION - Imports
//
//SECTION - Exports
//!SECTION - Exports
//
/// Selector that allows multible items to be selected from a group of items with the option to unselect and clear.
class AstromicChipSelector<T> extends StatefulWidget {
//SECTION - Widget Arguments
//s1 -- Functionality
final List<T>? initialSelectedValues;
final Function(List<T> selectedItems)? onChanged;
//s1 -- Configuration
final bool isCustom;
final AstromicChipSelectorConfiguration? configuration;
//s1 -- Style
final double? itemSpacing;
@@ -33,7 +25,6 @@ class AstromicChipSelector<T> extends StatefulWidget {
//
//s1 -- Content
final List<(T item, bool isEnabled)> items;
//
final Widget Function(T item, bool isEnabled, bool isSelected, VoidCallback? onTap, VoidCallback? onClearTapped) itemBuilder;
final Widget Function(List<Widget> items)? groupBuilder;
//!SECTION
@@ -45,7 +36,6 @@ class AstromicChipSelector<T> extends StatefulWidget {
this.onChanged,
//s1 -- Configuration
this.configuration,
this.isCustom = false,
//s1 -- Style
this.itemSpacing = 8,
this.runSpacing = 8,
@@ -57,46 +47,10 @@ class AstromicChipSelector<T> extends StatefulWidget {
(configuration?.isNullable ?? true) || (initialSelectedValues != null && items.map((i) => i.$1).toList().containsAll(initialSelectedValues)),
"Initial values are not all present in the items!",
),
assert(
(!isCustom || groupBuilder != null),
"You have to provide the group builder in a custom constructor.",
),
super(
key: key,
);
static AstromicChipSelector custom<T>({
Key? key,
//s1 -- Functionality
final List<T>? initialSelectedValues,
final Function(List<T> selectedItems)? onChanged,
//s1 -- Configuration
final AstromicChipSelectorConfiguration? configuration,
//s1 -- Content
required final List<(T item, bool isEnabled)> items,
//
required final Widget Function(T item, bool isEnabled, bool isSelected, VoidCallback? onTap, VoidCallback? onClearTapped) itemBuilder,
required final Widget Function(List<Widget> items) groupBuilder,
}) {
assert((configuration?.isNullable ?? true) || (initialSelectedValues != null && items.map((i) => i.$1).toList().containsAll(initialSelectedValues)),
"Initial values are not all present in the items!");
//
return AstromicChipSelector<T>(
key: key,
isCustom: true,
//
initialSelectedValues: initialSelectedValues,
onChanged: onChanged,
//
configuration: configuration,
//
items: items,
itemBuilder: itemBuilder,
groupBuilder: groupBuilder,
);
}
@override
State<AstromicChipSelector<T>> createState() => _AstromicChipSelectorState<T>();
}
@@ -141,13 +95,11 @@ class _AstromicChipSelectorState<T> extends State<AstromicChipSelector<T>> {
_onTap(T value) {
setState(() {
if (selectedItems.contains(value)) {
//---- if item selected
// Item is selected
selectedItems.remove(value);
//---- !if item selected
} else {
//---- if item Not selected
// Item is NOT selected
selectedItems.add(value);
//---- !if item Not selected
}
});
if (widget.onChanged != null) {
@@ -155,7 +107,6 @@ class _AstromicChipSelectorState<T> extends State<AstromicChipSelector<T>> {
}
}
// ----
_onTapClear(T value) {
setState(() {
if (selectedItems.contains(value)) {
@@ -216,7 +167,7 @@ class _AstromicChipSelectorState<T> extends State<AstromicChipSelector<T>> {
//s1 -Widgets
//!SECTION
//SECTION - Build Return
return widget.isCustom && widget.groupBuilder != null
return widget.groupBuilder != null
? widget.groupBuilder!(baseChildren)
: (widget.configuration?.isWrap ?? true)
? Wrap(

View File

@@ -1,5 +1,7 @@
import 'package:flutter/widgets.dart';
/// Configuration model for the chip selector element.
class AstromicChipSelectorConfiguration {
final bool isNullable;
final bool isWrap;

View File

@@ -0,0 +1 @@
export 'configuration.model.dart';

View File

@@ -1,34 +1,35 @@
import 'package:flutter/widgets.dart';
/// Configuration model for the radio selector element.
class AstromicRadioSelectorConfiguration {
final Axis axis;
final bool isNullable;
final bool withExpandedSpace;
//
final MainAxisAlignment? mainAxisAlignment;
final MainAxisSize? mainAxisSize;
final CrossAxisAlignment? crossAxisAlignment;
const AstromicRadioSelectorConfiguration({
this.axis = Axis.horizontal,
this.isNullable = true,
this.withExpandedSpace = false,
//
this.mainAxisAlignment,
this.mainAxisSize,
this.crossAxisAlignment,
});
AstromicRadioSelectorConfiguration copyWith({
Axis? axis,
bool? isNullable,
bool? withExpandedSpace,
//
MainAxisAlignment? mainAxisAlignment,
MainAxisSize? mainAxisSize,
CrossAxisAlignment? crossAxisAlignment,
}) {
return AstromicRadioSelectorConfiguration(
axis: axis ?? this.axis,
isNullable: isNullable ?? this.isNullable,
withExpandedSpace: withExpandedSpace ?? this.withExpandedSpace,
mainAxisAlignment: mainAxisAlignment ?? this.mainAxisAlignment,
mainAxisSize: mainAxisSize ?? this.mainAxisSize,
crossAxisAlignment: crossAxisAlignment ?? this.crossAxisAlignment,
);
}

View File

@@ -0,0 +1 @@
export 'configuration.model.dart';

View File

@@ -1,37 +1,27 @@
//SECTION - Imports
//
//s1 PACKAGES
//---------------
//s2 CORE
//s1 Imports
//s2 Core Package Imports
import 'package:flutter/widgets.dart';
//s2 3RD-PARTY
//
//s1 DEPENDENCIES
//---------------
//s2 SERVICES
//s2 MODELS
import '../../../Spacing/spacing.astromic.dart';
import 'models/configuration.model.dart';
//s2 MISC
//!SECTION - Imports
//
//SECTION - Exports
//!SECTION - Exports
//
//s2 1st-party Package Imports
//s2 3rd-party Package Imports
//s2 Dependancies Imports
//s3 Routes
//s3 Services
//s3 Models
import 'models/models.exports.dart';
//s1 Exports
//
/// Selector that allows only one item to be selected from a group of items.
class AstromicRadioSelector<T> extends StatefulWidget {
//SECTION - Widget Arguments
//s1 -- Functionality
final T? initialSelectedValue;
final Function(T selectedItem)? onChanged;
//s1 -- Configuration
final AstromicRadioSelectorConfiguration configuration;
final AstromicRadioSelectorConfiguration? configuration;
//s1 -- Style
final double itemSpacing;
final double? itemSpacing;
//s1 -- Content
final List<(T item, bool isEnabled)> items;
//
final Widget Function(T item, bool isEnabled, bool isSelected, VoidCallback? onTap) itemBuilder;
//!SECTION
//
@@ -42,14 +32,14 @@ class AstromicRadioSelector<T> extends StatefulWidget {
required this.items,
this.onChanged,
//s1 -- Configuration
required this.configuration,
this.configuration = const AstromicRadioSelectorConfiguration(),
//s1 -- Style
required this.itemSpacing,
this.itemSpacing = 8.0,
//s1 -- Content
required this.itemBuilder,
}) : assert(configuration.isNullable || initialSelectedValue != null, 'You need to supply an initial value if not nullable!'),
}) : assert(configuration!.isNullable || initialSelectedValue != null, 'You need to supply an initial value if not nullable!'),
assert(
configuration.isNullable ||
configuration!.isNullable ||
(items
.map(
(e) => e.$1,
@@ -123,8 +113,6 @@ class _AstromicRadioSelectorState<T> extends State<AstromicRadioSelector<T>> {
Widget build(BuildContext context) {
//SECTION - Build Setup
//s1 -Values
//double w = MediaQuery.of(context).size.width;
//double h = MediaQuery.of(context).size.height;
//s1 -Values
//
//s1 -Widgets
@@ -133,27 +121,26 @@ class _AstromicRadioSelectorState<T> extends State<AstromicRadioSelector<T>> {
bool isEnabled = currentItem.$2;
bool isSelected = item == selectedItem;
//
return widget.configuration.withExpandedSpace && widget.configuration.axis == Axis.horizontal
? Expanded(child: widget.itemBuilder(item, isEnabled, isSelected, isEnabled ? () => _onTap(item) : null))
: widget.itemBuilder(item, isEnabled, isSelected, isEnabled ? () => _onTap(item) : null);
return widget.itemBuilder(item, isEnabled, isSelected, isEnabled ? () => _onTap(item) : null);
}).toList();
//s1 -Widgets
//!SECTION
//SECTION - Build Return
return widget.configuration.axis == Axis.horizontal
? separatedRow(
baseChildren,
AstromicSpacing.hsb(widget.itemSpacing),
mainAxisAlignment: widget.configuration.mainAxisAlignment ?? MainAxisAlignment.start,
crossAxisAlignment: widget.configuration.crossAxisAlignment ?? CrossAxisAlignment.center,
return widget.configuration!.axis == Axis.horizontal
? Row(
children: baseChildren,
spacing: widget.itemSpacing!,
mainAxisAlignment: widget.configuration!.mainAxisAlignment ?? MainAxisAlignment.start,
mainAxisSize: widget.configuration!.mainAxisSize ?? MainAxisSize.min,
crossAxisAlignment: widget.configuration!.crossAxisAlignment ?? CrossAxisAlignment.center,
)
: separatedColumn(
baseChildren,
AstromicSpacing.vsb(widget.itemSpacing),
widget.configuration.withExpandedSpace,
mainAxisAlignment: widget.configuration.mainAxisAlignment ?? MainAxisAlignment.center,
crossAxisAlignment: widget.configuration.crossAxisAlignment ?? CrossAxisAlignment.center,
: Column(
children: baseChildren,
spacing: widget.itemSpacing!,
mainAxisAlignment: widget.configuration!.mainAxisAlignment ?? MainAxisAlignment.center,
mainAxisSize: widget.configuration!.mainAxisSize ?? MainAxisSize.min,
crossAxisAlignment: widget.configuration!.crossAxisAlignment ?? CrossAxisAlignment.center,
);
//!SECTION
}
@@ -165,48 +152,3 @@ class _AstromicRadioSelectorState<T> extends State<AstromicRadioSelector<T>> {
super.dispose();
}
}
Widget separatedRow(
List<Widget> children,
Widget separator, {
MainAxisAlignment? mainAxisAlignment,
CrossAxisAlignment? crossAxisAlignment,
}) {
List<Widget> finalChildren = [];
for (var e in children) {
if (children.indexOf(e) != children.length - 1) {
finalChildren.addAll([e, separator]);
} else {
finalChildren.add(e);
}
}
return Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: crossAxisAlignment!,
mainAxisAlignment: mainAxisAlignment!,
children: finalChildren,
);
}
Widget separatedColumn(
List<Widget> children,
Widget separator,
bool withExpandedSpace, {
MainAxisAlignment? mainAxisAlignment,
CrossAxisAlignment? crossAxisAlignment,
}) {
List<Widget> finalChildren = [];
for (var e in children) {
if (children.indexOf(e) != children.length - 1) {
finalChildren.addAll([e, separator]);
} else {
finalChildren.add(e);
}
}
return Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: withExpandedSpace ? CrossAxisAlignment.stretch : crossAxisAlignment!,
mainAxisAlignment: mainAxisAlignment!,
children: finalChildren,
);
}

View File

@@ -51,3 +51,8 @@ class AstromicSpacing {
);
}
}
extension SizedBoxExtension on num {
Widget hsb() => AstromicSpacing.hsb(toDouble());
Widget vsb() => AstromicSpacing.vsb(toDouble());
}

View File

@@ -1,24 +1,16 @@
//SECTION - Imports
//
//s1 PACKAGES
//---------------
//s2 CORE
//s1 Imports
//s2 Core Package Imports
import 'package:flutter/material.dart';
//s2 1st-party Package Imports
//s2 3rd-party Package Imports
//s2 Dependancies Imports
//s3 Routes
//s3 Services
//s3 Models
import 'models/models.exports.dart';
import '../models/models.exports.dart';
//s1 Exports
import 'style.dart';
//s2 3RD-PARTY
//
//s1 DEPENDENCIES
//---------------
//s2 SERVICES
//s2 MODELS
//s2 MISC
//!SECTION - Imports
//
//SECTION - Exports
//!SECTION - Exports
//
class AstromicCheckboxToggle extends StatefulWidget {
//SECTION - Widget Arguments
//s1 -- Functionality
@@ -27,12 +19,9 @@ class AstromicCheckboxToggle extends StatefulWidget {
final bool? initialState;
final void Function(bool)? onStateChanged;
//s1 -- Configuration
final bool? isEnabled;
final bool? withLabel;
final bool? isLabelTapable;
final TextDirection? textDirection;
final AstromicToggleConfiguration? configuration;
//s1 -- Style
final AstromicCheckboxToggleStyle Function(bool isEnabled, bool isSelected) style;
final AstromicCheckboxToggleStyle Function(bool isEnabled, bool isSelected)? style;
//
//s1 -- Content
final Widget Function(bool isEnabled, bool isSelected)? innerWidget;
@@ -46,12 +35,9 @@ class AstromicCheckboxToggle extends StatefulWidget {
this.initialState,
this.onStateChanged,
//
this.isEnabled = true,
this.withLabel = true,
this.isLabelTapable = true,
this.textDirection = TextDirection.ltr,
this.configuration,
//
required this.style,
this.style,
//
this.innerWidget,
this.label,
@@ -68,7 +54,9 @@ class _AstromicCheckboxToggleState extends State<AstromicCheckboxToggle> {
//s1 --Controllers
//
//s1 --State
late bool isChecked;
late bool _isChecked;
late AstromicCheckboxToggleStyle _style;
late AstromicToggleConfiguration _config;
//s1 --State
//
//s1 --Constants
@@ -84,7 +72,7 @@ class _AstromicCheckboxToggleState extends State<AstromicCheckboxToggle> {
//s1 --Controllers & Listeners
//
//s1 --State
isChecked = widget.initialState ?? widget.stateVariable ?? false;
_isChecked = widget.initialState ?? widget.stateVariable ?? false;
//s1 --State
//
//s1 --Late & Async Initializers
@@ -105,7 +93,7 @@ class _AstromicCheckboxToggleState extends State<AstromicCheckboxToggle> {
@override
void didUpdateWidget(AstromicCheckboxToggle oldWidget) {
if (widget.stateVariable != null && widget.stateVariable != oldWidget.stateVariable) {
isChecked = widget.stateVariable!;
_isChecked = widget.stateVariable!;
}
super.didUpdateWidget(oldWidget);
}
@@ -116,8 +104,8 @@ class _AstromicCheckboxToggleState extends State<AstromicCheckboxToggle> {
//SECTION - Action Callbacks
_onTap() {
setState(() {
isChecked = !isChecked;
if (widget.onStateChanged != null) widget.onStateChanged!(isChecked);
_isChecked = !_isChecked;
if (widget.onStateChanged != null) widget.onStateChanged!(_isChecked);
});
}
//!SECTION
@@ -126,6 +114,8 @@ class _AstromicCheckboxToggleState extends State<AstromicCheckboxToggle> {
Widget build(BuildContext context) {
//SECTION - Build Setup
//s1 -Values
_config = widget.configuration ?? AstromicToggleConfiguration();
_style = widget.style != null ? widget.style!(_config.isEnabled, _isChecked) : AstromicCheckboxToggleStyle();
//double w = MediaQuery.of(context).size.width;
//double h = MediaQuery.of(context).size.height;
//s1 -Values
@@ -136,31 +126,31 @@ class _AstromicCheckboxToggleState extends State<AstromicCheckboxToggle> {
//SECTION - Build Return
return Directionality(
textDirection: widget.textDirection ?? TextDirection.ltr,
textDirection: _config.textDirection,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
//S1 -- Item
//Item
SizedBox(
width: widget.style(widget.isEnabled!, isChecked).itemSize,
height: widget.style(widget.isEnabled!, isChecked).itemSize,
width: _style.itemSize,
height: _style.itemSize,
child: Material(
child: InkWell(
onTap: widget.isEnabled! ? () => _onTap() : null,
onTap: _config.isEnabled ? () => _onTap() : null,
child: Container(
decoration: BoxDecoration(
color: widget.style(widget.isEnabled!, isChecked).backgroundColor,
border: widget.style(widget.isEnabled!, isChecked).border,
borderRadius: widget.style(widget.isEnabled!, isChecked).borderRadius,
color: _style.backgroundColor,
border: _style.border,
borderRadius: _style.borderRadius,
),
child: widget.innerWidget != null ? widget.innerWidget!(widget.isEnabled!, isChecked) : Container(),
child: widget.innerWidget != null ? widget.innerWidget!(_config.isEnabled, _isChecked) : Container(),
),
),
)),
//S1 -- Label Spacing
if (widget.withLabel!) SizedBox(width: widget.style(widget.isEnabled!, isChecked).labelSpacing),
//S1 -- Label
if (widget.withLabel! && widget.label != null) GestureDetector(onTap: widget.isLabelTapable! ? _onTap() : null, child: widget.label!(widget.isEnabled!, isChecked)),
//Label Spacing
if (_config.withLabel) SizedBox(width: _style.labelSpacing),
//Label
if (_config.withLabel && widget.label != null) GestureDetector(onTap: _config.isLabelTapable ? _onTap() : null, child: widget.label!(_config.isEnabled, _isChecked)),
],
),
);

View File

@@ -0,0 +1 @@
export './style.model.dart';

View File

@@ -1,6 +1,6 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'package:flutter/material.dart';
/// Styling model for the CheckBox Toggle element.
class AstromicCheckboxToggleStyle {
final double? itemSize;
final double? labelSpacing;
@@ -8,15 +8,13 @@ class AstromicCheckboxToggleStyle {
final Border? border;
final BorderRadius? borderRadius;
final Color? backgroundColor;
final TextStyle? labelStyle;
AstromicCheckboxToggleStyle({
this.itemSize = 24.0,
this.labelSpacing = 8.0,
this.border,
this.borderRadius,
this.backgroundColor,
this.labelStyle,
this.border = const Border.fromBorderSide(BorderSide(width: 2, color: Colors.black)),
this.borderRadius = const BorderRadius.all(Radius.circular(4)),
this.backgroundColor = Colors.transparent,
});
AstromicCheckboxToggleStyle copyWith({
@@ -25,7 +23,6 @@ class AstromicCheckboxToggleStyle {
Border? border,
BorderRadius? borderRadius,
Color? backgroundColor,
TextStyle? labelStyle,
}) {
return AstromicCheckboxToggleStyle(
itemSize: itemSize ?? this.itemSize,
@@ -33,7 +30,6 @@ class AstromicCheckboxToggleStyle {
border: border ?? this.border,
borderRadius: borderRadius ?? this.borderRadius,
backgroundColor: backgroundColor ?? this.backgroundColor,
labelStyle: labelStyle ?? this.labelStyle,
);
}
}

View File

@@ -1,128 +0,0 @@
//SECTION - Imports
//
//s1 PACKAGES
//---------------
//s2 CORE
import 'package:flutter/material.dart';
//s2 3RD-PARTY
//
//s1 DEPENDENCIES
//---------------
//s2 SERVICES
//s2 MODELS
//s2 MISC
//!SECTION - Imports
//
//SECTION - Exports
//!SECTION - Exports
//
class AstromicCustomToggle extends StatefulWidget {
//SECTION - Widget Arguments
//s1 -- Functionality
/// If provided, you have to change the variable yourself in the onStateChanged!
final bool? stateVariable;
final bool? initialState;
final void Function(bool)? onStateChanged;
//s1 -- Configuration
final bool? isEnabled;
//
//s1 -- Content
final Widget Function(bool isChecked, bool isEnabled, VoidCallback? onTap) itemBuilder;
//!SECTION
//
const AstromicCustomToggle({
super.key,
//s1 -- Functionality
this.stateVariable,
this.initialState,
this.onStateChanged,
//s1 -- Configuration
this.isEnabled = true,
//s1 -- Content
required this.itemBuilder,
}) : assert(stateVariable == null || initialState == null, "Can't define both the state variable and the initial state");
@override
State<AstromicCustomToggle> createState() => _AstromicCustomToggleState();
}
class _AstromicCustomToggleState extends State<AstromicCustomToggle> {
//
//SECTION - State Variables
//s1 --Controllers
//s1 --Controllers
//
//s1 --State
late bool currentState;
//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
currentState = widget.initialState ?? widget.stateVariable ?? false;
//s1 --State
//
//s1 --Late & Async Initializers
//s1 --Late & Async Initializers
//!SECTION
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
//
//SECTION - State Variables initializations & Listeners
//s1 --State
//s1 --State
//!SECTION
}
@override
void didUpdateWidget(AstromicCustomToggle oldWidget) {
if (widget.stateVariable != null && widget.stateVariable != oldWidget.stateVariable) {
currentState = widget.stateVariable!;
}
super.didUpdateWidget(oldWidget);
}
//SECTION - Stateless functions
//!SECTION
//SECTION - Action Callbacks
_onTap() {
setState(() {
currentState = !currentState;
if (widget.onStateChanged != null) widget.onStateChanged!(currentState);
});
}
//!SECTION
@override
Widget build(BuildContext context) {
//SECTION - Build Setup
//s1 -Values
//double w = MediaQuery.of(context).size.width;
//double h = MediaQuery.of(context).size.height;
//s1 -Values
//
//s1 -Widgets
//s1 -Widgets
//!SECTION
//SECTION - Build Return
return widget.itemBuilder(currentState, widget.isEnabled!, widget.isEnabled! ? () => _onTap() : null);
//!SECTION
}
}

View File

@@ -0,0 +1 @@
export './style.model.dart';

View File

@@ -1,5 +1,6 @@
import 'package:flutter/widgets.dart';
/// Styling model for the Switcher Toggle element.
class AstromicSwitcherToggleStyle {
final double width;
final double height;

View File

@@ -1,15 +1,17 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
//s2 Core Packages Imports
//s1 Imports
//s2 Core Package Imports
import 'package:flutter/material.dart';
//s2 1st-party Package Imports
//s2 3rd-party Package Imports
import 'package:flutter_switch/flutter_switch.dart';
//s2 Dependancies Imports
//s3 Routes
//s3 Services
//s3 Models
import 'style.dart';
import 'models/models.exports.dart';
import '../models/models.exports.dart';
//s1 Exports
//
//
class AstromicSwitcherToggle extends StatefulWidget {
//SECTION - Widget Arguments
//s1 -- Functionality
@@ -18,12 +20,9 @@ class AstromicSwitcherToggle extends StatefulWidget {
final bool? initialState;
final void Function(bool)? onStateChanged;
//s1 -- Configuration
final bool? isEnabled;
final bool? withLabel;
final bool? isLabelTapable;
final TextDirection? textDirection;
final AstromicToggleConfiguration? configuration;
//s1 -- Style
final AstromicSwitcherToggleStyle Function(bool isEnabled, bool isSelected) style;
final AstromicSwitcherToggleStyle Function(bool isEnabled, bool isSelected)? style;
//s1 -- Content
final Widget Function(bool isEnabled, bool isSelected)? label;
//!SECTION
@@ -34,12 +33,9 @@ class AstromicSwitcherToggle extends StatefulWidget {
this.initialState,
this.onStateChanged,
//
this.isEnabled = true,
this.withLabel = true,
this.isLabelTapable = true,
this.textDirection = TextDirection.ltr,
this.configuration,
//
required this.style,
this.style,
//
this.label,
});
@@ -56,6 +52,8 @@ class AstromicSwitcherToggleState extends State<AstromicSwitcherToggle> {
//
//s1 --State
late bool _currentState;
late AstromicSwitcherToggleStyle _style;
late AstromicToggleConfiguration _config;
//s1 --State
//
//s1 --Constants
@@ -100,6 +98,8 @@ class AstromicSwitcherToggleState extends State<AstromicSwitcherToggle> {
Widget build(BuildContext context) {
//SECTION - Build Setup
//s1 -Values
_config = widget.configuration ?? AstromicToggleConfiguration();
_style = widget.style != null ? widget.style!(_config.isEnabled, _currentState) : const AstromicSwitcherToggleStyle();
//s1 -Values
//
//s1 -Widgets
@@ -108,39 +108,39 @@ class AstromicSwitcherToggleState extends State<AstromicSwitcherToggle> {
//SECTION - Build Return
return Directionality(
textDirection: widget.textDirection ?? TextDirection.ltr,
textDirection: _config.textDirection,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
//S1 -- Item
FlutterSwitch(
width: widget.style(widget.isEnabled!, _currentState).width,
height: widget.style(widget.isEnabled!, _currentState).height,
borderRadius: widget.style(widget.isEnabled!, _currentState).borderRadius,
width: _style.width,
height: _style.height,
borderRadius: _style.borderRadius,
value: _currentState,
toggleSize: widget.style(widget.isEnabled!, _currentState).toggleSize,
toggleSize: _style.toggleSize,
disabled: false,
//
activeColor: widget.style(widget.isEnabled!, _currentState).activeColor,
activeToggleColor: widget.style(widget.isEnabled!, _currentState).activeToggleColor,
activeSwitchBorder: widget.style(widget.isEnabled!, _currentState).activeSwitchBorder,
activeToggleBorder: widget.style(widget.isEnabled!, _currentState).activeToggleBorder,
activeIcon: widget.style(widget.isEnabled!, _currentState).innerActiveWidget,
activeColor: _style.activeColor,
activeToggleColor: _style.activeToggleColor,
activeSwitchBorder: _style.activeSwitchBorder,
activeToggleBorder: _style.activeToggleBorder,
activeIcon: _style.innerActiveWidget,
//
inactiveColor: widget.style(widget.isEnabled!, _currentState).inactiveColor,
inactiveToggleColor: widget.style(widget.isEnabled!, _currentState).inactiveToggleColor,
inactiveSwitchBorder: widget.style(widget.isEnabled!, _currentState).inactiveSwitchBorder,
inactiveToggleBorder: widget.style(widget.isEnabled!, _currentState).inactiveToggleBorder,
inactiveIcon: widget.style(widget.isEnabled!, _currentState).innerInactiveWidget,
inactiveColor: _style.inactiveColor,
inactiveToggleColor: _style.inactiveToggleColor,
inactiveSwitchBorder: _style.inactiveSwitchBorder,
inactiveToggleBorder: _style.inactiveToggleBorder,
inactiveIcon: _style.innerInactiveWidget,
//
padding: widget.style(widget.isEnabled!, _currentState).togglePadding,
padding: _style.togglePadding,
showOnOff: false,
onToggle: (s) => _onTap(s),
),
//S1 -- Label Spacing
if (widget.withLabel!) SizedBox(width: widget.style(widget.isEnabled!, _currentState).labelSpacing),
if (_config.withLabel) SizedBox(width: _style.labelSpacing),
//S1 -- Label
if (widget.withLabel! && widget.label != null) GestureDetector(onTap: widget.isLabelTapable! ? () => _onTap(!_currentState) : null, child: widget.label!(widget.isEnabled!, _currentState)),
if (_config.withLabel && widget.label != null) GestureDetector(onTap: _config.isLabelTapable ? () => _onTap(!_currentState) : null, child: widget.label!(_config.isEnabled, _currentState)),
],
),
);

View File

@@ -0,0 +1,29 @@
import 'dart:ui';
/// SConfiguration model for the Toggles element group.
class AstromicToggleConfiguration {
final bool isEnabled;
final bool withLabel;
final bool isLabelTapable;
final TextDirection textDirection;
AstromicToggleConfiguration({
this.isEnabled = true,
this.withLabel = false,
this.isLabelTapable = true,
this.textDirection = TextDirection.ltr,
});
AstromicToggleConfiguration copyWith({
bool? isEnabled,
bool? withLabel,
bool? isLabelTapable,
TextDirection? textDirection,
}) {
return AstromicToggleConfiguration(
isEnabled: isEnabled ?? this.isEnabled,
withLabel: withLabel ?? this.withLabel,
isLabelTapable: isLabelTapable ?? this.isLabelTapable,
textDirection: textDirection ?? this.textDirection,
);
}
}

View File

@@ -0,0 +1 @@
export './configuration.model.dart';

View File

@@ -1,92 +1,58 @@
//s2 Core Packages Imports
//s1 Imports
//s2 Core Package Imports
import 'package:flutter/widgets.dart';
import 'Checkbox/checkbox.toggle.dart';
import 'Switcher/switcher.toggle.dart';
import 'Custom/custom.toggle.dart';
//
import 'Switcher/style.dart';
import 'Checkbox/style.dart';
//
export 'Switcher/style.dart';
export 'Checkbox/style.dart';
//s2 1st-party Package Imports
//s2 3rd-party Package Imports
//s2 Dependancies Imports
//s3 Routes
//s3 Services
//s3 Models
import 'checkbox/checkbox.toggle.dart';
import 'switcher/switcher.toggle.dart';
import 'switcher/models/models.exports.dart';
import 'checkbox/models/models.exports.dart';
import 'models/models.exports.dart';
//s1 Exports
export 'switcher/models/models.exports.dart';
export 'checkbox/models/models.exports.dart';
export 'models/models.exports.dart';
class AstromicToggles {
//S1 -- Checkbox
/// A CheckBox element that toggles state On and Off.
static Widget checkBox({
//
bool? stateVariable,
bool? initialState,
void Function(bool)? onStateChanged,
//
bool? isEnabled,
bool? withLabel,
bool? isLabelTapable,
TextDirection? textDirection,
//
required AstromicCheckboxToggleStyle Function(bool isEnabled, bool isSelected) style,
//
AstromicToggleConfiguration? configuration,
AstromicCheckboxToggleStyle Function(bool isEnabled, bool isSelected)? style,
Widget Function(bool isEnabled, bool isSelected)? innerWidget,
Widget Function(bool isEnabled, bool isSelected)? label,
//
}) =>
AstromicCheckboxToggle(
stateVariable: stateVariable,
initialState: initialState,
onStateChanged: onStateChanged,
isEnabled: isEnabled ?? true,
withLabel: withLabel ?? false,
isLabelTapable: isLabelTapable ?? false,
textDirection: textDirection ?? TextDirection.ltr,
configuration: configuration,
style: style,
innerWidget: innerWidget,
label: label,
);
//S1 -- Switcher
/// A Switch element that toggles state On and Off.
static Widget switcher({
//
bool? stateVariable,
bool? initialState,
void Function(bool)? onStateChanged,
//
bool? isEnabled,
bool? withLabel,
bool? isLabelTapable,
TextDirection? textDirection,
//
required AstromicSwitcherToggleStyle Function(bool isEnabled, bool isSelected) style,
//
AstromicToggleConfiguration? configuration,
AstromicSwitcherToggleStyle Function(bool isEnabled, bool isSelected)? style,
Widget Function(bool isEnabled, bool isSelected)? label,
//
}) =>
AstromicSwitcherToggle(
stateVariable: stateVariable,
initialState: initialState,
onStateChanged: onStateChanged,
isEnabled: isEnabled ?? true,
withLabel: withLabel ?? false,
isLabelTapable: isLabelTapable ?? false,
textDirection: textDirection ?? TextDirection.ltr,
configuration: configuration,
style: style,
label: label,
);
//S1 -- Custom
static Widget custom({
//
bool? stateVariable,
bool? initialState,
void Function(bool)? onStateChanged,
//
bool? isEnabled,
//
required Widget Function(bool isChecked, bool isEnabled, VoidCallback? onTap) itemBuilder,
}) =>
AstromicCustomToggle(
stateVariable: stateVariable,
initialState: initialState,
onStateChanged: onStateChanged,
isEnabled: isEnabled ?? true,
itemBuilder: itemBuilder,
);
}

View File

@@ -1,217 +1,313 @@
import 'dart:typed_data';
import 'package:cached_network_image/cached_network_image.dart';
//s1 Imports
//s2 Core Package Imports
import 'package:flutter/widgets.dart';
import 'dart:convert';
import 'dart:typed_data';
//s2 1st-party Package Imports
//s2 3rd-party Package Imports
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:octo_image/octo_image.dart';
//s2 Dependancies Imports
//s3 Routes
//s3 Services
//s3 Models
//s1 Exports
enum ImageSizingMaster {
w,
h,
}
enum _DeclaredAssetType {
path,
url,
bytes,
fallback,
}
_DeclaredAssetType _parseAssetType(
String? path,
String? url,
Uint8List? bytes,
String? fallback,
) {
bool fromPath = (path != null && path != '');
bool fromBytes = (bytes != null && bytes.isNotEmpty);
bool fromNetwork = (url != null && url.isNotEmpty && Uri.tryParse(url)?.hasAbsolutePath == true && (url.startsWith('http://') || url.startsWith('https://')));
Widget astromicImage(
BuildContext context, {
//S1 -- Asset
String? assetPath,
String? assetURL,
Uint8List? assetBytes,
String? assetFallback,
//
//S1 -- Sizing | Width
double? wFactor, // variable width ratio
double? minW, // Min width allowed
double? maxW, // Max width allowed
double? fixedWidth, // Only used when uisng height variables
//S1 -- Sizing | Width
bool useHeight = false, // use height constrains
double? hFactor, // Variable height ratio
double? minH, // Min Height allowed
double? maxH, // Max height allowed
double? fixedHeight, // Only used when using width variables
return fromPath
? _DeclaredAssetType.path
: fromBytes
? _DeclaredAssetType.bytes
: fromNetwork
? _DeclaredAssetType.url
: _DeclaredAssetType.fallback;
}
class AstromicImage extends StatelessWidget {
//SECTION - Widget Arguments
//S1 -- Assets
final String? assetPath;
final String? assetURL;
final Uint8List? assetBytes;
final String? assetFallback;
//S1 -- Sizing
final ImageSizingMaster? sizingMaster;
final (double factor, double? min, double? max)? widthSizing;
/// Used when the width is Master and want to set fixed width OR if height is Master and want to constraint the width
final double? fixedWidth;
final (double factor, double? min, double? max)? heightSizing;
/// Used when the height is Master and want to set fixed height OR if width is Master and want to constraint the height
final double? fixedHeight;
//S1 -- STYLING
bool circular = false,
double? border,
Color? borderColor,
EdgeInsetsGeometry? borderPadding,
BorderRadiusGeometry radius = BorderRadius.zero,
List<BoxShadow>? shadow,
final bool? isCircular;
final double? borderWidth;
final Color? borderColor;
final EdgeInsetsGeometry? borderPadding;
final BorderRadiusGeometry? radius;
final List<BoxShadow>? shadow;
final Color? overlayColor;
final Gradient? overlayGradient;
//S1 -- CONFIGURATIONS
final Alignment? alignment;
final BoxFit? fit;
final BlendMode? blendMode;
final Curve? fadeInCurve;
final Duration? fadeInDuration;
//S1 -- SVG FILTERS
Color? color,
BlendMode? blend,
//S1 -- CONFIGS
BoxFit fit = BoxFit.cover,
Alignment alignment = Alignment.center,
LinearGradient? linearGradient,
//S1 -- STATE
Widget? loadingWidget,
Widget? errorWidget,
}) {
final Color? svgColor;
//S1 -- STATE WIDGETS
final Widget Function(int? loadedBytes, int? totalBytesToLoad)? loadingWidget;
final Widget Function(dynamic error, StackTrace? stackTrace)? errorWidget;
//!SECTION
//
assert(minW == null || maxW == null, "Please specify only one width constrain");
assert(minH == null || maxH == null, "Please specify only one height constrain");
assert(!useHeight || (hFactor != null), "Please specify The height factor and constrains");
assert(
(assetPath != null && assetBytes == null && assetURL == null) ||
(assetPath == null && assetBytes != null && assetURL == null) ||
(assetPath == null && assetBytes == null && assetURL != null) ||
(assetFallback != null && assetFallback != ''),
"Please specify only one Asset Source");
//
//
bool fromPath = (assetPath != null && assetPath != '');
bool fromBytes = (assetBytes != null && assetBytes.isNotEmpty);
bool fromNetwork = (assetURL != null && assetURL != '' && Uri.tryParse(assetURL) != null);
//
String finalAssetKey = fromPath
? assetPath
: fromNetwork
? assetURL
: fromBytes
? assetBytes.length.toString()
: 'N/A';
//
bool isFallback = (assetBytes == null && assetURL == null && assetPath == null && assetFallback != null);
bool isSVG = fromPath
? assetPath.endsWith('.svg')
: fromNetwork
? assetURL.endsWith('.svg')
: isFallback
? assetFallback.endsWith('.svg')
: false;
//
double h = MediaQuery.of(context).size.height;
double? fh = hFactor != null ? hFactor * h : null;
double w = MediaQuery.of(context).size.width;
double? fw = wFactor != null ? wFactor * w : null;
AstromicImage({
super.key,
//
this.assetPath,
this.assetURL,
this.assetBytes,
this.assetFallback,
//
this.sizingMaster = ImageSizingMaster.w,
this.widthSizing,
this.heightSizing,
this.fixedWidth,
this.fixedHeight,
//
this.isCircular = false,
this.borderWidth,
this.borderColor,
this.borderPadding,
this.radius = BorderRadius.zero,
this.shadow,
this.overlayColor,
this.overlayGradient,
//
this.fit = BoxFit.cover,
this.alignment = Alignment.center,
this.blendMode,
this.fadeInCurve = Curves.ease,
this.fadeInDuration = const Duration(milliseconds: 250),
//
this.svgColor,
//
this.loadingWidget,
this.errorWidget,
}) :
// Assert that a source is provided, or provide a fallback source..
assert(((assetPath?.isNotEmpty ?? false) || (assetURL?.isNotEmpty ?? false) || (assetBytes?.isNotEmpty ?? false)) || (assetFallback?.isNotEmpty ?? false),
"Please specify a source or provide a fallback."),
// Assert that only ONE source is provided...
assert(
(assetPath != null && assetBytes == null && assetURL == null) ||
(assetPath == null && assetBytes != null && assetURL == null) ||
(assetPath == null && assetBytes == null && assetURL != null),
"Please specify only ONE Asset Source"),
// Assert that correct sizing plan is provided...
assert((sizingMaster == ImageSizingMaster.w && (widthSizing != null || fixedWidth != null)) || (sizingMaster == ImageSizingMaster.h && (heightSizing != null || fixedHeight != null)),
"Please provide the correct sizing configurations based on the SizingMaster choosen");
//
double? finalW = useHeight
? fixedWidth
: fw != null
? fw > (maxW ?? double.infinity)
? maxW
: fw < (minW ?? 0)
? minW
: fw
: null;
double? finalH = !useHeight
? fixedHeight
: fh != null
? fh > (maxH ?? double.infinity)
? maxH
: fh < (minH ?? 0)
? minH
: fh
: null;
//
Widget finalError = errorWidget ??
const Center(
child: Text("Error has happened.."),
);
//
Widget finalLoading = loadingWidget ?? Container();
//
ImageProvider finalImageProvider = isFallback
? AssetImage(
assetFallback,
)
: fromPath
? AssetImage(
assetPath,
)
: fromBytes
? MemoryImage(assetBytes)
: fromNetwork
? CachedNetworkImageProvider(
assetURL,
cacheKey: assetURL,
)
: MemoryImage(kTransparentImage) as ImageProvider;
//
Widget? finalSVGWidget = fromPath
? SvgPicture.asset(
assetPath,
key: ValueKey(assetPath),
width: finalW,
height: finalH,
fit: fit,
placeholderBuilder: (_) => SizedBox(
width: finalW,
height: finalH,
child: finalLoading,
),
alignment: alignment,
colorFilter: color != null ? ColorFilter.mode(color, blend ?? BlendMode.srcATop) : null,
)
: fromNetwork
? SvgPicture.network(
assetURL,
key: ValueKey(assetURL),
width: finalW,
height: finalH,
fit: fit,
placeholderBuilder: (_) => SizedBox(
width: finalW,
height: finalH,
child: finalLoading,
@override
Widget build(BuildContext context) {
//SECTION - Build Setup
//s1 -Values
_DeclaredAssetType assetType = _parseAssetType(assetPath, assetURL, assetBytes, assetFallback);
dynamic assetRef = _getAssetRef(assetType);
bool isSVG = _isSVG(assetType, assetRef);
//s1 -Values
//
//s1 -Widgets
// Default Loading Widget
Widget defaultLoadingWidget = const Text('Loading...');
// Default Error Widget
Widget defaultErrorWidget(dynamic error) => Text('An error has happened: $error');
// Get final svg widget
Widget? finalSVGWidget(Size size, Widget? loadingWidget) => assetType == _DeclaredAssetType.path
? SvgPicture.asset(
assetRef,
key: ValueKey(assetRef),
width: size.width,
height: size.height,
fit: fit!,
placeholderBuilder: (_) => SizedBox(
width: size.width,
height: size.height,
child: loadingWidget,
),
alignment: alignment!,
colorFilter: svgColor != null ? ColorFilter.mode(svgColor!, blendMode ?? BlendMode.srcATop) : null,
)
: assetType == _DeclaredAssetType.url
? SvgPicture.network(
assetRef,
key: ValueKey(assetRef),
width: size.width,
height: size.height,
fit: fit!,
placeholderBuilder: (_) => SizedBox(
width: size.width,
height: size.height,
child: loadingWidget,
),
alignment: alignment!,
colorFilter: svgColor != null ? ColorFilter.mode(svgColor!, blendMode ?? BlendMode.srcATop) : null,
)
: null;
Widget buildImage(Size size) {
return Stack(
children: [
OctoImage(
key: ValueKey(assetType == _DeclaredAssetType.bytes ? (assetRef as Uint8List).length.toString() : assetRef),
//
width: size.width,
height: size.height,
fit: fit,
alignment: alignment,
filterQuality: FilterQuality.none,
color: svgColor,
colorBlendMode: blendMode ?? (isSVG ? BlendMode.srcATop : null),
//
errorBuilder: (context, error, stackTrace) {
debugPrint("AstromicImage Error: $error");
return errorWidget != null
? errorWidget!(error, stackTrace)
: assetFallback != null
? Image.asset(assetFallback!)
: defaultErrorWidget(error);
},
//
progressIndicatorBuilder: (_, bytes) => SizedBox(
width: size.width,
height: size.height,
child: loadingWidget != null ? loadingWidget!(bytes?.cumulativeBytesLoaded, bytes?.expectedTotalBytes) : defaultLoadingWidget,
),
placeholderBuilder: (context) => loadingWidget != null ? loadingWidget!(null, null) : defaultLoadingWidget,
fadeInCurve: fadeInCurve,
fadeInDuration: fadeInDuration,
imageBuilder: (context, image) => Container(
width: size.width,
height: size.height,
padding: borderPadding ?? EdgeInsets.zero,
margin: EdgeInsets.zero,
decoration: BoxDecoration(
border: borderWidth != null
? Border.all(
strokeAlign: BorderSide.strokeAlignInside,
width: borderWidth!,
color: borderColor ?? const Color(0xff000000),
)
: null,
borderRadius: isCircular! ? BorderRadius.circular(10000000) : radius,
boxShadow: shadow,
),
alignment: alignment,
colorFilter: color != null ? ColorFilter.mode(color, blend ?? BlendMode.srcATop) : null,
)
: null;
//
return SizedBox(
width: finalW,
height: finalH,
child: Stack(
children: [
OctoImage(
key: ValueKey(finalAssetKey),
//
width: finalW,
height: finalH,
fit: fit,
alignment: alignment,
filterQuality: FilterQuality.none,
color: color,
colorBlendMode: blend ?? (isSVG ? BlendMode.srcATop : null),
//
errorBuilder: (context, error, stackTrace) {
return finalError;
},
//
progressIndicatorBuilder: (a, b) => SizedBox(
width: finalW,
height: finalH,
child: finalLoading,
),
imageBuilder: (context, image) => Container(
width: finalW,
height: finalH,
padding: borderPadding ?? EdgeInsets.zero,
margin: EdgeInsets.zero,
decoration: BoxDecoration(
border: border != null
? Border.all(
strokeAlign: BorderSide.strokeAlignInside,
width: border,
color: borderColor ?? const Color(0xff000000),
child: isCircular!
? ClipOval(
child: isSVG ? finalSVGWidget(size, loadingWidget != null ? loadingWidget!(null, null) : defaultLoadingWidget) : image,
)
: null,
borderRadius: circular ? BorderRadius.circular(10000) : radius,
boxShadow: shadow,
: ClipRRect(
borderRadius: isCircular! ? BorderRadius.circular(10000000) : radius!,
child: isSVG ? finalSVGWidget(size, loadingWidget != null ? loadingWidget!(null, null) : defaultLoadingWidget) : image,
),
),
child: ClipRRect(
borderRadius: circular ? BorderRadius.circular(10000) : radius,
child: isSVG ? finalSVGWidget : image,
image: isSVG ? MemoryImage(kTransparentImage) : _imageProvider(assetType, assetRef),
),
if (overlayColor != null || overlayGradient != null)
Container(
decoration: BoxDecoration(color: overlayGradient != null ? null : overlayColor, gradient: overlayGradient, borderRadius: radius),
),
),
image: isSVG ? MemoryImage(kTransparentImage) : finalImageProvider,
),
if (linearGradient != null)
Container(
decoration: BoxDecoration(gradient: linearGradient, borderRadius: radius),
),
],
),
);
],
);
}
//s1 -Widgets
//!SECTION
//SECTION - Build Return
return LayoutBuilder(builder: (context, constraints) {
Size size = _calculateSize(constraints);
return SizedBox(width: size.width, height: size.height, child: buildImage(size));
});
//!SECTION
}
//SECTION - Helper Functions
// Get asset reference based on asset type...
dynamic _getAssetRef(_DeclaredAssetType type) {
switch (type) {
case _DeclaredAssetType.path:
return assetPath;
case _DeclaredAssetType.bytes:
return assetBytes;
case _DeclaredAssetType.url:
return assetURL;
case _DeclaredAssetType.fallback:
return assetFallback;
}
}
// Detect if asset is an SVG...
bool _isSVG(_DeclaredAssetType type, dynamic ref) =>
type == _DeclaredAssetType.bytes ? utf8.decode(ref.sublist(0, ref.length > 10 ? 10 : ref.length), allowMalformed: true).trimLeft().startsWith('<svg') : (ref?.endsWith('.svg') ?? false);
// Get image provider based on type
ImageProvider _imageProvider(_DeclaredAssetType type, dynamic ref) {
switch (type) {
case _DeclaredAssetType.fallback:
return AssetImage(ref);
case _DeclaredAssetType.path:
return AssetImage(ref);
case _DeclaredAssetType.bytes:
return MemoryImage(ref);
case _DeclaredAssetType.url:
return CachedNetworkImageProvider(ref, cacheKey: ref);
}
}
// Calculate the sizing of the image...
Size _calculateSize(BoxConstraints constraints) {
double? maxAvailablewidth = constraints.maxWidth;
double? maxAvailableheight = constraints.maxHeight;
Size finalSize;
switch (sizingMaster!) {
case ImageSizingMaster.w:
{
finalSize = Size(fixedWidth ?? (widthSizing!.$1 * maxAvailablewidth).clamp(widthSizing!.$2 ?? 0, widthSizing!.$3 ?? double.infinity), fixedHeight ?? maxAvailableheight);
}
case ImageSizingMaster.h:
{
finalSize = Size(fixedWidth ?? maxAvailablewidth, fixedHeight ?? (heightSizing!.$1 * maxAvailableheight).clamp(heightSizing!.$2 ?? 0, heightSizing!.$3 ?? double.infinity));
}
}
return finalSize;
}
//!SECTION
}
final Uint8List kTransparentImage = Uint8List.fromList(<int>[

View File

@@ -58,75 +58,65 @@ class AstromicWidgets {
);
//S1 -- IMAGE
static Widget image(
BuildContext context, {
//S1 -- Asset
static Widget image({
String? assetPath,
String? assetURL,
Uint8List? assetBytes,
String? assetFallback,
//S1 -- Sizing | Width
double? wFactor,
double? minW,
double? maxW,
ImageSizingMaster? sizingMaster,
(double factor, double? min, double? max)? widthSizing,
/// Used when the width is Master and want to set fixed width OR if height is Master and want to constraint the width
double? fixedWidth,
//S1 -- Sizing | Width
bool useHeight = false,
double? hFactor,
double? minH,
double? maxH,
(double factor, double? min, double? max)? heightSizing,
/// Used when the height is Master and want to set fixed height OR if width is Master and want to constraint the height
double? fixedHeight,
//S1 -- STYLING
bool circular = false,
double? border,
bool? isCircular,
double? borderWidth,
Color? borderColor,
EdgeInsetsGeometry? borderPadding,
BorderRadiusGeometry radius = BorderRadius.zero,
BorderRadiusGeometry? radius,
List<BoxShadow>? shadow,
//S1 -- SVG FILTERS
Color? color,
BlendMode? blend,
//S1 -- CONFIGS
BoxFit fit = BoxFit.cover,
Alignment alignment = Alignment.center,
LinearGradient? linearGradient,
//S1 -- STATE
Widget? loadingWidget,
Widget? errorWidget,
Color? overlayColor,
Gradient? overlayGradient,
Alignment? alignment,
BoxFit? fit,
BlendMode? blendMode,
Curve? fadeInCurve,
Duration? fadeInDuration,
Color? svgColor,
Widget Function(int? loadedBytes, int? totalBytesToLoad)? loadingWidget,
Widget Function(dynamic error, StackTrace? stackTrace)? errorWidget,
}) =>
astromicImage(
context,
//
AstromicImage(
assetPath: assetPath,
assetURL: assetURL,
assetBytes: assetBytes,
assetFallback: assetFallback,
//
wFactor: wFactor,
minW: minW,
maxW: maxW,
sizingMaster: sizingMaster,
widthSizing: widthSizing,
/// Used when the width is Master and want to set fixed width OR if height is Master and want to constraint the width
fixedWidth: fixedWidth,
//
useHeight: useHeight,
hFactor: hFactor,
minH: minH,
maxH: maxH,
heightSizing: heightSizing,
/// Used when the height is Master and want to set fixed height OR if width is Master and want to constraint the height
fixedHeight: fixedHeight,
//
circular: circular,
border: border,
isCircular: isCircular,
borderWidth: borderWidth,
borderColor: borderColor,
borderPadding: borderPadding,
radius: radius,
shadow: shadow,
//
color: color,
blend: blend,
//
fit: fit,
overlayColor: overlayColor,
overlayGradient: overlayGradient,
alignment: alignment,
linearGradient: linearGradient,
//
fit: fit,
blendMode: blendMode,
fadeInCurve: fadeInCurve,
fadeInDuration: fadeInDuration,
svgColor: svgColor,
loadingWidget: loadingWidget,
errorWidget: errorWidget,
);