import 'package:flutter/material.dart'; import 'package:fladder/util/position_provider.dart'; class ExpressiveButtonGroup extends StatelessWidget { final List> options; final Set selectedValues; final ValueChanged> onSelected; final bool multiSelection; const ExpressiveButtonGroup({ super.key, required this.options, required this.selectedValues, required this.onSelected, this.multiSelection = false, }); @override Widget build(BuildContext context) { return Row( mainAxisSize: MainAxisSize.max, spacing: 2, crossAxisAlignment: CrossAxisAlignment.stretch, children: List.generate( options.length, (index) { final option = options[index]; final isSelected = selectedValues.contains(option.value); final position = index == 0 ? PositionContext.first : (index == options.length - 1 ? PositionContext.last : PositionContext.middle); return PositionProvider( position: position, child: ExpressiveButton( isSelected: isSelected, label: option.child, icon: isSelected ? option.selected ?? const Icon(Icons.check_rounded) : option.icon, onPressed: () { final newSet = Set.from(selectedValues); if (multiSelection) { isSelected ? newSet.remove(option.value) : newSet.add(option.value); } else { newSet ..clear() ..add(option.value); } onSelected(newSet); }, ), ); }, ), ); } } class ExpressiveButton extends StatelessWidget { const ExpressiveButton({ super.key, required this.isSelected, required this.label, this.icon, required this.onPressed, }); final bool isSelected; final Widget label; final Widget? icon; final Function()? onPressed; @override Widget build(BuildContext context) { final position = PositionProvider.of(context); final borderRadius = BorderRadius.horizontal( left: isSelected || position == PositionContext.first ? const Radius.circular(16) : const Radius.circular(4), right: isSelected || position == PositionContext.last ? const Radius.circular(16) : const Radius.circular(4), ); return ElevatedButton.icon( style: ButtonStyle( shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: borderRadius)), elevation: WidgetStatePropertyAll(isSelected ? 4 : 0), backgroundColor: WidgetStatePropertyAll( isSelected ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.surfaceContainerHighest), foregroundColor: WidgetStatePropertyAll( isSelected ? Theme.of(context).colorScheme.onPrimary : Theme.of(context).colorScheme.onSurfaceVariant), textStyle: WidgetStatePropertyAll(Theme.of(context).textTheme.labelLarge), visualDensity: VisualDensity.comfortable, side: WidgetStateProperty.resolveWith((states) => BorderSide( width: 2, color: (isSelected ? Theme.of(context).colorScheme.onPrimary : Theme.of(context).colorScheme.onPrimaryContainer) .withValues(alpha: states.contains(WidgetState.focused) ? 1.0 : 0), )), padding: const WidgetStatePropertyAll(EdgeInsets.all(12)), ), onPressed: onPressed, label: label, icon: icon, ); } } class ButtonGroupOption { final T value; final Icon? icon; final Icon? selected; final Widget child; const ButtonGroupOption({ required this.value, this.icon, this.selected, required this.child, }); }