Files
999/lib/screens/home_search_screen.dart
T

478 lines
17 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../models/history_entry_model.dart';
import '../models/word_model.dart';
import '../services/words_repository.dart';
import '../widgets/cleromancy_dialog.dart';
import '../widgets/d10_dice_roller_widget.dart';
import '../widgets/gradient_star_badge.dart';
import '../widgets/word_card_widget.dart';
class HomeSearchScreen extends StatefulWidget {
const HomeSearchScreen({super.key});
@override
State<HomeSearchScreen> createState() => _HomeSearchScreenState();
}
class _HomeSearchScreenState extends State<HomeSearchScreen> {
final _repo = WordsRepository();
final _questionController = TextEditingController();
final _diceRollerKey = GlobalKey<D10DiceRollerWidgetState>();
WordItem? _currentWord;
String? _activeHistoryId;
bool _showKeypad = true;
String _currentDigits = ''; // Tracks 0..3 digits input: e.g. "", "7", "74", "742"
@override
void dispose() {
_questionController.dispose();
super.dispose();
}
Future<void> _setWord(WordItem word, {bool recordHistory = true}) async {
final question = _questionController.text.trim();
String? newHistId;
if (recordHistory) {
newHistId = DateTime.now().millisecondsSinceEpoch.toString();
final entry = HistoryEntry(
id: newHistId,
timestamp: DateTime.now(),
word: word.word,
sessionNumber: word.sessionNumber,
partOfSpeech: word.partOfSpeech,
symbolicMeaning: word.description,
readingType: 'Клеромантия (3× d10)',
question: question.isNotEmpty ? question : null,
);
await _repo.addHistoryEntry(entry);
}
setState(() {
_currentWord = word;
_activeHistoryId = newHistId ?? _activeHistoryId;
_currentDigits = word.formattedSessionNumber;
});
HapticFeedback.selectionClick();
}
void _onKeypadTap(String val) {
HapticFeedback.lightImpact();
if (val == 'C') {
setState(() {
_currentDigits = '';
});
return;
}
if (val == '⌫') {
if (_currentDigits.isNotEmpty) {
setState(() {
_currentDigits = _currentDigits.substring(0, _currentDigits.length - 1);
});
}
return;
}
// Append digit up to 3 digits (1st d10, 2nd d10, 3rd d10)
String newDigits;
if (_currentDigits.length >= 3) {
newDigits = val;
} else {
newDigits = _currentDigits + val;
}
setState(() {
_currentDigits = newDigits;
});
// Only resolve word when all 3 digits are provided!
if (newDigits.length == 3) {
final num = int.tryParse(newDigits);
if (num != null && num <= 999) {
final word = _repo.getWordBySessionNumber(num);
if (word != null) {
_setWord(word, recordHistory: true);
}
}
}
}
void _onVirtualRollCompleted(int finalNumber) {
final word = _repo.getWordBySessionNumber(finalNumber);
if (word != null) {
_setWord(word, recordHistory: true);
}
}
void _resetToWelcome() {
setState(() {
_currentWord = null;
_currentDigits = '';
_activeHistoryId = null;
});
}
void _goToNext() {
if (_currentWord == null) return;
final next = _repo.getNextWord(_currentWord!.sessionNumber);
_setWord(next, recordHistory: false);
}
void _goToPrevious() {
if (_currentWord == null) return;
final prev = _repo.getPreviousWord(_currentWord!.sessionNumber);
_setWord(prev, recordHistory: false);
}
void _reshuffleSession() {
_repo.reshuffleSessionNumbers();
_resetToWelcome();
HapticFeedback.heavyImpact();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const Row(
children: [
GradientStar(size: 20),
SizedBox(width: 10),
Expanded(
child: Text(
'Случайный порядок 1000 чисел перетасован заново!',
style: TextStyle(fontWeight: FontWeight.w600),
),
),
],
),
duration: const Duration(seconds: 2),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
);
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final isDark = theme.brightness == Brightness.dark;
return Scaffold(
appBar: AppBar(
title: const Row(
children: [
GradientStar(size: 24),
SizedBox(width: 10),
Text(
'Оракул 999',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w900,
letterSpacing: 0.5,
),
),
],
),
actions: [
IconButton(
tooltip: 'Инструкция: Клеромантия и кубики d10',
icon: const Icon(Icons.help_outline_rounded, color: Color(0xFFFFD54F)),
onPressed: () => CleromancyGuideDialog.show(context),
),
if (_currentWord != null)
IconButton(
tooltip: 'Новый вопрос / Сброс',
icon: const Icon(Icons.refresh_rounded),
onPressed: _resetToWelcome,
),
IconButton(
tooltip: 'Перетасовать матрицу (#000..#999)',
icon: const Icon(Icons.shuffle_rounded),
onPressed: _reshuffleSession,
),
IconButton(
tooltip: _showKeypad ? 'Скрыть панель' : 'Показать панель',
icon: Icon(_showKeypad ? Icons.dialpad_outlined : Icons.dialpad),
onPressed: () {
setState(() => _showKeypad = !_showKeypad);
},
),
],
),
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Question Input Field
TextField(
controller: _questionController,
decoration: InputDecoration(
labelText: 'Ваш вопрос или намерение (необязательно)',
hintText: 'Например: В чем корень текущей ситуации?',
prefixIcon: const Icon(Icons.help_outline_rounded, color: Color(0xFFFFD54F), size: 20),
suffixIcon: _questionController.text.isNotEmpty
? IconButton(
icon: const Icon(Icons.clear, size: 18),
onPressed: () => _questionController.clear(),
)
: null,
filled: true,
fillColor: isDark ? const Color(0xFF17192C) : theme.colorScheme.surfaceContainerHigh,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide(
color: const Color(0xFFFFD54F).withValues(alpha: 0.25),
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: BorderSide(
color: const Color(0xFFFFD54F).withValues(alpha: 0.2),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: const BorderSide(
color: Color(0xFFFFD54F),
width: 1.5,
),
),
),
),
const SizedBox(height: 12),
// D10 Dice Roller Slots Widget (Clean & without prefilled junk)
D10DiceRollerWidget(
key: _diceRollerKey,
digits: _currentDigits,
isRolling: false,
onRollPressed: () {},
onRollCompleted: _onVirtualRollCompleted,
),
const SizedBox(height: 14),
// Welcome Idle State OR Active Word Card
if (_currentWord == null) ...[
// Welcome Initiation Card
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
const Color(0xFF1F223B),
const Color(0xFF131526),
],
),
borderRadius: BorderRadius.circular(24),
border: Border.all(
color: const Color(0xFFFFD54F).withValues(alpha: 0.35),
width: 1.2,
),
),
child: Column(
children: [
const GradientStar(size: 38),
const SizedBox(height: 12),
const Text(
'СВЯЩЕННЫЙ ЖРЕБИЙ КЛЕРОМАНТИИ',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w900,
letterSpacing: 0.8,
color: Color(0xFFFFF9C4),
),
),
const SizedBox(height: 8),
Text(
'Сформулируйте вопрос, затем совершите 3 броска кубика d10 кнопкой выше или наберите 3 выпавших грани на панели.',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 13,
height: 1.4,
color: theme.colorScheme.onSurface.withValues(alpha: 0.85),
),
),
const SizedBox(height: 14),
OutlinedButton.icon(
onPressed: () => CleromancyGuideDialog.show(context),
icon: const Icon(Icons.menu_book_rounded, size: 16, color: Color(0xFFFFD54F)),
label: const Text(
'Инструкция по броскам d10',
style: TextStyle(
color: Color(0xFFFFD54F),
fontWeight: FontWeight.bold,
fontSize: 12.5,
),
),
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Color(0xFFFFD54F), width: 1),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
),
),
],
),
),
] else ...[
// Main Sacred Word Card Display
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
transitionBuilder: (child, animation) {
return FadeTransition(
opacity: animation,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0.0, 0.04),
end: Offset.zero,
).animate(animation),
child: child,
),
);
},
child: WordCardWidget(
key: ValueKey('${_currentWord!.id}_${_currentWord!.sessionNumber}'),
word: _currentWord!,
question: _questionController.text.trim(),
historyEntryId: _activeHistoryId,
onNext: _goToNext,
onPrevious: _goToPrevious,
onRandom: () {
_diceRollerKey.currentState?.triggerRollAnimation();
},
),
),
],
const SizedBox(height: 14),
// Interactive Numeric Keypad (Directly maps to 3 d10 dice slots)
if (_showKeypad) ...[
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isDark
? const Color(0xFF17192A)
: theme.colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: const Color(0xFFFFD54F).withValues(alpha: 0.25),
),
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Row(
children: [
Icon(Icons.casino_outlined, size: 15, color: Color(0xFFFFD54F)),
SizedBox(width: 6),
Text(
'Ввод граней d10:',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w700,
color: Color(0xFFFFD54F),
),
),
],
),
Text(
_currentDigits.isEmpty
? 'Ожидание 3 цифр'
: (_currentDigits.length == 3
? 'Код: #$_currentDigits'
: 'Введено: ${_currentDigits.length} из 3'),
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.bold,
color: _currentDigits.length == 3
? const Color(0xFFFFD54F)
: theme.colorScheme.outline,
),
),
],
),
const SizedBox(height: 10),
_buildKeypadRow(['1', '2', '3']),
const SizedBox(height: 8),
_buildKeypadRow(['4', '5', '6']),
const SizedBox(height: 8),
_buildKeypadRow(['7', '8', '9']),
const SizedBox(height: 8),
_buildKeypadRow(['C', '0', '⌫']),
],
),
),
const SizedBox(height: 16),
],
],
),
),
),
);
}
Widget _buildKeypadRow(List<String> keys) {
return Row(
children: keys.map((key) {
final isSpecial = key == 'C' || key == '⌫';
final theme = Theme.of(context);
final isDark = theme.brightness == Brightness.dark;
return Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Material(
color: isSpecial
? const Color(0xFF2A2D4A)
: isDark
? const Color(0xFF20233A)
: theme.colorScheme.surface,
borderRadius: BorderRadius.circular(14),
elevation: 1,
child: InkWell(
onTap: () => _onKeypadTap(key),
borderRadius: BorderRadius.circular(14),
child: Container(
height: 46,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: isSpecial
? const Color(0xFFFFD54F).withValues(alpha: 0.4)
: Colors.white10,
),
),
child: Text(
key,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: isSpecial
? const Color(0xFFFFF9C4)
: isDark
? Colors.white
: Colors.black87,
),
),
),
),
),
),
);
}).toList(),
);
}
}