664 lines
24 KiB
Dart
664 lines
24 KiB
Dart
import 'dart:math';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import '../models/divination_layout.dart';
|
|
import '../models/syncretic_session_model.dart';
|
|
import '../models/tradition_model.dart';
|
|
import '../models/word_model.dart';
|
|
import '../services/traditions_repository.dart';
|
|
import '../services/words_repository.dart';
|
|
import '../widgets/divination_instruction_modal.dart';
|
|
import 'lenormand_station_screen.dart';
|
|
|
|
class LenormandDivinationScreen extends StatefulWidget {
|
|
final String? initialQuestion;
|
|
|
|
const LenormandDivinationScreen({super.key, this.initialQuestion});
|
|
|
|
@override
|
|
State<LenormandDivinationScreen> createState() => _LenormandDivinationScreenState();
|
|
}
|
|
|
|
class _LenormandDivinationScreenState extends State<LenormandDivinationScreen> with SingleTickerProviderStateMixin {
|
|
late TabController _tabController;
|
|
final _questionController = TextEditingController();
|
|
final _directCardNumberController = TextEditingController();
|
|
final _repo = WordsRepository();
|
|
|
|
List<TraditionStation> _cards = [];
|
|
|
|
// Layouts: 2 cards (pair), 3 cards (line), 5 cards (cross)
|
|
DivinationLayout _selectedLayout = DivinationLayouts.lenormandLayouts[0];
|
|
int _currentStepIndex = 0;
|
|
|
|
// Dice simulation (d6 + d12)
|
|
int? _d6Value;
|
|
int? _d12Value;
|
|
bool _isRollingDice = false;
|
|
|
|
// Drawn results
|
|
List<({TraditionStation card, int cardNumber, String positionTitle})> _drawnResults = [];
|
|
bool _savedToSession = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_tabController = TabController(length: 2, vsync: this);
|
|
if (widget.initialQuestion != null) {
|
|
_questionController.text = widget.initialQuestion!;
|
|
}
|
|
_loadCards();
|
|
}
|
|
|
|
void _loadCards() {
|
|
final canon = TraditionsRepository().byId('lenormand');
|
|
if (canon != null) {
|
|
_cards = canon.stations;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_tabController.dispose();
|
|
_questionController.dispose();
|
|
_directCardNumberController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
int _calculateCardNumber(int d6, int d12) {
|
|
int base = 0;
|
|
if (d6 == 3 || d6 == 4) base = 12;
|
|
if (d6 == 5 || d6 == 6) base = 24;
|
|
return (base + d12).clamp(1, 36);
|
|
}
|
|
|
|
void _onLayoutChanged(DivinationLayout layout) {
|
|
setState(() {
|
|
_selectedLayout = layout;
|
|
_currentStepIndex = 0;
|
|
_drawnResults.clear();
|
|
_d6Value = null;
|
|
_d12Value = null;
|
|
_savedToSession = false;
|
|
});
|
|
}
|
|
|
|
Future<void> _rollDiceForStep() async {
|
|
if (_cards.isEmpty || _currentStepIndex >= _selectedLayout.count) return;
|
|
setState(() => _isRollingDice = true);
|
|
HapticFeedback.mediumImpact();
|
|
|
|
final rnd = Random();
|
|
for (int i = 0; i < 8; i++) {
|
|
await Future.delayed(const Duration(milliseconds: 50));
|
|
if (mounted) {
|
|
setState(() {
|
|
_d6Value = rnd.nextInt(6) + 1;
|
|
_d12Value = rnd.nextInt(12) + 1;
|
|
});
|
|
}
|
|
}
|
|
|
|
int finalD6 = rnd.nextInt(6) + 1;
|
|
int finalD12 = rnd.nextInt(12) + 1;
|
|
int cardNum = _calculateCardNumber(finalD6, finalD12);
|
|
|
|
// If card already drawn in this spread, pick next available card (no duplicates in a single layout)
|
|
final alreadyDrawnNums = _drawnResults.map((r) => r.cardNumber).toSet();
|
|
if (alreadyDrawnNums.contains(cardNum)) {
|
|
for (int i = 1; i <= 36; i++) {
|
|
final candidate = (cardNum + i - 1) % 36 + 1;
|
|
if (!alreadyDrawnNums.contains(candidate)) {
|
|
cardNum = candidate;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
final card = (cardNum - 1 < _cards.length) ? _cards[cardNum - 1] : null;
|
|
|
|
if (card != null && mounted) {
|
|
final posTitle = _selectedLayout.positionTitles[_currentStepIndex];
|
|
setState(() {
|
|
_d6Value = finalD6;
|
|
_d12Value = finalD12;
|
|
_isRollingDice = false;
|
|
_drawnResults.add((card: card, cardNumber: cardNum, positionTitle: posTitle));
|
|
_currentStepIndex++;
|
|
if (_currentStepIndex >= _selectedLayout.count) {
|
|
_saveToSession('dice');
|
|
}
|
|
});
|
|
HapticFeedback.heavyImpact();
|
|
}
|
|
}
|
|
|
|
void _applyManualCardNumber(int cardNum) {
|
|
if (cardNum < 1 || cardNum > 36 || _cards.isEmpty || _currentStepIndex >= _selectedLayout.count) return;
|
|
final card = (cardNum - 1 < _cards.length) ? _cards[cardNum - 1] : null;
|
|
if (card == null) return;
|
|
|
|
final posTitle = _selectedLayout.positionTitles[_currentStepIndex];
|
|
setState(() {
|
|
_drawnResults.add((card: card, cardNumber: cardNum, positionTitle: posTitle));
|
|
_currentStepIndex++;
|
|
_directCardNumberController.clear();
|
|
if (_currentStepIndex >= _selectedLayout.count) {
|
|
_saveToSession('manual');
|
|
}
|
|
});
|
|
HapticFeedback.selectionClick();
|
|
}
|
|
|
|
void _resetSpread() {
|
|
setState(() {
|
|
_currentStepIndex = 0;
|
|
_drawnResults.clear();
|
|
_d6Value = null;
|
|
_d12Value = null;
|
|
_savedToSession = false;
|
|
});
|
|
}
|
|
|
|
Future<void> _saveToSession(String inputMethod) async {
|
|
if (_drawnResults.isEmpty) return;
|
|
|
|
final items = _drawnResults.map((r) {
|
|
WordItem? linkedWord;
|
|
if (r.card.primary != null) {
|
|
linkedWord = _repo.getWordById(r.card.primary!.id) ?? _repo.getWordByName(r.card.primary!.word);
|
|
}
|
|
return ReadingSignItem(
|
|
title: '${r.positionTitle}: #${r.cardNumber} «${r.card.name}»',
|
|
subtitle: r.card.group,
|
|
symbol: '#${r.cardNumber}',
|
|
lore: r.card.lore,
|
|
linkedWord: linkedWord,
|
|
);
|
|
}).toList();
|
|
|
|
final reading = SyncreticReading(
|
|
id: 'reading_${DateTime.now().millisecondsSinceEpoch}',
|
|
systemId: 'lenormand',
|
|
systemName: 'Малый Оракул Ленорман',
|
|
systemIcon: '🪶',
|
|
timestamp: DateTime.now(),
|
|
layoutTitle: '${_selectedLayout.title} (${inputMethod == "dice" ? "d6+d12" : "со стола"})',
|
|
inputMethod: inputMethod,
|
|
items: items,
|
|
question: _questionController.text.trim(),
|
|
);
|
|
|
|
await _repo.addReadingToSyncreticSession(reading, question: _questionController.text.trim());
|
|
if (mounted) {
|
|
setState(() => _savedToSession = true);
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text('Расклад Ленорман «${_selectedLayout.title}» сохранён в сессию дня!'),
|
|
backgroundColor: const Color(0xFF2E7D32),
|
|
behavior: SnackBarBehavior.floating,
|
|
duration: const Duration(seconds: 2),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
Future<void> _autoDealFullTableau() async {
|
|
if (_cards.length < 36) return;
|
|
setState(() => _isRollingDice = true);
|
|
HapticFeedback.mediumImpact();
|
|
|
|
final rnd = Random();
|
|
final shuffledIndices = List<int>.generate(36, (i) => i)..shuffle(rnd);
|
|
|
|
_drawnResults.clear();
|
|
for (int i = 0; i < 36; i++) {
|
|
final cardIdx = shuffledIndices[i];
|
|
final card = _cards[cardIdx];
|
|
final cardNum = cardIdx + 1;
|
|
final posTitle = _selectedLayout.positionTitles[i];
|
|
_drawnResults.add((card: card, cardNumber: cardNum, positionTitle: posTitle));
|
|
}
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
_isRollingDice = false;
|
|
_currentStepIndex = 36;
|
|
});
|
|
HapticFeedback.heavyImpact();
|
|
_saveToSession('dice');
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final isSpreadComplete = _currentStepIndex >= _selectedLayout.count;
|
|
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFF0C0E1A),
|
|
appBar: AppBar(
|
|
actions: [
|
|
IconButton(
|
|
tooltip: 'Инструкция по Ленорман',
|
|
icon: const Icon(Icons.help_outline_rounded, color: Color(0xFFFFD54F)),
|
|
onPressed: () => DivinationInstructionModal.show(context, 'lenormand'),
|
|
),
|
|
if (_drawnResults.isNotEmpty)
|
|
IconButton(
|
|
tooltip: 'Сбросить расклад',
|
|
icon: const Icon(Icons.refresh_rounded),
|
|
onPressed: _resetSpread,
|
|
),
|
|
],
|
|
title: const Row(
|
|
children: [
|
|
Text('🪶', style: TextStyle(fontSize: 22)),
|
|
SizedBox(width: 8),
|
|
Text('Ленорман (d6 + d12)', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 17)),
|
|
],
|
|
),
|
|
bottom: TabBar(
|
|
controller: _tabController,
|
|
indicatorColor: const Color(0xFFFFD54F),
|
|
tabs: const [
|
|
Tab(icon: Icon(Icons.casino_rounded, size: 18), text: 'Жребий d6+d12'),
|
|
Tab(icon: Icon(Icons.touch_app_rounded, size: 18), text: 'Ввод 1..36 со стола'),
|
|
],
|
|
),
|
|
),
|
|
body: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
TextField(
|
|
controller: _questionController,
|
|
style: const TextStyle(color: Colors.white, fontSize: 14),
|
|
decoration: InputDecoration(
|
|
hintText: 'Вопрос к оракулу Ленорман...',
|
|
hintStyle: const TextStyle(color: Color(0xFF8C93B8), fontSize: 13),
|
|
prefixIcon: const Icon(Icons.help_outline_rounded, color: Color(0xFFFFD54F), size: 18),
|
|
filled: true,
|
|
fillColor: const Color(0xFF151829),
|
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(14)),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(14),
|
|
borderSide: const BorderSide(color: Color(0xFF2A2D4A)),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(height: 14),
|
|
|
|
// Layout Picker Row
|
|
Row(
|
|
children: [
|
|
const Icon(Icons.dashboard_customize_rounded, size: 16, color: Color(0xFFFFD54F)),
|
|
const SizedBox(width: 6),
|
|
const Text('РАСКЛАД:', style: TextStyle(color: Color(0xFFFFD54F), fontSize: 11, fontWeight: FontWeight.bold)),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.horizontal,
|
|
child: Row(
|
|
children: DivinationLayouts.lenormandLayouts.map((l) {
|
|
final isSel = _selectedLayout.id == l.id;
|
|
return Padding(
|
|
padding: const EdgeInsets.only(right: 6),
|
|
child: ChoiceChip(
|
|
label: Text(l.title, style: TextStyle(fontSize: 11.5, fontWeight: isSel ? FontWeight.bold : FontWeight.normal)),
|
|
selected: isSel,
|
|
selectedColor: const Color(0xFF4A3E1E),
|
|
side: BorderSide(color: isSel ? const Color(0xFFFFD54F) : const Color(0xFF2A2D4A)),
|
|
onSelected: (_) => _onLayoutChanged(l),
|
|
),
|
|
);
|
|
}).toList(),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 14),
|
|
|
|
// Progress Banner
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF181C30),
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(color: const Color(0xFF2A2D4A)),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Text(
|
|
'Позиция: ${_currentStepIndex + 1} из ${_selectedLayout.count}',
|
|
style: const TextStyle(color: Color(0xFF80DEEA), fontSize: 12, fontWeight: FontWeight.bold),
|
|
),
|
|
const SizedBox(width: 8),
|
|
if (!isSpreadComplete)
|
|
Expanded(
|
|
child: Text(
|
|
'«${_selectedLayout.positionTitles[_currentStepIndex]}»',
|
|
style: const TextStyle(color: Color(0xFFFFF9C4), fontSize: 12, fontWeight: FontWeight.bold),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
)
|
|
else
|
|
const Text('✓ Расклад собран', style: TextStyle(color: Color(0xFF81C784), fontSize: 12, fontWeight: FontWeight.bold)),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 14),
|
|
|
|
if (!isSpreadComplete)
|
|
SizedBox(
|
|
height: 290,
|
|
child: TabBarView(
|
|
controller: _tabController,
|
|
children: [
|
|
_buildDiceTab(),
|
|
_buildManualTab(),
|
|
],
|
|
),
|
|
),
|
|
|
|
if (isSpreadComplete) ...[
|
|
Container(
|
|
padding: const EdgeInsets.all(14),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF1E2238),
|
|
borderRadius: BorderRadius.circular(14),
|
|
border: Border.all(color: const Color(0xFF81C784)),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
const Icon(Icons.check_circle_rounded, color: Color(0xFF81C784), size: 20),
|
|
const SizedBox(width: 10),
|
|
const Expanded(
|
|
child: Text(
|
|
'Все карты расклада извлечены и записаны в сессию дня!',
|
|
style: TextStyle(color: Color(0xFFECEFF1), fontSize: 12.5, fontWeight: FontWeight.bold),
|
|
),
|
|
),
|
|
TextButton(
|
|
onPressed: _resetSpread,
|
|
child: const Text('Заново', style: TextStyle(color: Color(0xFFFFD54F), fontWeight: FontWeight.bold)),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
const SizedBox(height: 16),
|
|
|
|
if (_drawnResults.isNotEmpty) ...[
|
|
const Text(
|
|
'ВЫПАВШИЕ КАРТЫ В РАСКЛАДЕ:',
|
|
style: TextStyle(color: Color(0xFFFFD54F), fontSize: 11.5, fontWeight: FontWeight.w900, letterSpacing: 0.8),
|
|
),
|
|
const SizedBox(height: 10),
|
|
for (int i = 0; i < _drawnResults.length; i++) ...[
|
|
_buildResultCardItem(_drawnResults[i], i + 1),
|
|
const SizedBox(height: 10),
|
|
],
|
|
],
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildDiceTab() {
|
|
return Container(
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF151829),
|
|
borderRadius: BorderRadius.circular(20),
|
|
border: Border.all(color: const Color(0xFF2A2D4A)),
|
|
),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const Text(
|
|
'БРОСОК d6 + d12 (1..36 РАВНОМЕРНО)',
|
|
style: TextStyle(color: Color(0xFFFFD54F), fontSize: 11, fontWeight: FontWeight.w900, letterSpacing: 0.8),
|
|
),
|
|
const SizedBox(height: 6),
|
|
Text(
|
|
'Извлечение позиции: «' + _selectedLayout.positionTitles[_currentStepIndex] + '»',
|
|
textAlign: TextAlign.center,
|
|
style: const TextStyle(color: Color(0xFF8C93B8), fontSize: 11.5),
|
|
),
|
|
const SizedBox(height: 16),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
_buildDieBox('d6 (блок)', _d6Value, isD6: true),
|
|
const SizedBox(width: 20),
|
|
_buildDieBox('d12 (номер)', _d12Value),
|
|
],
|
|
),
|
|
const SizedBox(height: 18),
|
|
ElevatedButton.icon(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color(0xFFFFD54F),
|
|
foregroundColor: Colors.black,
|
|
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
|
),
|
|
onPressed: _isRollingDice ? null : _rollDiceForStep,
|
|
icon: const Icon(Icons.casino_rounded, size: 20),
|
|
label: Text(
|
|
_isRollingDice ? 'Кости летят...' : 'Бросить d6+d12 (шаг ${_currentStepIndex + 1})',
|
|
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13.5),
|
|
),
|
|
),
|
|
if (_selectedLayout.count >= 9) ...[
|
|
const SizedBox(height: 10),
|
|
OutlinedButton.icon(
|
|
style: OutlinedButton.styleFrom(
|
|
foregroundColor: const Color(0xFF80DEEA),
|
|
side: const BorderSide(color: Color(0xFF80DEEA)),
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
),
|
|
onPressed: _isRollingDice ? null : _autoDealFullTableau,
|
|
icon: const Icon(Icons.auto_awesome_rounded, size: 16),
|
|
label: Text(
|
|
_selectedLayout.count == 36 ? 'Случайный расклад всех 36 Домов' : 'Разложить весь квадрат 3×3',
|
|
style: const TextStyle(fontSize: 11.5, fontWeight: FontWeight.bold),
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildDieBox(String label, int? val, {bool isD6 = false}) {
|
|
String sub = '';
|
|
if (val != null && isD6) {
|
|
if (val <= 2) sub = 'База +0 (1..12)';
|
|
else if (val <= 4) sub = 'База +12 (13..24)';
|
|
else sub = 'База +24 (25..36)';
|
|
}
|
|
|
|
return Column(
|
|
children: [
|
|
Container(
|
|
width: 68,
|
|
height: 68,
|
|
alignment: Alignment.center,
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF232742),
|
|
borderRadius: BorderRadius.circular(16),
|
|
border: Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.6), width: 1.5),
|
|
),
|
|
child: Text(
|
|
val != null ? '$val' : '?',
|
|
style: const TextStyle(color: Color(0xFFFFF9C4), fontSize: 26, fontWeight: FontWeight.w900),
|
|
),
|
|
),
|
|
const SizedBox(height: 6),
|
|
Text(label, style: const TextStyle(color: Color(0xFF90A4AE), fontSize: 10, fontWeight: FontWeight.bold)),
|
|
if (sub.isNotEmpty)
|
|
Text(sub, style: const TextStyle(color: Color(0xFFFFD54F), fontSize: 9, fontWeight: FontWeight.bold)),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildManualTab() {
|
|
return Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF151829),
|
|
borderRadius: BorderRadius.circular(20),
|
|
border: Border.all(color: const Color(0xFF2A2D4A)),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: TextField(
|
|
controller: _directCardNumberController,
|
|
keyboardType: TextInputType.number,
|
|
style: const TextStyle(color: Colors.white, fontSize: 14),
|
|
decoration: InputDecoration(
|
|
hintText: 'Номер карты для шага ${_currentStepIndex + 1} (1..36)...',
|
|
hintStyle: const TextStyle(color: Color(0xFF8C93B8), fontSize: 11),
|
|
isDense: true,
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
|
|
filled: true,
|
|
fillColor: const Color(0xFF1E2238),
|
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10)),
|
|
),
|
|
onSubmitted: (val) {
|
|
final n = int.tryParse(val.trim());
|
|
if (n != null) _applyManualCardNumber(n);
|
|
},
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
ElevatedButton(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: const Color(0xFFFFD54F),
|
|
foregroundColor: Colors.black,
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
|
),
|
|
onPressed: () {
|
|
final n = int.tryParse(_directCardNumberController.text.trim());
|
|
if (n != null) _applyManualCardNumber(n);
|
|
},
|
|
child: const Text('Ввод', style: TextStyle(fontWeight: FontWeight.bold)),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 8),
|
|
Expanded(
|
|
child: GridView.builder(
|
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
|
crossAxisCount: 6,
|
|
crossAxisSpacing: 6,
|
|
mainAxisSpacing: 6,
|
|
childAspectRatio: 1.0,
|
|
),
|
|
itemCount: _cards.length,
|
|
itemBuilder: (ctx, i) {
|
|
return InkWell(
|
|
onTap: () => _applyManualCardNumber(i + 1),
|
|
borderRadius: BorderRadius.circular(8),
|
|
child: Container(
|
|
alignment: Alignment.center,
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF1E2238),
|
|
borderRadius: BorderRadius.circular(8),
|
|
border: Border.all(color: const Color(0xFF2A2D4A)),
|
|
),
|
|
child: Text(
|
|
'${i + 1}',
|
|
style: const TextStyle(color: Color(0xFFE0E2EC), fontSize: 14, fontWeight: FontWeight.bold),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildResultCardItem(({TraditionStation card, int cardNumber, String positionTitle}) res, int index) {
|
|
final c = res.card;
|
|
return Container(
|
|
padding: const EdgeInsets.all(14),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF181C30),
|
|
borderRadius: BorderRadius.circular(16),
|
|
border: Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.4)),
|
|
),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Container(
|
|
width: 44,
|
|
height: 44,
|
|
alignment: Alignment.center,
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF232742),
|
|
borderRadius: BorderRadius.circular(10),
|
|
border: Border.all(color: const Color(0xFFFFD54F)),
|
|
),
|
|
child: Text(
|
|
'#${res.cardNumber}',
|
|
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFFFFD54F)),
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
'#$index. ${res.positionTitle}: ${c.name}',
|
|
style: const TextStyle(color: Color(0xFFFFF9C4), fontSize: 14, fontWeight: FontWeight.bold),
|
|
),
|
|
),
|
|
IconButton(
|
|
icon: const Icon(Icons.auto_stories_rounded, size: 18, color: Color(0xFFFFD54F)),
|
|
padding: EdgeInsets.zero,
|
|
constraints: const BoxConstraints(),
|
|
tooltip: 'Сочетания карты',
|
|
onPressed: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (_) => LenormandStationScreen(number: res.cardNumber),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(c.group, style: const TextStyle(color: Color(0xFF80DEEA), fontSize: 11)),
|
|
const SizedBox(height: 6),
|
|
Text(
|
|
c.lore,
|
|
maxLines: 2,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: const TextStyle(color: Color(0xFFD1D5DB), fontSize: 12, height: 1.4),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|