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,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,
);
}