feat: wordle animation on guess
This commit is contained in:
parent
2b64d4ad50
commit
251bb79f0a
@ -13,18 +13,26 @@ class _WordlePageState extends State<WordlePage> with TickerProviderStateMixin {
|
|||||||
static const int MAX_ATTEMPTS = 6;
|
static const int MAX_ATTEMPTS = 6;
|
||||||
static const int WORD_LENGTH = 5;
|
static const int WORD_LENGTH = 5;
|
||||||
|
|
||||||
List<List<String>> grid = List.generate(MAX_ATTEMPTS, (_) => List.filled(WORD_LENGTH, ''));
|
List<List<String>> grid = List.generate(
|
||||||
List<List<LetterState>> gridStates = List.generate(MAX_ATTEMPTS, (_) => List.filled(WORD_LENGTH, LetterState.empty));
|
MAX_ATTEMPTS,
|
||||||
|
(_) => List.filled(WORD_LENGTH, ''),
|
||||||
|
);
|
||||||
|
List<List<LetterState>> gridStates = List.generate(
|
||||||
|
MAX_ATTEMPTS,
|
||||||
|
(_) => List.filled(WORD_LENGTH, LetterState.empty),
|
||||||
|
);
|
||||||
Map<String, LetterState> keyboardStates = {};
|
Map<String, LetterState> keyboardStates = {};
|
||||||
|
|
||||||
int currentRow = 0;
|
int currentRow = 0;
|
||||||
int currentCol = 0;
|
int currentCol = 0;
|
||||||
|
int animatingRow = -1;
|
||||||
bool gameWon = false;
|
bool gameWon = false;
|
||||||
bool gameLost = false;
|
bool gameLost = false;
|
||||||
|
|
||||||
late List<AnimationController> _flipControllers;
|
late AnimationController _revealController;
|
||||||
late List<Animation<double>> _flipAnimations;
|
late List<Animation<double>> _tileAnimations;
|
||||||
late FocusNode _focusNode;
|
late FocusNode _focusNode;
|
||||||
|
Set<LogicalKeyboardKey> _pressedKeys = <LogicalKeyboardKey>{};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -32,7 +40,7 @@ class _WordlePageState extends State<WordlePage> with TickerProviderStateMixin {
|
|||||||
_initializeAnimations();
|
_initializeAnimations();
|
||||||
_initializeKeyboardStates();
|
_initializeKeyboardStates();
|
||||||
_focusNode = FocusNode();
|
_focusNode = FocusNode();
|
||||||
|
|
||||||
// Request focus for keyboard input on web/desktop
|
// Request focus for keyboard input on web/desktop
|
||||||
if (kIsWeb || (!kIsWeb && !Platform.isIOS && !Platform.isAndroid)) {
|
if (kIsWeb || (!kIsWeb && !Platform.isIOS && !Platform.isAndroid)) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
@ -42,18 +50,24 @@ class _WordlePageState extends State<WordlePage> with TickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _initializeAnimations() {
|
void _initializeAnimations() {
|
||||||
_flipControllers = List.generate(
|
_revealController = AnimationController(
|
||||||
|
duration: Duration(milliseconds: 1500),
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
|
||||||
|
_tileAnimations = List.generate(
|
||||||
WORD_LENGTH,
|
WORD_LENGTH,
|
||||||
(index) => AnimationController(
|
(index) => Tween<double>(begin: 0.0, end: 1.0).animate(
|
||||||
duration: Duration(milliseconds: 600),
|
CurvedAnimation(
|
||||||
vsync: this,
|
parent: _revealController,
|
||||||
|
curve: Interval(
|
||||||
|
index * 0.1,
|
||||||
|
(index * 0.1) + 0.5,
|
||||||
|
curve: Curves.elasticOut,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
_flipAnimations = _flipControllers.map((controller) =>
|
|
||||||
Tween<double>(begin: 0.0, end: 1.0).animate(
|
|
||||||
CurvedAnimation(parent: controller, curve: Curves.easeInOut)
|
|
||||||
)
|
|
||||||
).toList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initializeKeyboardStates() {
|
void _initializeKeyboardStates() {
|
||||||
@ -64,15 +78,16 @@ class _WordlePageState extends State<WordlePage> with TickerProviderStateMixin {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
for (var controller in _flipControllers) {
|
_revealController.dispose();
|
||||||
controller.dispose();
|
|
||||||
}
|
|
||||||
_focusNode.dispose();
|
_focusNode.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _addLetter(String letter) {
|
void _addLetter(String letter) {
|
||||||
if (currentCol < WORD_LENGTH && currentRow < MAX_ATTEMPTS && !gameWon && !gameLost) {
|
if (currentCol < WORD_LENGTH &&
|
||||||
|
currentRow < MAX_ATTEMPTS &&
|
||||||
|
!gameWon &&
|
||||||
|
!gameLost) {
|
||||||
setState(() {
|
setState(() {
|
||||||
grid[currentRow][currentCol] = letter;
|
grid[currentRow][currentCol] = letter;
|
||||||
currentCol++;
|
currentCol++;
|
||||||
@ -94,26 +109,23 @@ class _WordlePageState extends State<WordlePage> with TickerProviderStateMixin {
|
|||||||
Future<void> _submitWord() async {
|
Future<void> _submitWord() async {
|
||||||
if (currentCol == WORD_LENGTH && !gameWon && !gameLost) {
|
if (currentCol == WORD_LENGTH && !gameWon && !gameLost) {
|
||||||
String guess = grid[currentRow].join('');
|
String guess = grid[currentRow].join('');
|
||||||
|
|
||||||
// Animate the flip
|
|
||||||
for (int i = 0; i < WORD_LENGTH; i++) {
|
|
||||||
await Future.delayed(Duration(milliseconds: 100));
|
|
||||||
_flipControllers[i].forward();
|
|
||||||
}
|
|
||||||
|
|
||||||
await Future.delayed(Duration(milliseconds: 300));
|
|
||||||
|
|
||||||
List<LetterState> newStates = _evaluateGuess(guess);
|
List<LetterState> newStates = _evaluateGuess(guess);
|
||||||
|
|
||||||
|
// Set the states and start animating the current row
|
||||||
setState(() {
|
setState(() {
|
||||||
gridStates[currentRow] = newStates;
|
gridStates[currentRow] = newStates;
|
||||||
_updateKeyboardStates(guess, newStates);
|
_updateKeyboardStates(guess, newStates);
|
||||||
|
animatingRow = currentRow;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Reset animations
|
// Start the reveal animation
|
||||||
for (var controller in _flipControllers) {
|
_revealController.reset();
|
||||||
controller.reset();
|
await _revealController.forward();
|
||||||
}
|
|
||||||
|
// Clear the animating row and move to next row
|
||||||
|
setState(() {
|
||||||
|
animatingRow = -1;
|
||||||
|
});
|
||||||
|
|
||||||
if (guess == TARGET_WORD) {
|
if (guess == TARGET_WORD) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -172,7 +184,8 @@ class _WordlePageState extends State<WordlePage> with TickerProviderStateMixin {
|
|||||||
LetterState newState = states[i];
|
LetterState newState = states[i];
|
||||||
|
|
||||||
if (currentState == LetterState.correct) continue;
|
if (currentState == LetterState.correct) continue;
|
||||||
if (currentState == LetterState.present && newState == LetterState.absent) continue;
|
if (currentState == LetterState.present && newState == LetterState.absent)
|
||||||
|
continue;
|
||||||
|
|
||||||
keyboardStates[letter] = newState;
|
keyboardStates[letter] = newState;
|
||||||
}
|
}
|
||||||
@ -185,14 +198,19 @@ class _WordlePageState extends State<WordlePage> with TickerProviderStateMixin {
|
|||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text(won ? 'Congratulations!' : 'Game Over'),
|
title: Text(won ? 'Congratulations!' : 'Game Over'),
|
||||||
content: Text(won ? 'You solved today\'s puzzle!' : 'The word was: $TARGET_WORD'),
|
content: Text(
|
||||||
|
won ? 'You solved today\'s puzzle!' : 'The word was: $TARGET_WORD',
|
||||||
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
_resetGame();
|
_resetGame();
|
||||||
},
|
},
|
||||||
child: Text('Play Again', style: TextStyle(color: Color(0xFF6A4C93))),
|
child: Text(
|
||||||
|
'Play Again',
|
||||||
|
style: TextStyle(color: Color(0xFF6A4C93)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -203,9 +221,13 @@ class _WordlePageState extends State<WordlePage> with TickerProviderStateMixin {
|
|||||||
void _resetGame() {
|
void _resetGame() {
|
||||||
setState(() {
|
setState(() {
|
||||||
grid = List.generate(MAX_ATTEMPTS, (_) => List.filled(WORD_LENGTH, ''));
|
grid = List.generate(MAX_ATTEMPTS, (_) => List.filled(WORD_LENGTH, ''));
|
||||||
gridStates = List.generate(MAX_ATTEMPTS, (_) => List.filled(WORD_LENGTH, LetterState.empty));
|
gridStates = List.generate(
|
||||||
|
MAX_ATTEMPTS,
|
||||||
|
(_) => List.filled(WORD_LENGTH, LetterState.empty),
|
||||||
|
);
|
||||||
currentRow = 0;
|
currentRow = 0;
|
||||||
currentCol = 0;
|
currentCol = 0;
|
||||||
|
animatingRow = -1;
|
||||||
gameWon = false;
|
gameWon = false;
|
||||||
gameLost = false;
|
gameLost = false;
|
||||||
_initializeKeyboardStates();
|
_initializeKeyboardStates();
|
||||||
@ -225,166 +247,176 @@ class _WordlePageState extends State<WordlePage> with TickerProviderStateMixin {
|
|||||||
_focusNode.requestFocus();
|
_focusNode.requestFocus();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: KeyboardListener(
|
child: RawKeyboardListener(
|
||||||
focusNode: _focusNode,
|
focusNode: _focusNode,
|
||||||
onKeyEvent: _handleKeyEvent,
|
onKey: _handleRawKeyEvent,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
title: Row(
|
title: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'وصال',
|
'وصال',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 28,
|
fontSize: 28,
|
||||||
fontWeight: FontWeight.w200,
|
fontWeight: FontWeight.w200,
|
||||||
fontFamily: 'Blaka',
|
fontFamily: 'Blaka',
|
||||||
color: Color(0xFF6A4C93),
|
color: Color(0xFF6A4C93),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'Daily Challenge',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
SizedBox(width: 8),
|
bottom: PreferredSize(
|
||||||
Text(
|
preferredSize: Size.fromHeight(1),
|
||||||
'Daily Challenge',
|
child: Container(height: 1, color: Colors.grey[200]),
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.grey[600],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
body: Column(
|
||||||
bottom: PreferredSize(
|
children: [
|
||||||
preferredSize: Size.fromHeight(1),
|
Expanded(
|
||||||
child: Container(height: 1, color: Colors.grey[200]),
|
child: Center(
|
||||||
),
|
child: Container(
|
||||||
),
|
constraints: BoxConstraints(maxWidth: 350),
|
||||||
body: Column(
|
child: Column(
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
Expanded(
|
children: [
|
||||||
child: Center(
|
// Game Grid
|
||||||
child: Container(
|
Container(
|
||||||
constraints: BoxConstraints(maxWidth: 350),
|
padding: EdgeInsets.all(16),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
children: List.generate(MAX_ATTEMPTS, (row) {
|
||||||
children: [
|
return Padding(
|
||||||
// Game Grid
|
padding: EdgeInsets.symmetric(vertical: 2),
|
||||||
Container(
|
child: Row(
|
||||||
padding: EdgeInsets.all(16),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
child: Column(
|
children: List.generate(WORD_LENGTH, (col) {
|
||||||
children: List.generate(MAX_ATTEMPTS, (row) {
|
bool shouldAnimate =
|
||||||
return Padding(
|
row == animatingRow &&
|
||||||
padding: EdgeInsets.symmetric(vertical: 2),
|
gridStates[row][col] !=
|
||||||
child: Row(
|
LetterState.empty;
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: List.generate(WORD_LENGTH, (col) {
|
return Container(
|
||||||
return Container(
|
margin: EdgeInsets.all(2),
|
||||||
margin: EdgeInsets.all(2),
|
width: 62,
|
||||||
width: 62,
|
height: 62,
|
||||||
height: 62,
|
child: shouldAnimate
|
||||||
child: AnimatedBuilder(
|
? AnimatedBuilder(
|
||||||
animation: _flipAnimations[col],
|
animation: _tileAnimations[col],
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
// Check if this tile should be animating
|
double scale =
|
||||||
bool isCurrentRowAndAnimating = row == currentRow && _flipAnimations[col].value > 0;
|
0.8 +
|
||||||
|
(0.2 *
|
||||||
// If not animating, show the final state
|
_tileAnimations[col]
|
||||||
if (!isCurrentRowAndAnimating) {
|
.value);
|
||||||
return Container(
|
return Transform.scale(
|
||||||
width: 62,
|
scale: scale,
|
||||||
height: 62,
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
width: 62,
|
||||||
border: Border.all(
|
height: 62,
|
||||||
color: _getBorderColor(row, col),
|
decoration: BoxDecoration(
|
||||||
width: 2,
|
border: Border.all(
|
||||||
),
|
color:
|
||||||
color: _getTileColor(row, col),
|
_getBorderColor(
|
||||||
),
|
row,
|
||||||
child: Center(
|
col,
|
||||||
child: Text(
|
),
|
||||||
grid[row][col],
|
width: 2,
|
||||||
style: TextStyle(
|
),
|
||||||
fontSize: 32,
|
color:
|
||||||
fontWeight: FontWeight.bold,
|
_getAnimatedTileColor(
|
||||||
color: _getTextColor(row, col),
|
row,
|
||||||
|
col,
|
||||||
|
_tileAnimations[col]
|
||||||
|
.value,
|
||||||
|
),
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(
|
||||||
|
4,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
grid[row][col],
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 32,
|
||||||
|
fontWeight:
|
||||||
|
FontWeight.bold,
|
||||||
|
color: _getAnimatedTextColor(
|
||||||
|
row,
|
||||||
|
col,
|
||||||
|
_tileAnimations[col]
|
||||||
|
.value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: Container(
|
||||||
|
width: 62,
|
||||||
|
height: 62,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: _getBorderColor(
|
||||||
|
row,
|
||||||
|
col,
|
||||||
|
),
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
|
color: _getTileColor(
|
||||||
|
row,
|
||||||
|
col,
|
||||||
|
),
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
),
|
child: Center(
|
||||||
),
|
child: Text(
|
||||||
);
|
grid[row][col],
|
||||||
}
|
style: TextStyle(
|
||||||
|
fontSize: 32,
|
||||||
// Animation is running - create flip effect
|
fontWeight:
|
||||||
double progress = _flipAnimations[col].value;
|
FontWeight.bold,
|
||||||
double rotationX = progress * 3.14159;
|
color: _getTextColor(
|
||||||
|
row,
|
||||||
// Determine colors to show during animation
|
col,
|
||||||
bool showFinalColors = progress > 0.5;
|
),
|
||||||
Color bgColor = showFinalColors ? _getTileColor(row, col) : Colors.white;
|
),
|
||||||
Color textColor = showFinalColors ? _getTextColor(row, col) : Colors.black;
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
width: 62,
|
|
||||||
height: 62,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border.all(
|
|
||||||
color: _getBorderColor(row, col),
|
|
||||||
width: 2,
|
|
||||||
),
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
// Animated background that flips
|
|
||||||
Transform(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
transform: Matrix4.identity()
|
|
||||||
..setEntry(3, 2, 0.001)
|
|
||||||
..rotateX(rotationX),
|
|
||||||
child: Container(
|
|
||||||
width: 62,
|
|
||||||
height: 62,
|
|
||||||
color: bgColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Text that always stays upright and visible
|
|
||||||
Center(
|
|
||||||
child: Text(
|
|
||||||
grid[row][col],
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 32,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: textColor,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
);
|
||||||
),
|
}),
|
||||||
);
|
),
|
||||||
},
|
);
|
||||||
),
|
}),
|
||||||
);
|
),
|
||||||
}),
|
),
|
||||||
),
|
],
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
// Keyboard
|
||||||
|
Container(padding: EdgeInsets.all(8), child: _buildKeyboard()),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
// Keyboard
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.all(8),
|
|
||||||
child: _buildKeyboard(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -407,7 +439,7 @@ class _WordlePageState extends State<WordlePage> with TickerProviderStateMixin {
|
|||||||
children: row.map((key) {
|
children: row.map((key) {
|
||||||
bool isSpecial = key == 'ENTER' || key == '⌫';
|
bool isSpecial = key == 'ENTER' || key == '⌫';
|
||||||
double width = isSpecial ? 65 : 35;
|
double width = isSpecial ? 65 : 35;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
margin: EdgeInsets.symmetric(horizontal: 1.5),
|
margin: EdgeInsets.symmetric(horizontal: 1.5),
|
||||||
width: width,
|
width: width,
|
||||||
@ -449,23 +481,19 @@ class _WordlePageState extends State<WordlePage> with TickerProviderStateMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _handleKeyEvent(KeyEvent event) {
|
void _handleRawKeyEvent(RawKeyEvent event) {
|
||||||
if (event is KeyDownEvent) {
|
if (event is RawKeyDownEvent) {
|
||||||
if (event.logicalKey == LogicalKeyboardKey.enter) {
|
if (event.logicalKey == LogicalKeyboardKey.enter) {
|
||||||
_submitWord();
|
_submitWord();
|
||||||
return true;
|
|
||||||
} else if (event.logicalKey == LogicalKeyboardKey.backspace) {
|
} else if (event.logicalKey == LogicalKeyboardKey.backspace) {
|
||||||
_deleteLetter();
|
_deleteLetter();
|
||||||
return true;
|
} else if (event.character != null && event.character!.isNotEmpty) {
|
||||||
} else if (event.logicalKey.keyLabel.length == 1) {
|
String key = event.character!.toUpperCase();
|
||||||
String key = event.logicalKey.keyLabel.toUpperCase();
|
|
||||||
if (key.codeUnitAt(0) >= 65 && key.codeUnitAt(0) <= 90) {
|
if (key.codeUnitAt(0) >= 65 && key.codeUnitAt(0) <= 90) {
|
||||||
_addLetter(key);
|
_addLetter(key);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Color _getBorderColor(int row, int col) {
|
Color _getBorderColor(int row, int col) {
|
||||||
@ -482,7 +510,7 @@ class _WordlePageState extends State<WordlePage> with TickerProviderStateMixin {
|
|||||||
Color _getTileColor(int row, int col) {
|
Color _getTileColor(int row, int col) {
|
||||||
if (row >= MAX_ATTEMPTS || col >= WORD_LENGTH) return Colors.white;
|
if (row >= MAX_ATTEMPTS || col >= WORD_LENGTH) return Colors.white;
|
||||||
if (row >= currentRow && !gameWon) return Colors.white;
|
if (row >= currentRow && !gameWon) return Colors.white;
|
||||||
|
|
||||||
switch (gridStates[row][col]) {
|
switch (gridStates[row][col]) {
|
||||||
case LetterState.correct:
|
case LetterState.correct:
|
||||||
return Color(0xFF6AAE7C);
|
return Color(0xFF6AAE7C);
|
||||||
@ -498,14 +526,53 @@ class _WordlePageState extends State<WordlePage> with TickerProviderStateMixin {
|
|||||||
Color _getTextColor(int row, int col) {
|
Color _getTextColor(int row, int col) {
|
||||||
if (row >= MAX_ATTEMPTS || col >= WORD_LENGTH) return Colors.black;
|
if (row >= MAX_ATTEMPTS || col >= WORD_LENGTH) return Colors.black;
|
||||||
if (row >= currentRow && !gameWon) return Colors.black;
|
if (row >= currentRow && !gameWon) return Colors.black;
|
||||||
return gridStates[row][col] == LetterState.empty ? Colors.black : Colors.white;
|
return gridStates[row][col] == LetterState.empty
|
||||||
|
? Colors.black
|
||||||
|
: Colors.white;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color _getAnimatedTileColor(int row, int col, double animationValue) {
|
||||||
|
if (row >= MAX_ATTEMPTS || col >= WORD_LENGTH) return Colors.white;
|
||||||
|
if (row != animatingRow || gridStates[row][col] == LetterState.empty) {
|
||||||
|
return _getTileColor(row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show color based on animation progress
|
||||||
|
Color targetColor;
|
||||||
|
switch (gridStates[row][col]) {
|
||||||
|
case LetterState.correct:
|
||||||
|
targetColor = Color(0xFF6AAE7C);
|
||||||
|
break;
|
||||||
|
case LetterState.present:
|
||||||
|
targetColor = Color(0xFFC9B037);
|
||||||
|
break;
|
||||||
|
case LetterState.absent:
|
||||||
|
targetColor = Color(0xFF787C7E);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
targetColor = Colors.white;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Color.lerp(Colors.white, targetColor, animationValue) ??
|
||||||
|
Colors.white;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color _getAnimatedTextColor(int row, int col, double animationValue) {
|
||||||
|
if (row >= MAX_ATTEMPTS || col >= WORD_LENGTH) return Colors.black;
|
||||||
|
if (row != animatingRow || gridStates[row][col] == LetterState.empty) {
|
||||||
|
return _getTextColor(row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text color changes with animation
|
||||||
|
return Color.lerp(Colors.black, Colors.white, animationValue) ??
|
||||||
|
Colors.black;
|
||||||
}
|
}
|
||||||
|
|
||||||
Color _getKeyColor(String key) {
|
Color _getKeyColor(String key) {
|
||||||
if (key == 'ENTER' || key == '⌫') {
|
if (key == 'ENTER' || key == '⌫') {
|
||||||
return Colors.grey[300]!;
|
return Colors.grey[300]!;
|
||||||
}
|
}
|
||||||
|
|
||||||
LetterState state = keyboardStates[key] ?? LetterState.empty;
|
LetterState state = keyboardStates[key] ?? LetterState.empty;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case LetterState.correct:
|
case LetterState.correct:
|
||||||
@ -523,15 +590,10 @@ class _WordlePageState extends State<WordlePage> with TickerProviderStateMixin {
|
|||||||
if (key == 'ENTER' || key == '⌫') {
|
if (key == 'ENTER' || key == '⌫') {
|
||||||
return Colors.black;
|
return Colors.black;
|
||||||
}
|
}
|
||||||
|
|
||||||
LetterState state = keyboardStates[key] ?? LetterState.empty;
|
LetterState state = keyboardStates[key] ?? LetterState.empty;
|
||||||
return state == LetterState.empty ? Colors.black : Colors.white;
|
return state == LetterState.empty ? Colors.black : Colors.white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum LetterState {
|
enum LetterState { empty, absent, present, correct }
|
||||||
empty,
|
|
||||||
absent,
|
|
||||||
present,
|
|
||||||
correct,
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user