import 'package:ente_auth/models/execution_states.dart'; import 'package:ente_auth/theme/ente_theme.dart'; import 'package:ente_auth/ui/common/loading_widget.dart'; import 'package:flutter/material.dart'; class TrailingWidget extends StatefulWidget { final ValueNotifier executionStateNotifier; final IconData? trailingIcon; final Color? trailingIconColor; final Widget? trailingWidget; final bool trailingIconIsMuted; final double trailingExtraMargin; final bool showExecutionStates; const TrailingWidget({ Key? key, required this.executionStateNotifier, this.trailingIcon, this.trailingIconColor, this.trailingWidget, required this.trailingIconIsMuted, required this.trailingExtraMargin, required this.showExecutionStates, }) : super(key: key); @override State createState() => _TrailingWidgetState(); } class _TrailingWidgetState extends State { Widget? trailingWidget; @override void initState() { widget.showExecutionStates ? widget.executionStateNotifier.addListener(_executionStateListener) : null; super.initState(); } @override void dispose() { widget.executionStateNotifier.removeListener(_executionStateListener); super.dispose(); } @override Widget build(BuildContext context) { if (trailingWidget == null || !widget.showExecutionStates) { _setTrailingIcon(); } return AnimatedSwitcher( duration: const Duration(milliseconds: 175), switchInCurve: Curves.easeInExpo, switchOutCurve: Curves.easeOutExpo, child: trailingWidget, ); } void _executionStateListener() { final colorScheme = getEnteColorScheme(context); setState(() { if (widget.executionStateNotifier.value == ExecutionState.idle) { _setTrailingIcon(); } else if (widget.executionStateNotifier.value == ExecutionState.inProgress) { trailingWidget = EnteLoadingWidget( color: colorScheme.strokeMuted, ); } else if (widget.executionStateNotifier.value == ExecutionState.successful) { trailingWidget = Icon( Icons.check_outlined, size: 22, color: colorScheme.primary500, ); } else { trailingWidget = const SizedBox.shrink(); } }); } void _setTrailingIcon() { if (widget.trailingIcon != null) { trailingWidget = Padding( padding: EdgeInsets.only( right: widget.trailingExtraMargin, ), child: Icon( widget.trailingIcon, color: widget.trailingIconIsMuted ? getEnteColorScheme(context).strokeMuted : widget.trailingIconColor, ), ); } else { trailingWidget = widget.trailingWidget ?? const SizedBox.shrink(); } } } class ExpansionTrailingIcon extends StatelessWidget { final bool isExpanded; final IconData? trailingIcon; final Color? trailingIconColor; const ExpansionTrailingIcon({ Key? key, required this.isExpanded, this.trailingIcon, this.trailingIconColor, }) : super(key: key); @override Widget build(BuildContext context) { return AnimatedOpacity( duration: const Duration(milliseconds: 100), curve: Curves.easeInOut, opacity: isExpanded ? 0 : 1, child: AnimatedSwitcher( transitionBuilder: (child, animation) { return ScaleTransition(scale: animation, child: child); }, duration: const Duration(milliseconds: 200), switchInCurve: Curves.easeOut, child: isExpanded ? const SizedBox.shrink() : Icon( trailingIcon, color: trailingIconColor, ), ), ); } } class LeadingWidget extends StatelessWidget { final IconData? leadingIcon; final Color? leadingIconColor; final Widget? leadingIconWidget; // leadIconSize deafult value is 20. final double leadingIconSize; const LeadingWidget({ Key? key, required this.leadingIconSize, this.leadingIcon, this.leadingIconColor, this.leadingIconWidget, }) : super(key: key); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only(right: 10), child: SizedBox( height: leadingIconSize, width: leadingIconSize, child: leadingIcon == null ? (leadingIconWidget != null ? FittedBox( fit: BoxFit.contain, child: leadingIconWidget, ) : const SizedBox.shrink()) : FittedBox( fit: BoxFit.contain, child: Icon( leadingIcon, color: leadingIconColor ?? getEnteColorScheme(context).strokeBase, ), ), ), ); } }