fluffychat/lib/pangea/choreographer/igc/start_igc_button.dart

191 lines
5.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/choreographer/assistance_state_enum.dart';
import 'package:fluffychat/pangea/choreographer/choreographer.dart';
import 'package:fluffychat/pangea/choreographer/choreographer_state_extension.dart';
import 'package:fluffychat/pangea/learning_settings/settings_learning.dart';
class StartIGCButton extends StatefulWidget {
final VoidCallback onPressed;
final Choreographer choreographer;
final AssistanceStateEnum initialState;
final Color initialForegroundColor;
final Color initialBackgroundColor;
const StartIGCButton({
super.key,
required this.onPressed,
required this.choreographer,
required this.initialState,
required this.initialForegroundColor,
required this.initialBackgroundColor,
});
@override
State<StartIGCButton> createState() => _StartIGCButtonState();
}
class _StartIGCButtonState extends State<StartIGCButton>
with TickerProviderStateMixin {
AnimationController? _spinController;
late Animation<double> _rotation;
AnimationController? _colorController;
late Animation<Color?> _iconColor;
late Animation<Color?> _backgroundColor;
AssistanceStateEnum? _prevState;
bool _shouldStop = false;
@override
void initState() {
super.initState();
_spinController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
)..addStatusListener((status) {
if (status == AnimationStatus.completed) {
if (_shouldStop) {
_spinController?.stop();
_spinController?.value = 0;
} else {
_spinController?.forward(from: 0);
}
}
});
_rotation = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _spinController!,
curve: Curves.linear,
),
);
_colorController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
);
_prevState = widget.initialState;
_iconColor = AlwaysStoppedAnimation(widget.initialForegroundColor);
_backgroundColor = AlwaysStoppedAnimation(widget.initialBackgroundColor);
_colorController!.forward(from: 0.0);
widget.choreographer.addListener(_handleStateChange);
}
@override
void dispose() {
widget.choreographer.removeListener(_handleStateChange);
_spinController?.dispose();
_colorController?.dispose();
super.dispose();
}
void _handleStateChange() {
final prev = _prevState;
final current = widget.choreographer.assistanceState;
_prevState = current;
if (!mounted || prev == current) return;
final newIconColor = current.stateColor(context);
final newBgColor = current.backgroundColor(context);
final oldIconColor = _iconColor.value;
final oldBgColor = _backgroundColor.value;
// Create tweens from current → new colors
_iconColor = ColorTween(
begin: oldIconColor,
end: newIconColor,
).animate(_colorController!);
_backgroundColor = ColorTween(
begin: oldBgColor,
end: newBgColor,
).animate(_colorController!);
_colorController!.forward(from: 0.0);
if (current == AssistanceStateEnum.fetching) {
_shouldStop = false;
_spinController!.forward(from: 0.0);
} else if (prev == AssistanceStateEnum.fetching) {
_shouldStop = true;
}
}
@override
Widget build(BuildContext context) {
if (_colorController == null || _spinController == null) {
return const SizedBox.shrink();
}
return AnimatedBuilder(
animation: Listenable.merge([_colorController!, _spinController!]),
builder: (context, child) {
final enableFeedback =
widget.choreographer.assistanceState.allowsFeedback;
return Tooltip(
message: enableFeedback ? L10n.of(context).check : "",
child: Material(
elevation: enableFeedback ? 4.0 : 0.0,
shape: const CircleBorder(),
clipBehavior: Clip.antiAlias,
shadowColor: Theme.of(context).colorScheme.surface.withAlpha(128),
child: InkWell(
enableFeedback: enableFeedback,
customBorder: const CircleBorder(),
onTap: enableFeedback ? widget.onPressed : null,
onLongPress: enableFeedback
? () => showDialog(
context: context,
builder: (c) => const SettingsLearning(),
barrierDismissible: false,
)
: null,
child: Stack(
alignment: Alignment.center,
children: [
Container(
height: 40.0,
width: 40.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _backgroundColor.value,
),
),
AnimatedBuilder(
animation: _rotation,
builder: (context, child) {
return Transform.rotate(
angle: _rotation.value * 2 * 3.14159,
child: child,
);
},
child: Icon(
Icons.autorenew_rounded,
size: 36,
color: _iconColor.value,
),
),
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _backgroundColor.value,
),
),
Icon(
size: 16,
Icons.check,
color: _iconColor.value,
),
],
),
),
),
);
},
);
}
}