mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-11-13 09:04:09 +01:00
* Fix some colors * fix(theme-updates): Modify condition for theme border style display when hasUnderlineBorder is true * fix(themes-updates): Adjust switch colors and modify payjoin hint text * fix: Crash report relating to success image display on successful exchange * fix(theme-updates): Adjust animated text max width to prevent overflows * fix(theme-updates): Adjust fee picker styling * fix(themes-updates): Remove text overflow on welcome screen animation * fix(themes-update): Fix the cursor to the baseline for the text --------- Co-authored-by: tuxsudo <tuxsudo@tux.pizza>
170 lines
4.0 KiB
Dart
170 lines
4.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
class AnimatedTypingText extends StatefulWidget {
|
|
final List<String> words;
|
|
final TextStyle? style;
|
|
final Duration typingSpeed;
|
|
final Duration pauseDuration;
|
|
final Color cursorColor;
|
|
final double cursorWidth;
|
|
final double cursorHeight;
|
|
final Duration cursorBlinkDuration;
|
|
|
|
const AnimatedTypingText({
|
|
Key? key,
|
|
required this.words,
|
|
this.style,
|
|
this.typingSpeed = const Duration(milliseconds: 100),
|
|
this.pauseDuration = const Duration(milliseconds: 3000),
|
|
this.cursorColor = Colors.black,
|
|
this.cursorWidth = 2.0,
|
|
this.cursorHeight = 20.0,
|
|
this.cursorBlinkDuration = const Duration(milliseconds: 500),
|
|
}) : super(key: key);
|
|
|
|
@override
|
|
State<AnimatedTypingText> createState() => _AnimatedTypingTextState();
|
|
}
|
|
|
|
class _AnimatedTypingTextState extends State<AnimatedTypingText> with TickerProviderStateMixin {
|
|
late AnimationController _controller;
|
|
late AnimationController _cursorController;
|
|
late Animation<double> _cursorAnimation;
|
|
|
|
String _displayText = '';
|
|
|
|
int _currentWordIndex = 0;
|
|
int _currentCharIndex = 0;
|
|
|
|
bool _isTyping = true;
|
|
bool _isPaused = false;
|
|
bool _isWaitingForPause = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_controller = AnimationController(
|
|
vsync: this,
|
|
duration: widget.typingSpeed,
|
|
);
|
|
|
|
_cursorController = AnimationController(
|
|
vsync: this,
|
|
duration: widget.cursorBlinkDuration,
|
|
)..repeat(reverse: true);
|
|
|
|
_cursorAnimation = Tween<double>(begin: 1.0, end: 0.0).animate(
|
|
CurvedAnimation(
|
|
parent: _cursorController,
|
|
curve: Curves.easeInOut,
|
|
),
|
|
);
|
|
|
|
_controller.addStatusListener((status) {
|
|
if (status == AnimationStatus.completed) {
|
|
_updateText();
|
|
if (!_isWaitingForPause) {
|
|
_controller.reset();
|
|
_controller.forward();
|
|
}
|
|
}
|
|
});
|
|
|
|
_controller.forward();
|
|
}
|
|
|
|
void _updateText() {
|
|
if (_isWaitingForPause) return;
|
|
|
|
if (_isPaused) {
|
|
if (_isTyping) {
|
|
_isTyping = false;
|
|
_isWaitingForPause = true;
|
|
Future.delayed(widget.pauseDuration, () {
|
|
if (mounted) {
|
|
setState(() {
|
|
_isPaused = false;
|
|
_isWaitingForPause = false;
|
|
_controller.reset();
|
|
_controller.forward();
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
_deletePrevChar();
|
|
}
|
|
} else {
|
|
if (_isTyping) {
|
|
_typeNextChar();
|
|
} else {
|
|
_deletePrevChar();
|
|
}
|
|
}
|
|
|
|
setState(() {
|
|
_displayText = widget.words[_currentWordIndex].substring(0, _currentCharIndex);
|
|
});
|
|
}
|
|
|
|
void _typeNextChar() {
|
|
if (_currentCharIndex < widget.words[_currentWordIndex].length) {
|
|
_currentCharIndex++;
|
|
} else {
|
|
_isPaused = true;
|
|
}
|
|
}
|
|
|
|
void _deletePrevChar() {
|
|
if (_currentCharIndex > 0) {
|
|
_currentCharIndex--;
|
|
} else {
|
|
_currentWordIndex = (_currentWordIndex + 1) % widget.words.length;
|
|
_currentCharIndex = 0;
|
|
_isTyping = true;
|
|
_isPaused = false;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_controller.dispose();
|
|
_cursorController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Stack(
|
|
clipBehavior: Clip.none,
|
|
children: [
|
|
// Fixed cursor position
|
|
Positioned(
|
|
right: 0,
|
|
bottom: 0,
|
|
child: AnimatedBuilder(
|
|
animation: _cursorAnimation,
|
|
builder: (context, child) {
|
|
return Opacity(
|
|
opacity: _cursorAnimation.value,
|
|
child: Container(
|
|
width: widget.cursorWidth,
|
|
height: widget.cursorHeight,
|
|
color: widget.cursorColor,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
// Animated text
|
|
Align(
|
|
alignment: Alignment.centerRight,
|
|
child: Text(
|
|
_displayText + ' ',
|
|
style: widget.style,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|