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 options; final void Function(List?)? onChanged; final FormFieldController> 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 createState() => _FlutterChoiceChipsState(); } class _FlutterChoiceChipsState extends State { late List choiceChipValues; ValueListenable?> get changeSelectedValues => widget.controller; List 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( (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), ), ), ); } } }