clean: remove pre-filled search bar clutter and unify direct d10 dice input on home screen

This commit is contained in:
Alma Alexyz
2026-08-31 14:34:49 +05:00
parent e4bd7475cf
commit 66ba3c5247
+41 -186
View File
@@ -17,72 +17,21 @@ class HomeSearchScreen extends StatefulWidget {
class _HomeSearchScreenState extends State<HomeSearchScreen> { class _HomeSearchScreenState extends State<HomeSearchScreen> {
final _repo = WordsRepository(); final _repo = WordsRepository();
final _searchController = TextEditingController();
final _questionController = TextEditingController(); final _questionController = TextEditingController();
final _focusNode = FocusNode();
final _diceRollerKey = GlobalKey<D10DiceRollerWidgetState>(); final _diceRollerKey = GlobalKey<D10DiceRollerWidgetState>();
WordItem? _currentWord; WordItem? _currentWord;
String? _activeHistoryId; String? _activeHistoryId;
List<WordItem> _searchResults = [];
bool _showKeypad = true; bool _showKeypad = true;
String _currentDigits = ''; // Tracks 0..3 digits input for d10 dice cleromancy String _currentDigits = ''; // Tracks 0..3 digits input: e.g. "", "7", "74", "742"
@override
void initState() {
super.initState();
_searchController.addListener(_onSearchChanged);
}
@override @override
void dispose() { void dispose() {
_searchController.removeListener(_onSearchChanged);
_searchController.dispose();
_questionController.dispose(); _questionController.dispose();
_focusNode.dispose();
super.dispose(); super.dispose();
} }
void _onSearchChanged() { Future<void> _setWord(WordItem word, {bool recordHistory = true}) async {
final query = _searchController.text.trim();
if (query.isEmpty) {
setState(() {
_searchResults = [];
_currentDigits = '';
});
return;
}
final isNumeric = int.tryParse(query) != null;
if (isNumeric) {
setState(() {
_currentDigits = query;
});
// In Cleromancy, exactly 3 digits (d10x3) are required to resolve a number 000..999
if (query.length >= 3) {
final num = int.tryParse(query);
if (num != null && num <= 999) {
final word = _repo.getWordBySessionNumber(num);
if (word != null) {
_setWord(word, recordHistory: true);
}
}
}
} else {
// Text search by word
final results = _repo.search(query);
setState(() {
_searchResults = results;
_currentDigits = '';
if (results.isNotEmpty) {
_setWord(results.first, recordHistory: true);
}
});
}
}
Future<void> _setWord(WordItem word, {bool updateText = false, bool recordHistory = true}) async {
final question = _questionController.text.trim(); final question = _questionController.text.trim();
String? newHistId; String? newHistId;
@@ -105,71 +54,78 @@ class _HomeSearchScreenState extends State<HomeSearchScreen> {
_currentWord = word; _currentWord = word;
_activeHistoryId = newHistId ?? _activeHistoryId; _activeHistoryId = newHistId ?? _activeHistoryId;
_currentDigits = word.formattedSessionNumber; _currentDigits = word.formattedSessionNumber;
if (updateText) {
_searchController.text = word.formattedSessionNumber;
}
}); });
HapticFeedback.selectionClick(); HapticFeedback.selectionClick();
} }
void _onKeypadTap(String val) { void _onKeypadTap(String val) {
HapticFeedback.lightImpact(); HapticFeedback.lightImpact();
String currentText = _searchController.text;
if (val == 'C') { if (val == 'C') {
_searchController.clear(); setState(() {
setState(() => _currentDigits = ''); _currentDigits = '';
});
return; return;
} }
if (val == '⌫') { if (val == '⌫') {
if (currentText.isNotEmpty) { if (_currentDigits.isNotEmpty) {
_searchController.text = currentText.substring(0, currentText.length - 1); setState(() {
_searchController.selection = TextSelection.fromPosition( _currentDigits = _currentDigits.substring(0, _currentDigits.length - 1);
TextPosition(offset: _searchController.text.length), });
);
} }
return; return;
} }
// In Cleromancy, append digit up to 3 digits // Append digit up to 3 digits (1st d10, 2nd d10, 3rd d10)
if (currentText.length >= 3) { String newDigits;
_searchController.text = val; if (_currentDigits.length >= 3) {
newDigits = val;
} else { } else {
_searchController.text = currentText + val; newDigits = _currentDigits + val;
} }
_searchController.selection = TextSelection.fromPosition( setState(() {
TextPosition(offset: _searchController.text.length), _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) { void _onVirtualRollCompleted(int finalNumber) {
final word = _repo.getWordBySessionNumber(finalNumber); final word = _repo.getWordBySessionNumber(finalNumber);
if (word != null) { if (word != null) {
_setWord(word, updateText: true, recordHistory: true); _setWord(word, recordHistory: true);
} }
} }
void _resetToWelcome() { void _resetToWelcome() {
setState(() { setState(() {
_currentWord = null; _currentWord = null;
_searchController.clear();
_searchResults = [];
_currentDigits = ''; _currentDigits = '';
_activeHistoryId = null;
}); });
} }
void _goToNext() { void _goToNext() {
if (_currentWord == null) return; if (_currentWord == null) return;
final next = _repo.getNextWord(_currentWord!.sessionNumber); final next = _repo.getNextWord(_currentWord!.sessionNumber);
_setWord(next, updateText: true, recordHistory: false); _setWord(next, recordHistory: false);
} }
void _goToPrevious() { void _goToPrevious() {
if (_currentWord == null) return; if (_currentWord == null) return;
final prev = _repo.getPreviousWord(_currentWord!.sessionNumber); final prev = _repo.getPreviousWord(_currentWord!.sessionNumber);
_setWord(prev, updateText: true, recordHistory: false); _setWord(prev, recordHistory: false);
} }
void _reshuffleSession() { void _reshuffleSession() {
@@ -289,7 +245,7 @@ class _HomeSearchScreenState extends State<HomeSearchScreen> {
const SizedBox(height: 12), const SizedBox(height: 12),
// D10 Dice Roller Slots Widget // D10 Dice Roller Slots Widget (Clean & without prefilled junk)
D10DiceRollerWidget( D10DiceRollerWidget(
key: _diceRollerKey, key: _diceRollerKey,
digits: _currentDigits, digits: _currentDigits,
@@ -298,109 +254,6 @@ class _HomeSearchScreenState extends State<HomeSearchScreen> {
onRollCompleted: _onVirtualRollCompleted, onRollCompleted: _onVirtualRollCompleted,
), ),
const SizedBox(height: 10),
// Search & Direct Number Input Bar
TextField(
controller: _searchController,
focusNode: _focusNode,
keyboardType: TextInputType.text,
decoration: InputDecoration(
labelText: 'Введите число (#000..#999) или слово',
hintText: 'Например: 777 или огонь',
prefixIcon: const Padding(
padding: EdgeInsets.symmetric(horizontal: 12),
child: GradientStar(size: 18),
),
suffixIcon: _searchController.text.isNotEmpty
? IconButton(
icon: const Icon(Icons.clear_rounded),
onPressed: () {
_searchController.clear();
_focusNode.unfocus();
setState(() => _currentDigits = '');
},
)
: null,
filled: true,
fillColor: isDark
? const Color(0xFF1B1D30)
: theme.colorScheme.surfaceContainerHighest.withValues(alpha: 0.4),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(18),
borderSide: BorderSide(
color: const Color(0xFFFFD54F).withValues(alpha: 0.3),
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(18),
borderSide: BorderSide(
color: const Color(0xFFFFD54F).withValues(alpha: 0.25),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(18),
borderSide: const BorderSide(
color: Color(0xFFFFD54F),
width: 1.8,
),
),
),
),
// Search matches dropdown list (for text search)
if (_searchResults.length > 1 && _searchController.text.isNotEmpty && int.tryParse(_searchController.text) == null) ...[
const SizedBox(height: 8),
Container(
constraints: const BoxConstraints(maxHeight: 140),
decoration: BoxDecoration(
color: isDark ? const Color(0xFF161828) : theme.colorScheme.surface,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: const Color(0xFFFFD54F).withValues(alpha: 0.3),
),
),
child: ListView.separated(
shrinkWrap: true,
padding: const EdgeInsets.symmetric(vertical: 4),
itemCount: _searchResults.length > 10 ? 10 : _searchResults.length,
separatorBuilder: (_, _) => const Divider(height: 1),
itemBuilder: (context, idx) {
final item = _searchResults[idx];
final isSelected = item.sessionNumber == _currentWord?.sessionNumber;
return ListTile(
dense: true,
selected: isSelected,
leading: GradientStarBadge(
text: '#${item.formattedSessionNumber}',
size: 14,
),
title: Text(
item.word.toUpperCase(),
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(
item.description,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 11),
),
trailing: Text(
item.partOfSpeech,
style: TextStyle(
fontSize: 11,
color: theme.colorScheme.outline,
),
),
onTap: () {
_setWord(item, updateText: true, recordHistory: true);
},
);
},
),
),
],
const SizedBox(height: 14), const SizedBox(height: 14),
// Welcome Idle State OR Active Word Card // Welcome Idle State OR Active Word Card
@@ -439,7 +292,7 @@ class _HomeSearchScreenState extends State<HomeSearchScreen> {
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
'Сформулируйте вопрос, затем совершите 3 броска кубика d10 кнопкой выше или введите 3 выпавших числа (Сотни, Десятки, Единицы).', 'Сформулируйте вопрос, затем совершите 3 броска кубика d10 кнопкой выше или наберите 3 выпавших грани на панели.',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
@@ -501,7 +354,7 @@ class _HomeSearchScreenState extends State<HomeSearchScreen> {
const SizedBox(height: 14), const SizedBox(height: 14),
// Interactive Numeric Keypad // Interactive Numeric Keypad (Directly maps to 3 d10 dice slots)
if (_showKeypad) ...[ if (_showKeypad) ...[
Container( Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
@@ -524,7 +377,7 @@ class _HomeSearchScreenState extends State<HomeSearchScreen> {
Icon(Icons.casino_outlined, size: 15, color: Color(0xFFFFD54F)), Icon(Icons.casino_outlined, size: 15, color: Color(0xFFFFD54F)),
SizedBox(width: 6), SizedBox(width: 6),
Text( Text(
'Ввод граней d10 (3 цифры):', 'Ввод граней d10:',
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
@@ -534,9 +387,11 @@ class _HomeSearchScreenState extends State<HomeSearchScreen> {
], ],
), ),
Text( Text(
_currentDigits.length == 3 _currentDigits.isEmpty
? 'Код: #$_currentDigits' ? 'Ожидание 3 цифр'
: 'Введено: ${_currentDigits.length} из 3', : (_currentDigits.length == 3
? 'Код: #$_currentDigits'
: 'Введено: ${_currentDigits.length} из 3'),
style: TextStyle( style: TextStyle(
fontSize: 11, fontSize: 11,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,