import 'dart:developer'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import '../../utils/bot_style.dart'; import 'it_shimmer.dart'; typedef ChoiceCallback = void Function(String value, int index); class ChoicesArray extends StatefulWidget { final bool isLoading; final List? choices; final ChoiceCallback onPressed; final ChoiceCallback? onLongPress; final int? selectedChoiceIndex; final String originalSpan; final String Function(int) uniqueKeyForLayerLink; /// some uses of this widget want to disable clicking of the choices final bool isActive; const ChoicesArray({ super.key, required this.isLoading, required this.choices, required this.onPressed, required this.originalSpan, required this.uniqueKeyForLayerLink, required this.selectedChoiceIndex, this.isActive = true, this.onLongPress, }); @override ChoicesArrayState createState() => ChoicesArrayState(); } class ChoicesArrayState extends State { bool interactionDisabled = false; void disableInteraction() { WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) setState(() => interactionDisabled = true); }); } void enableInteractions() { WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) setState(() => interactionDisabled = false); }); } @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); return widget.isLoading && (widget.choices == null || widget.choices!.length <= 1) ? ItShimmer(originalSpan: widget.originalSpan) : Wrap( alignment: WrapAlignment.center, children: widget.choices! .mapIndexed( (index, entry) => ChoiceItem( theme: theme, onLongPress: widget.isActive ? widget.onLongPress : null, onPressed: widget.isActive ? widget.onPressed : (String value, int index) { debugger(when: kDebugMode); }, entry: MapEntry(index, entry), interactionDisabled: interactionDisabled, enableInteraction: enableInteractions, disableInteraction: disableInteraction, isSelected: widget.selectedChoiceIndex == index, ), ) .toList(), ); } } class Choice { Choice({ this.color, required this.text, this.isGold = false, }); final Color? color; final String text; final bool isGold; } class ChoiceItem extends StatelessWidget { const ChoiceItem({ super.key, required this.theme, required this.onLongPress, required this.onPressed, required this.entry, required this.isSelected, required this.interactionDisabled, required this.enableInteraction, required this.disableInteraction, }); final MapEntry entry; final ThemeData theme; final ChoiceCallback? onLongPress; final ChoiceCallback onPressed; final bool isSelected; final bool interactionDisabled; final VoidCallback enableInteraction; final VoidCallback disableInteraction; @override Widget build(BuildContext context) { try { return Tooltip( message: onLongPress != null ? L10n.of(context)!.holdForInfo : "", waitDuration: onLongPress != null ? const Duration(milliseconds: 500) : const Duration(days: 1), child: ChoiceAnimationWidget( key: ValueKey(entry.value.text), selected: entry.value.color != null, isGold: entry.value.isGold, enableInteraction: enableInteraction, disableInteraction: disableInteraction, child: Container( margin: const EdgeInsets.all(2), padding: EdgeInsets.zero, decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(10)), border: Border.all( color: isSelected ? entry.value.color ?? theme.colorScheme.primary : Colors.transparent, style: BorderStyle.solid, width: 2.0, ), ), child: TextButton( style: ButtonStyle( padding: WidgetStateProperty.all( const EdgeInsets.symmetric(horizontal: 7), ), //if index is selected, then give the background a slight primary color backgroundColor: entry.value.color != null ? WidgetStateProperty.all( entry.value.color!.withOpacity(0.2), ) // : theme.colorScheme.primaryFixed, : null, textStyle: WidgetStateProperty.all( BotStyle.text(context), ), shape: WidgetStateProperty.all( RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), ), onLongPress: onLongPress != null && !interactionDisabled ? () => onLongPress!(entry.value.text, entry.key) : null, onPressed: interactionDisabled ? null : () => onPressed(entry.value.text, entry.key), child: Text( entry.value.text, style: BotStyle.text(context), ), ), ), ), ); } catch (e) { debugger(when: kDebugMode); return Container(); } } } class ChoiceAnimationWidget extends StatefulWidget { final Widget child; final bool selected; final bool isGold; final VoidCallback enableInteraction; final VoidCallback disableInteraction; const ChoiceAnimationWidget({ super.key, required this.child, required this.selected, required this.enableInteraction, required this.disableInteraction, this.isGold = false, }); @override ChoiceAnimationWidgetState createState() => ChoiceAnimationWidgetState(); } enum AnimationState { ready, forward, reverse, finished } class ChoiceAnimationWidgetState extends State with SingleTickerProviderStateMixin { late final AnimationController _controller; late final Animation _animation; // AnimationState animationState = AnimationState.ready; @override void initState() { super.initState(); // _controller = AnimationController( // duration: const Duration(milliseconds: 300), // vsync: this, // ); // _animation = widget.isGold // ? Tween(begin: 1.0, end: 1.2).animate(_controller) // : TweenSequence([ // TweenSequenceItem( // tween: Tween(begin: 0, end: -8 * pi / 180), // weight: 1.0, // ), // TweenSequenceItem( // tween: Tween(begin: -8 * pi / 180, end: 16 * pi / 180), // weight: 2.0, // ), // TweenSequenceItem( // tween: Tween(begin: 16 * pi / 180, end: 0), // weight: 1.0, // ), // ]).animate(_controller); widget.enableInteraction(); // if (widget.selected && animationState == AnimationState.ready) { // widget.disableInteraction(); // _controller.forward(); // setState(() { // animationState = AnimationState.forward; // }); // } // _controller.addStatusListener((status) { // if (status == AnimationStatus.completed && // animationState == AnimationState.forward) { // _controller.reverse(); // setState(() { // animationState = AnimationState.reverse; // }); // } // if (status == AnimationStatus.dismissed && // animationState == AnimationState.reverse) { // widget.enableInteraction(); // setState(() { // animationState = AnimationState.finished; // }); // } // }); } @override void didUpdateWidget(ChoiceAnimationWidget oldWidget) { super.didUpdateWidget(oldWidget); // if (widget.selected && animationState == AnimationState.ready) { // widget.disableInteraction(); // _controller.forward(); // setState(() { // animationState = AnimationState.forward; // }); // } } @override Widget build(BuildContext context) { return widget.child; // widget.isGold // ? AnimatedBuilder( // key: UniqueKey(), // animation: _animation, // builder: (context, child) { // return Transform.scale( // scale: _animation.value, // child: child, // ); // }, // child: widget.child, // ) // : AnimatedBuilder( // key: UniqueKey(), // animation: _animation, // builder: (context, child) { // return Transform.rotate( // angle: _animation.value, // child: child, // ); // }, // child: widget.child, // ); } @override void dispose() { _controller.dispose(); super.dispose(); } }