feat: complete academic symbol lexicon heal, 2-column Cooper extraction, and updated assets
This commit is contained in:
@@ -0,0 +1,608 @@
|
||||
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';
|
||||
|
||||
class RunesDivinationScreen extends StatefulWidget {
|
||||
final String? initialQuestion;
|
||||
|
||||
const RunesDivinationScreen({super.key, this.initialQuestion});
|
||||
|
||||
@override
|
||||
State<RunesDivinationScreen> createState() => _RunesDivinationScreenState();
|
||||
}
|
||||
|
||||
class _RunesDivinationScreenState extends State<RunesDivinationScreen> with SingleTickerProviderStateMixin {
|
||||
late TabController _tabController;
|
||||
final _questionController = TextEditingController();
|
||||
final _repo = WordsRepository();
|
||||
|
||||
// 24 runes from canon
|
||||
List<TraditionStation> _runes = [];
|
||||
|
||||
// Layout selection
|
||||
DivinationLayout _selectedLayout = DivinationLayouts.runesLayouts[0]; // 1, 3, or 5
|
||||
int _currentStepIndex = 0; // for multi-step layouts
|
||||
|
||||
// Dice state (for current step)
|
||||
int? _d12First;
|
||||
int? _d12Second;
|
||||
bool _isRollingDice = false;
|
||||
|
||||
// Manual input state
|
||||
bool _isManualReversed = false;
|
||||
|
||||
// Multi-item results
|
||||
List<({TraditionStation rune, bool isReversed, 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!;
|
||||
}
|
||||
_loadRunes();
|
||||
}
|
||||
|
||||
void _loadRunes() {
|
||||
final canon = TraditionsRepository().byId('runes');
|
||||
if (canon != null) {
|
||||
_runes = canon.stations.take(24).toList();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tabController.dispose();
|
||||
_questionController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
int _calculateRuneIndexFromDice(int d1, int d2) {
|
||||
final isOdd = (d1 % 2) != 0;
|
||||
final runeNumber = isOdd ? d2 : (d2 + 12);
|
||||
return (runeNumber - 1).clamp(0, 23);
|
||||
}
|
||||
|
||||
void _onLayoutChanged(DivinationLayout layout) {
|
||||
setState(() {
|
||||
_selectedLayout = layout;
|
||||
_currentStepIndex = 0;
|
||||
_drawnResults.clear();
|
||||
_d12First = null;
|
||||
_d12Second = null;
|
||||
_savedToSession = false;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _rollDiceForCurrentStep() async {
|
||||
if (_runes.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(() {
|
||||
_d12First = rnd.nextInt(12) + 1;
|
||||
_d12Second = rnd.nextInt(12) + 1;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
final finalD1 = rnd.nextInt(12) + 1;
|
||||
final finalD2 = rnd.nextInt(12) + 1;
|
||||
final isRev = rnd.nextBool() && rnd.nextBool(); // 25%
|
||||
|
||||
final idx = _calculateRuneIndexFromDice(finalD1, finalD2);
|
||||
final rune = _runes[idx];
|
||||
|
||||
final posTitle = _selectedLayout.positionTitles[_currentStepIndex];
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_d12First = finalD1;
|
||||
_d12Second = finalD2;
|
||||
_isRollingDice = false;
|
||||
_drawnResults.add((rune: rune, isReversed: isRev, positionTitle: posTitle));
|
||||
_currentStepIndex++;
|
||||
if (_currentStepIndex >= _selectedLayout.count) {
|
||||
_saveToSession('dice');
|
||||
}
|
||||
});
|
||||
HapticFeedback.heavyImpact();
|
||||
}
|
||||
}
|
||||
|
||||
void _applyManualChoice(int runeIndex) {
|
||||
if (runeIndex < 0 || runeIndex >= _runes.length || _currentStepIndex >= _selectedLayout.count) return;
|
||||
final rune = _runes[runeIndex];
|
||||
final posTitle = _selectedLayout.positionTitles[_currentStepIndex];
|
||||
|
||||
setState(() {
|
||||
_drawnResults.add((rune: rune, isReversed: _isManualReversed, positionTitle: posTitle));
|
||||
_isManualReversed = false;
|
||||
_currentStepIndex++;
|
||||
if (_currentStepIndex >= _selectedLayout.count) {
|
||||
_saveToSession('manual');
|
||||
}
|
||||
});
|
||||
HapticFeedback.selectionClick();
|
||||
}
|
||||
|
||||
void _resetSpread() {
|
||||
setState(() {
|
||||
_currentStepIndex = 0;
|
||||
_drawnResults.clear();
|
||||
_d12First = null;
|
||||
_d12Second = null;
|
||||
_savedToSession = false;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _saveToSession(String inputMethod) async {
|
||||
if (_drawnResults.isEmpty) return;
|
||||
|
||||
final items = _drawnResults.map((r) {
|
||||
WordItem? linkedWord;
|
||||
if (r.rune.primary != null) {
|
||||
linkedWord = _repo.getWordById(r.rune.primary!.id) ?? _repo.getWordByName(r.rune.primary!.word);
|
||||
}
|
||||
return ReadingSignItem(
|
||||
title: '${r.positionTitle}: ${r.rune.name}',
|
||||
subtitle: r.rune.group,
|
||||
symbol: r.rune.symbol,
|
||||
lore: r.rune.lore,
|
||||
isReversed: r.isReversed,
|
||||
linkedWord: linkedWord,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
final reading = SyncreticReading(
|
||||
id: 'reading_${DateTime.now().millisecondsSinceEpoch}',
|
||||
systemId: 'runes',
|
||||
systemName: 'Руны Старшего Футарка (24)',
|
||||
systemIcon: 'ᚱ',
|
||||
timestamp: DateTime.now(),
|
||||
layoutTitle: '${_selectedLayout.title} (${inputMethod == "dice" ? "2×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),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@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, 'runes'),
|
||||
),
|
||||
if (_drawnResults.isNotEmpty)
|
||||
IconButton(
|
||||
tooltip: 'Сбросить расклад',
|
||||
icon: const Icon(Icons.refresh_rounded),
|
||||
onPressed: _resetSpread,
|
||||
),
|
||||
],
|
||||
title: const Row(
|
||||
children: [
|
||||
Text('ᚱ', style: TextStyle(fontSize: 22, color: Color(0xFFFFD54F))),
|
||||
SizedBox(width: 8),
|
||||
Text('Руны Старшего Футарка', 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: 'Жребий 2×d12'),
|
||||
Tab(icon: Icon(Icons.touch_app_rounded, size: 18), text: 'Ввод со стола'),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// Question Input
|
||||
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.runesLayouts.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),
|
||||
|
||||
// Controls Tabs
|
||||
if (!isSpreadComplete)
|
||||
SizedBox(
|
||||
height: 310,
|
||||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
children: [
|
||||
_buildDiceTab(),
|
||||
_buildManualTabThreeAetts(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
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),
|
||||
|
||||
// Drawn Runes Cards List
|
||||
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++) ...[
|
||||
_buildResultRuneCard(_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(
|
||||
'БРОСОК 2×d12 (1..24 РАВНОМЕРНО)',
|
||||
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('d12 #1 (чет/нечет)', _d12First, isOddEven: true),
|
||||
const SizedBox(width: 20),
|
||||
_buildDieBox('d12 #2 (1..12)', _d12Second),
|
||||
],
|
||||
),
|
||||
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 : _rollDiceForCurrentStep,
|
||||
icon: const Icon(Icons.casino_rounded, size: 20),
|
||||
label: Text(
|
||||
_isRollingDice ? 'Кости звенят...' : 'Бросить для шага ${_currentStepIndex + 1}',
|
||||
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13.5),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDieBox(String label, int? val, {bool isOddEven = false}) {
|
||||
String sub = '';
|
||||
if (val != null && isOddEven) {
|
||||
sub = (val % 2 != 0) ? 'Нечет (1..12)' : 'Чет (13..24)';
|
||||
}
|
||||
|
||||
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)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Exact 3 Rows of 8 Runes: 1st Aett (1..8), 2nd Aett (9..16), 3rd Aett (17..24)
|
||||
Widget _buildManualTabThreeAetts() {
|
||||
const aettNames = [
|
||||
'I ЭТТ ФРЕЙИ (ᚠ..ᚹ)',
|
||||
'II ЭТТ ХЕЙМДАЛЛЯ (ᚺ..ᛏ)',
|
||||
'III ЭТТ ТЮРА (ᛒ..ᛟ)',
|
||||
];
|
||||
|
||||
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(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'ВЫБЕРИТЕ РУНУ ДЛЯ ШАГА ${_currentStepIndex + 1}:',
|
||||
style: const TextStyle(color: Color(0xFFFFD54F), fontSize: 10.5, fontWeight: FontWeight.bold),
|
||||
),
|
||||
FilterChip(
|
||||
label: const Text('Перевёрнутая ᛉ/ᛦ', style: TextStyle(fontSize: 10)),
|
||||
selected: _isManualReversed,
|
||||
selectedColor: const Color(0xFFEF5350).withValues(alpha: 0.3),
|
||||
side: BorderSide(color: _isManualReversed ? const Color(0xFFEF5350) : const Color(0xFF2A2D4A)),
|
||||
onSelected: (v) => setState(() => _isManualReversed = v),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
// 3 Rows of 8 runes
|
||||
for (int aett = 0; aett < 3; aett++) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4, bottom: 2),
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
aettNames[aett],
|
||||
style: const TextStyle(color: Color(0xFF80DEEA), fontSize: 9.5, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
for (int col = 0; col < 8; col++) ...[
|
||||
Builder(builder: (ctx) {
|
||||
final idx = aett * 8 + col;
|
||||
if (idx >= _runes.length) return const SizedBox.shrink();
|
||||
final r = _runes[idx];
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 2),
|
||||
child: InkWell(
|
||||
onTap: () => _applyManualChoice(idx),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
child: Container(
|
||||
height: 38,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF1E2238),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(color: const Color(0xFF2A2D4A)),
|
||||
),
|
||||
child: Text(
|
||||
r.symbol,
|
||||
style: const TextStyle(color: Color(0xFFFFF9C4), fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildResultRuneCard(({TraditionStation rune, bool isReversed, String positionTitle}) res, int index) {
|
||||
final r = res.rune;
|
||||
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(
|
||||
r.symbol,
|
||||
style: const TextStyle(fontSize: 26, color: Color(0xFFFFD54F)),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'#$index. ${res.positionTitle}: ${r.name}',
|
||||
style: const TextStyle(color: Color(0xFFFFF9C4), fontSize: 14, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
if (res.isReversed)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF5C1D1D),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(color: const Color(0xFFEF5350)),
|
||||
),
|
||||
child: const Text('Перевёрнутая', style: TextStyle(color: Color(0xFFFF8A80), fontSize: 9.5, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(r.group, style: const TextStyle(color: Color(0xFF80DEEA), fontSize: 11)),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
r.lore,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(color: Color(0xFFD1D5DB), fontSize: 12, height: 1.4),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user