Files
numstation-flutter/lib/flutterlib/flutter_choice_chips.dart

184 lines
5.5 KiB
Dart
Raw Permalink Normal View History

2023-12-27 16:10:09 +08:00
import 'form_field_controller.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import '/flutterlib/flutter_util.dart';
class ChipData {
const ChipData(this.label, [this.iconData]);
final String label;
final IconData? iconData;
}
class ChipStyle {
const ChipStyle({
this.backgroundColor,
this.textStyle,
this.iconColor,
this.iconSize,
this.labelPadding,
this.elevation,
this.borderColor,
this.borderWidth,
this.borderRadius,
});
final Color? backgroundColor;
final TextStyle? textStyle;
final Color? iconColor;
final double? iconSize;
final EdgeInsetsGeometry? labelPadding;
final double? elevation;
final Color? borderColor;
final double? borderWidth;
final BorderRadius? borderRadius;
}
class FlutterChoiceChips extends StatefulWidget {
const FlutterChoiceChips({
super.key,
required this.options,
required this.onChanged,
required this.controller,
required this.selectedChipStyle,
required this.unselectedChipStyle,
required this.chipSpacing,
this.rowSpacing = 0.0,
required this.multiselect,
this.initialized = true,
this.alignment = WrapAlignment.start,
this.disabledColor,
this.wrapped = true,
});
final List<ChipData> options;
final void Function(List<String>?)? onChanged;
final FormFieldController<List<String>> controller;
final ChipStyle selectedChipStyle;
final ChipStyle unselectedChipStyle;
final double chipSpacing;
final double rowSpacing;
final bool multiselect;
final bool initialized;
final WrapAlignment alignment;
final Color? disabledColor;
final bool wrapped;
@override
State<FlutterChoiceChips> createState() => _FlutterChoiceChipsState();
}
class _FlutterChoiceChipsState extends State<FlutterChoiceChips> {
late List<String> choiceChipValues;
ValueListenable<List<String>?> get changeSelectedValues => widget.controller;
List<String> get selectedValues => widget.controller.value ?? [];
@override
void initState() {
super.initState();
choiceChipValues = selectedValues;
if (!widget.initialized && choiceChipValues.isNotEmpty) {
SchedulerBinding.instance.addPostFrameCallback(
(_) {
if (widget.onChanged != null) {
widget.onChanged!(choiceChipValues);
}
},
);
}
changeSelectedValues.addListener(() {
if (widget.onChanged != null) {
widget.onChanged!(selectedValues);
}
});
}
@override
void dispose() {
changeSelectedValues.removeListener(() {});
super.dispose();
}
@override
Widget build(BuildContext context) {
final children = widget.options.map<Widget>(
(option) {
final selected = selectedValues.contains(option.label);
final style =
selected ? widget.selectedChipStyle : widget.unselectedChipStyle;
return Theme(
data: Theme.of(context).copyWith(canvasColor: Colors.transparent),
child: ChoiceChip(
selected: selected,
onSelected: widget.onChanged != null
? (isSelected) {
if (isSelected) {
widget.multiselect
? choiceChipValues.add(option.label)
: choiceChipValues = [option.label];
widget.controller.value = List.from(choiceChipValues);
setState(() {});
} else {
if (widget.multiselect) {
choiceChipValues.remove(option.label);
widget.controller.value = List.from(choiceChipValues);
setState(() {});
}
}
}
: null,
label: Text(
option.label,
style: style.textStyle,
),
labelPadding: style.labelPadding,
avatar: option.iconData != null
? FaIcon(
option.iconData,
size: style.iconSize,
color: style.iconColor,
)
: null,
elevation: style.elevation,
disabledColor: widget.disabledColor,
selectedColor:
selected ? widget.selectedChipStyle.backgroundColor : null,
backgroundColor:
selected ? null : widget.unselectedChipStyle.backgroundColor,
shape: RoundedRectangleBorder(
borderRadius: style.borderRadius ?? BorderRadius.circular(16),
side: BorderSide(
color: style.borderColor ?? Colors.transparent,
width: style.borderWidth ?? 0,
),
),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
);
},
).toList();
if (widget.wrapped) {
return Wrap(
spacing: widget.chipSpacing,
runSpacing: widget.rowSpacing,
alignment: widget.alignment,
crossAxisAlignment: WrapCrossAlignment.center,
children: children,
);
} else {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
clipBehavior: Clip.none,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: children.divide(
SizedBox(width: widget.chipSpacing),
),
),
);
}
}
}