Files
999/lib/screens/numerology_divination_screen.dart

1163 lines
51 KiB
Dart

import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../models/syncretic_session_model.dart';
import '../models/word_model.dart';
import '../services/day_numerology.dart';
import '../services/words_repository.dart';
import '../widgets/divination_instruction_modal.dart';
import 'symbol_detail_screen.dart';
class NumerologyDivinationScreen extends StatefulWidget {
final String? initialQuestion;
const NumerologyDivinationScreen({super.key, this.initialQuestion});
@override
State<NumerologyDivinationScreen> createState() => _NumerologyDivinationScreenState();
}
class _NumerologyDivinationScreenState extends State<NumerologyDivinationScreen>
with SingleTickerProviderStateMixin {
late TabController _tabController;
final _questionController = TextEditingController();
final _repo = WordsRepository();
DateTime _selectedDate = DateTime(1990, 7, 15);
final _dayCtrl = TextEditingController(text: '15');
final _monthCtrl = TextEditingController(text: '07');
final _yearCtrl = TextEditingController(text: '1990');
// Matrix calculation data
int _w1 = 0;
int _w2 = 0;
int _w3 = 0;
int _w4 = 0;
int _lifePath = 0;
Map<int, int> _counts = {};
int? _selectedCell = 1;
bool _savedToSession = false;
// Dice roll state
List<int> _diceDigits = [];
bool _isRollingDice = false;
@override
void initState() {
super.initState();
_tabController = TabController(length: 2, vsync: this);
if (widget.initialQuestion != null) {
_questionController.text = widget.initialQuestion!;
}
_calculateFromDate(_selectedDate);
}
@override
void dispose() {
_tabController.dispose();
_questionController.dispose();
_dayCtrl.dispose();
_monthCtrl.dispose();
_yearCtrl.dispose();
super.dispose();
}
// --- Numerological Math (Alexandrian School) ---
void _calculateFromDate(DateTime date) {
_selectedDate = date;
_dayCtrl.text = date.day.toString().padLeft(2, '0');
_monthCtrl.text = date.month.toString().padLeft(2, '0');
_yearCtrl.text = date.year.toString();
final dayDigits = _splitDigits(date.day);
final monthDigits = _splitDigits(date.month);
final yearDigits = _splitDigits(date.year);
final allDateDigits = [...dayDigits, ...monthDigits, ...yearDigits];
// 1st working number: sum of all digits of date
final w1 = allDateDigits.reduce((a, b) => a + b);
// 2nd working number: sum of digits of w1
final w2 = _splitDigits(w1).reduce((a, b) => a + b);
// 3rd working number: w1 - 2 * (first non-zero digit of day)
int firstNonZeroDayDigit = dayDigits.firstWhere((d) => d > 0, orElse: () => 1);
final w3 = w1 - (2 * firstNonZeroDayDigit);
// 4th working number: sum of digits of w3
final w4 = _splitDigits(w3.abs()).reduce((a, b) => a + b);
// Life Path Number: reduce day + month + year
int rDay = _reduceNumber(date.day, keepMaster: true);
int rMonth = _reduceNumber(date.month, keepMaster: true);
int rYear = _reduceNumber(date.year, keepMaster: true);
int lifePath = _reduceNumber(rDay + rMonth + rYear, keepMaster: true);
// Digit pool for 3x3 matrix
final pool = <int>[
...allDateDigits,
..._splitDigits(w1),
..._splitDigits(w2),
..._splitDigits(w3.abs()),
..._splitDigits(w4),
];
final counts = <int, int>{};
for (int i = 1; i <= 9; i++) {
counts[i] = pool.where((d) => d == i).length;
}
setState(() {
_w1 = w1;
_w2 = w2;
_w3 = w3;
_w4 = w4;
_lifePath = lifePath;
_counts = counts;
_savedToSession = false;
});
}
void _calculateFromDigits(List<int> digits) {
// Digits is 8 numbers: d1 d2 m1 m2 y1 y2 y3 y4
final day = digits[0] * 10 + digits[1];
final month = digits[2] * 10 + digits[3];
final year = digits[4] * 1000 + digits[5] * 100 + digits[6] * 10 + digits[7];
final w1 = digits.reduce((a, b) => a + b);
final w2 = _splitDigits(w1).reduce((a, b) => a + b);
int firstNonZero = digits.take(2).firstWhere((d) => d > 0, orElse: () => 1);
final w3 = w1 - (2 * firstNonZero);
final w4 = _splitDigits(w3.abs()).reduce((a, b) => a + b);
int lifePath = _reduceNumber(w1, keepMaster: true);
final pool = <int>[
...digits,
..._splitDigits(w1),
..._splitDigits(w2),
..._splitDigits(w3.abs()),
..._splitDigits(w4),
];
final counts = <int, int>{};
for (int i = 1; i <= 9; i++) {
counts[i] = pool.where((d) => d == i).length;
}
setState(() {
_w1 = w1;
_w2 = w2;
_w3 = w3;
_w4 = w4;
_lifePath = lifePath;
_counts = counts;
_savedToSession = false;
});
}
List<int> _splitDigits(int n) {
if (n == 0) return [0];
final s = n.abs().toString();
return s.split('').map(int.parse).toList();
}
int _reduceNumber(int n, {bool keepMaster = false}) {
return DayNumerology.reduce(n, keepMaster: keepMaster);
}
WordItem? _findWordForNumber(int n) {
final names = {
0: 'ноль',
1: 'один',
2: 'два',
3: 'три',
4: 'четыре',
5: 'пять',
6: 'шесть',
7: 'семь',
8: 'восемь',
9: 'девять',
10: 'десять',
11: 'одиннадцать',
12: 'двенадцать',
13: 'тринадцать',
21: 'двадцать один',
22: 'двадцать два',
33: 'тридцать три',
1000: 'тысяча',
};
final name = names[n];
if (name == null) return null;
try {
return _repo.allWords.firstWhere((w) => w.word.toLowerCase() == name);
} catch (_) {
return null;
}
}
void _rollDice() async {
setState(() => _isRollingDice = true);
HapticFeedback.mediumImpact();
final rng = Random();
// Generate 8 d10 digits (0..9)
await Future.delayed(const Duration(milliseconds: 300));
final digits = List.generate(8, (_) => rng.nextInt(10));
// Ensure day month year make somewhat sane numerology
if (digits[0] > 3) digits[0] = rng.nextInt(3);
if (digits[2] > 1) digits[2] = rng.nextInt(2);
_diceDigits = digits;
_calculateFromDigits(digits);
setState(() => _isRollingDice = false);
}
Future<void> _saveToSyncreticSession() async {
if (_savedToSession) return;
final lpWord = _findWordForNumber(_lifePath);
final items = <ReadingSignItem>[
ReadingSignItem(
title: 'Число Жизненного Пути: $_lifePath',
subtitle: _getLifePathTitle(_lifePath),
symbol: '$_lifePath',
lore: _getLifePathLore(_lifePath),
linkedWord: lpWord,
meta: {
'type': 'life_path',
'number': _lifePath,
'w1': _w1,
'w2': _w2,
'w3': _w3,
'w4': _w4,
},
),
ReadingSignItem(
title: 'Рабочие Числа Пифагора',
subtitle: 'W1: $_w1 • W2: $_w2 • W3: $_w3 • W4: $_w4',
symbol: 'Σ',
lore: 'W1 (сумма даты) показывает потенциал воплощения. W2 — ведущее качество. W3 — кармический багаж рода. W4 — ключевой навык для реализации.',
),
];
// Add significant cells (counts > 0)
for (int cell = 1; cell <= 9; cell++) {
final cnt = _counts[cell] ?? 0;
final cellWord = _findWordForNumber(cell);
items.add(
ReadingSignItem(
title: 'Ячейка $cell: ${_getCellName(cell)} (${cnt > 0 ? List.filled(cnt, cell).join() : "нет"})',
subtitle: _getCellQualitySummary(cell, cnt),
symbol: '$cell',
lore: _getCellLore(cell, cnt),
linkedWord: cellWord,
meta: {'cell': cell, 'count': cnt},
),
);
}
final reading = SyncreticReading(
id: 'num_${DateTime.now().millisecondsSinceEpoch}',
systemId: 'numerology',
systemName: 'Нумерология и Квадрат Пифагора',
systemIcon: '🔢',
timestamp: DateTime.now(),
layoutTitle: 'Психоматрица 3×3 (ЧЖП: $_lifePath)',
inputMethod: _tabController.index == 0 ? 'manual' : 'dice',
items: items,
question: _questionController.text.trim().isNotEmpty ? _questionController.text.trim() : null,
);
await _repo.addReadingToSyncreticSession(reading, question: _questionController.text.trim());
setState(() => _savedToSession = true);
HapticFeedback.heavyImpact();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: const Color(0xFF2E7D32),
behavior: SnackBarBehavior.floating,
content: Row(
children: [
const Icon(Icons.check_circle_rounded, color: Colors.white, size: 20),
const SizedBox(width: 10),
Expanded(
child: Text(
'Психоматрица сохранена в Сессию Дня (ЧЖП: $_lifePath)',
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13),
),
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
final selectedCellWord = _selectedCell != null ? _findWordForNumber(_selectedCell!) : null;
return Scaffold(
backgroundColor: const Color(0xFF0C0E1A),
appBar: AppBar(
backgroundColor: const Color(0xFF141729),
elevation: 0,
title: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Нумерология и Пифагор',
style: TextStyle(color: Color(0xFFFFF9C4), fontSize: 16, fontWeight: FontWeight.bold),
),
Text(
'Психоматрица 3×3 и Число Судьбы',
style: TextStyle(color: Color(0xFF8C93B8), fontSize: 11),
),
],
),
actions: [
IconButton(
icon: const Icon(Icons.menu_book_rounded, color: Color(0xFFFFD54F)),
tooltip: 'Инструкция',
onPressed: () => DivinationInstructionModal.show(context, 'numerology'),
),
IconButton(
icon: Icon(
_savedToSession ? Icons.bookmark_added_rounded : Icons.bookmark_add_outlined,
color: _savedToSession ? const Color(0xFF81C784) : const Color(0xFFFFD54F),
),
tooltip: 'Добавить в Сессию Дня',
onPressed: _saveToSyncreticSession,
),
],
bottom: TabBar(
controller: _tabController,
indicatorColor: const Color(0xFFFFD54F),
indicatorWeight: 3,
labelColor: const Color(0xFFFFD54F),
unselectedLabelColor: const Color(0xFF8C93B8),
tabs: const [
Tab(icon: Icon(Icons.cake_rounded, size: 18), text: 'Дата рождения'),
Tab(icon: Icon(Icons.casino_rounded, size: 18), text: 'Жребий d10'),
],
),
),
body: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Question Input
TextField(
controller: _questionController,
style: const TextStyle(color: Colors.white, fontSize: 13),
decoration: InputDecoration(
hintText: 'Фокус внимания / имя кверента / вопрос...',
hintStyle: const TextStyle(color: Color(0xFF6B7280), fontSize: 12),
filled: true,
fillColor: const Color(0xFF16192E),
prefixIcon: const Icon(Icons.help_outline_rounded, color: Color(0xFFFFD54F), size: 18),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none),
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
),
),
const SizedBox(height: 14),
// Tab View Area
if (_tabController.index == 0) _buildDateControls() else _buildDiceControls(),
const SizedBox(height: 16),
// Life Path & Working Numbers Banner
_buildSummaryBanner(),
const SizedBox(height: 16),
// Pythagorean 3x3 Grid
_buildPythagoreanGrid(),
const SizedBox(height: 16),
// Selected Cell Detail Card
if (_selectedCell != null) _buildCellDetailCard(_selectedCell!, selectedCellWord),
const SizedBox(height: 16),
// Lines Analysis (Rows, Cols, Diags)
_buildLinesAnalysis(),
const SizedBox(height: 24),
// Save to Session Button
SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: _savedToSession ? const Color(0xFF2E7D32) : const Color(0xFFFFD54F),
foregroundColor: _savedToSession ? Colors.white : Colors.black,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
elevation: 4,
),
icon: Icon(_savedToSession ? Icons.check_circle_rounded : Icons.auto_awesome_rounded),
label: Text(
_savedToSession ? 'Сохранено в Сессию Дня' : 'Записать матрицу в Сессию Дня',
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
),
onPressed: _saveToSyncreticSession,
),
),
const SizedBox(height: 20),
],
),
),
);
}
Widget _buildDateControls() {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFF141729),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: const Color(0xFF232845)),
),
child: Column(
children: [
Row(
children: [
Expanded(
child: Row(
children: [
Expanded(
child: TextField(
controller: _dayCtrl,
keyboardType: TextInputType.number,
maxLength: 2,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
decoration: InputDecoration(
counterText: '',
labelText: 'День',
labelStyle: const TextStyle(color: Color(0xFF8C93B8), fontSize: 11),
filled: true,
fillColor: const Color(0xFF1C2038),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
contentPadding: const EdgeInsets.symmetric(vertical: 8),
),
onChanged: (_) => _onManualDateInput(),
),
),
const SizedBox(width: 6),
Expanded(
child: TextField(
controller: _monthCtrl,
keyboardType: TextInputType.number,
maxLength: 2,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
decoration: InputDecoration(
counterText: '',
labelText: 'Месяц',
labelStyle: const TextStyle(color: Color(0xFF8C93B8), fontSize: 11),
filled: true,
fillColor: const Color(0xFF1C2038),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
contentPadding: const EdgeInsets.symmetric(vertical: 8),
),
onChanged: (_) => _onManualDateInput(),
),
),
const SizedBox(width: 6),
Expanded(
flex: 2,
child: TextField(
controller: _yearCtrl,
keyboardType: TextInputType.number,
maxLength: 4,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
decoration: InputDecoration(
counterText: '',
labelText: 'Год',
labelStyle: const TextStyle(color: Color(0xFF8C93B8), fontSize: 11),
filled: true,
fillColor: const Color(0xFF1C2038),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
contentPadding: const EdgeInsets.symmetric(vertical: 8),
),
onChanged: (_) => _onManualDateInput(),
),
),
],
),
),
const SizedBox(width: 8),
IconButton(
style: IconButton.styleFrom(
backgroundColor: const Color(0xFF232845),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
),
icon: const Icon(Icons.calendar_month_rounded, color: Color(0xFFFFD54F)),
tooltip: 'Календарь',
onPressed: () async {
final picked = await showDatePicker(
context: context,
initialDate: _selectedDate,
firstDate: DateTime(1900),
lastDate: DateTime(2100),
builder: (ctx, child) {
return Theme(
data: ThemeData.dark().copyWith(
colorScheme: const ColorScheme.dark(
primary: Color(0xFFFFD54F),
onPrimary: Colors.black,
surface: Color(0xFF181C30),
),
),
child: child!,
);
},
);
if (picked != null) {
_calculateFromDate(picked);
}
},
),
],
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton.icon(
icon: const Icon(Icons.today_rounded, size: 15, color: Color(0xFF80DEEA)),
label: const Text('Сегодняшний день', style: TextStyle(color: Color(0xFF80DEEA), fontSize: 12)),
onPressed: () => _calculateFromDate(DateTime.now()),
),
Text(
'Дата: ${_selectedDate.day.toString().padLeft(2, '0')}.${_selectedDate.month.toString().padLeft(2, '0')}.${_selectedDate.year}',
style: const TextStyle(color: Color(0xFFFFF9C4), fontSize: 12, fontWeight: FontWeight.bold),
),
],
),
],
),
);
}
void _onManualDateInput() {
final d = int.tryParse(_dayCtrl.text) ?? 1;
final m = int.tryParse(_monthCtrl.text) ?? 1;
final y = int.tryParse(_yearCtrl.text) ?? 1990;
if (d >= 1 && d <= 31 && m >= 1 && m <= 12 && y >= 1900 && y <= 2100) {
_calculateFromDate(DateTime(y, m, d));
}
}
Widget _buildDiceControls() {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFF141729),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: const Color(0xFF232845)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'МАНТИЧЕСКИЙ СРЕЗ (8×d10)',
style: TextStyle(color: Color(0xFFFFD54F), fontSize: 11, fontWeight: FontWeight.bold),
),
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFFFD54F),
foregroundColor: Colors.black,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
icon: _isRollingDice
? const SizedBox(width: 14, height: 14, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.black))
: const Icon(Icons.casino_rounded, size: 16),
label: const Text('Бросить кости', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12)),
onPressed: _isRollingDice ? null : _rollDice,
),
],
),
const SizedBox(height: 8),
if (_diceDigits.isNotEmpty)
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: _diceDigits.map((d) {
return Container(
width: 32,
height: 32,
alignment: Alignment.center,
decoration: BoxDecoration(
color: const Color(0xFF232845),
borderRadius: BorderRadius.circular(6),
border: Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.5)),
),
child: Text(
'$d',
style: const TextStyle(color: Color(0xFFFFF9C4), fontWeight: FontWeight.bold, fontSize: 15),
),
);
}).toList(),
)
else
const Text(
'Нажмите «Бросить кости», чтобы сгенерировать нумерологический код ситуации.',
style: TextStyle(color: Color(0xFF8C93B8), fontSize: 11.5),
),
],
),
);
}
Widget _buildSummaryBanner() {
final lpWord = _findWordForNumber(_lifePath);
return Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF1E1B38), Color(0xFF141729)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.3)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 44,
height: 44,
alignment: Alignment.center,
decoration: BoxDecoration(
color: const Color(0xFF3B2B5E),
shape: BoxShape.circle,
border: Border.all(color: const Color(0xFFFFD54F), width: 1.5),
),
child: Text(
'$_lifePath',
style: const TextStyle(color: Color(0xFFFFD54F), fontSize: 20, fontWeight: FontWeight.w900),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Text(
'ЧИСЛО ЖИЗНЕННОГО ПУТИ',
style: TextStyle(color: Color(0xFFFFD54F), fontSize: 10.5, fontWeight: FontWeight.bold, letterSpacing: 0.8),
),
if (lpWord != null) ...[
const SizedBox(width: 6),
Container(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 1),
decoration: BoxDecoration(
color: const Color(0xFF232845),
borderRadius: BorderRadius.circular(4),
),
child: Text(
'#${lpWord.formattedSessionNumber} «${lpWord.word}»',
style: const TextStyle(color: Color(0xFF80DEEA), fontSize: 10, fontWeight: FontWeight.bold),
),
),
],
],
),
const SizedBox(height: 2),
Text(
_getLifePathTitle(_lifePath),
style: const TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.bold),
),
],
),
),
],
),
const SizedBox(height: 10),
Text(
_getLifePathLore(_lifePath),
style: const TextStyle(color: Color(0xFFECEFF1), fontSize: 12, height: 1.35),
),
const Divider(color: Color(0xFF2B2F4C), height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildWorkingNumberBadge('W1', '$_w1', 'Потенциал'),
_buildWorkingNumberBadge('W2', '$_w2', 'Доминанта'),
_buildWorkingNumberBadge('W3', '$_w3', 'Карма рода'),
_buildWorkingNumberBadge('W4', '$_w4', 'Основа'),
],
),
],
),
);
}
Widget _buildWorkingNumberBadge(String label, String value, String desc) {
return Column(
children: [
Text(label, style: const TextStyle(color: Color(0xFF8C93B8), fontSize: 10, fontWeight: FontWeight.bold)),
const SizedBox(height: 2),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: const Color(0xFF16192E),
borderRadius: BorderRadius.circular(6),
border: Border.all(color: const Color(0xFF2E3458)),
),
child: Text(
value,
style: const TextStyle(color: Color(0xFFFFD54F), fontSize: 13, fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 2),
Text(desc, style: const TextStyle(color: Color(0xFF6B7280), fontSize: 9)),
],
);
}
Widget _buildPythagoreanGrid() {
// Standard layout of the 3x3 Pythagorean Psychomatrix:
// Row 1: 1, 4, 7
// Row 2: 2, 5, 8
// Row 3: 3, 6, 9
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.grid_3x3_rounded, size: 16, color: Color(0xFFFFD54F)),
SizedBox(width: 6),
Text(
'ПСИХОМАТРИЦА 3×3 (КВАДРАТ ПИФАГОРА)',
style: TextStyle(color: Color(0xFFFFD54F), fontSize: 11, fontWeight: FontWeight.bold, letterSpacing: 0.8),
),
],
),
const SizedBox(height: 8),
Container(
decoration: BoxDecoration(
color: const Color(0xFF141729),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: const Color(0xFF232845)),
),
child: Column(
children: [
Row(
children: [
Expanded(child: _buildGridCell(1)),
Container(width: 1, height: 80, color: const Color(0xFF232845)),
Expanded(child: _buildGridCell(4)),
Container(width: 1, height: 80, color: const Color(0xFF232845)),
Expanded(child: _buildGridCell(7)),
],
),
Container(height: 1, color: const Color(0xFF232845)),
Row(
children: [
Expanded(child: _buildGridCell(2)),
Container(width: 1, height: 80, color: const Color(0xFF232845)),
Expanded(child: _buildGridCell(5)),
Container(width: 1, height: 80, color: const Color(0xFF232845)),
Expanded(child: _buildGridCell(8)),
],
),
Container(height: 1, color: const Color(0xFF232845)),
Row(
children: [
Expanded(child: _buildGridCell(3)),
Container(width: 1, height: 80, color: const Color(0xFF232845)),
Expanded(child: _buildGridCell(6)),
Container(width: 1, height: 80, color: const Color(0xFF232845)),
Expanded(child: _buildGridCell(9)),
],
),
],
),
),
],
);
}
Widget _buildGridCell(int cell) {
final count = _counts[cell] ?? 0;
final isSelected = _selectedCell == cell;
final name = _getCellShortName(cell);
return InkWell(
onTap: () {
setState(() => _selectedCell = cell);
HapticFeedback.selectionClick();
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4),
decoration: BoxDecoration(
color: isSelected ? const Color(0xFFFFD54F).withValues(alpha: 0.12) : Colors.transparent,
border: isSelected ? Border.all(color: const Color(0xFFFFD54F), width: 1.5) : null,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
name,
style: TextStyle(
color: isSelected ? const Color(0xFFFFD54F) : const Color(0xFF8C93B8),
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
count > 0 ? List.filled(count, cell).join() : '—',
style: TextStyle(
color: count > 0 ? Colors.white : const Color(0xFF4B5563),
fontSize: count > 3 ? 15 : 17,
fontWeight: FontWeight.w900,
letterSpacing: 1.2,
),
),
const SizedBox(height: 2),
Text(
count == 0 ? 'нет' : '$count шт.',
style: TextStyle(
color: count > 0 ? const Color(0xFF80DEEA) : const Color(0xFF6B7280),
fontSize: 9,
),
),
],
),
),
);
}
Widget _buildCellDetailCard(int cell, WordItem? word) {
final count = _counts[cell] ?? 0;
final name = _getCellName(cell);
final lore = _getCellLore(cell, count);
final summary = _getCellQualitySummary(cell, count);
return Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: const Color(0xFF141729),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.4)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: const Color(0xFF232845),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: const Color(0xFFFFD54F).withValues(alpha: 0.4)),
),
child: Text(
'$cell',
style: const TextStyle(color: Color(0xFFFFD54F), fontWeight: FontWeight.bold, fontSize: 16),
),
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Ячейка $cell: $name',
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14),
),
Text(
'Количество: ${count > 0 ? List.filled(count, cell).join() : "нет"} ($summary)',
style: const TextStyle(color: Color(0xFF80DEEA), fontSize: 11.5),
),
],
),
),
],
),
const SizedBox(height: 10),
Text(
lore,
style: const TextStyle(color: Color(0xFFCFD8DC), fontSize: 12.5, height: 1.4),
),
if (word != null) ...[
const Divider(color: Color(0xFF232845), height: 18),
InkWell(
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => SymbolDetailScreen(word: word)),
),
child: Row(
children: [
const Icon(Icons.link_rounded, color: Color(0xFFFFD54F), size: 16),
const SizedBox(width: 6),
Text(
'Связанный знак 999: #${word.formattedSessionNumber} «${word.word.toUpperCase()}»',
style: const TextStyle(color: Color(0xFFFFD54F), fontSize: 11.5, fontWeight: FontWeight.bold),
),
const Spacer(),
const Icon(Icons.arrow_forward_ios_rounded, size: 12, color: Color(0xFF8C93B8)),
],
),
),
],
],
),
);
}
Widget _buildLinesAnalysis() {
final c = _counts;
final r1 = (c[1] ?? 0) + (c[4] ?? 0) + (c[7] ?? 0); // Целеустремленность
final r2 = (c[2] ?? 0) + (c[5] ?? 0) + (c[8] ?? 0); // Семья
final r3 = (c[3] ?? 0) + (c[6] ?? 0) + (c[9] ?? 0); // Стабильность
final col1 = (c[1] ?? 0) + (c[2] ?? 0) + (c[3] ?? 0); // Самооценка
final col2 = (c[4] ?? 0) + (c[5] ?? 0) + (c[6] ?? 0); // Быт и Финансы
final col3 = (c[7] ?? 0) + (c[8] ?? 0) + (c[9] ?? 0); // Талант
final d1 = (c[1] ?? 0) + (c[5] ?? 0) + (c[9] ?? 0); // Духовность
final d2 = (c[3] ?? 0) + (c[5] ?? 0) + (c[7] ?? 0); // Темперамент
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.tune_rounded, size: 16, color: Color(0xFFFFD54F)),
SizedBox(width: 6),
Text(
'БАЛАНС ЛИНИЙ И СИЛ МАТРИЦЫ',
style: TextStyle(color: Color(0xFFFFD54F), fontSize: 11, fontWeight: FontWeight.bold, letterSpacing: 0.8),
),
],
),
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFF141729),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: const Color(0xFF232845)),
),
child: Column(
children: [
_buildLineBar('Строка 1 (1-4-7): Целеустремленность', r1, 5),
_buildLineBar('Строка 2 (2-5-8): Качество семьянина', r2, 5),
_buildLineBar('Строка 3 (3-6-9): Стабильность и уклад', r3, 5),
const Divider(color: Color(0xFF232845), height: 14),
_buildLineBar('Столбец 1 (1-2-3): Самооценка и эго', col1, 5),
_buildLineBar('Столбец 2 (4-5-6): Быт и заработок', col2, 5),
_buildLineBar('Столбец 3 (7-8-9): Внутренний талант', col3, 5),
const Divider(color: Color(0xFF232845), height: 14),
_buildLineBar('Диагональ 1 (1-5-9): Духовный вектор', d1, 5),
_buildLineBar('Диагональ 2 (3-5-7): Темперамент', d2, 5),
],
),
),
],
);
}
Widget _buildLineBar(String title, int count, int normalMax) {
final ratio = (count / normalMax).clamp(0.0, 1.0);
final color = count == 0
? const Color(0xFF6B7280)
: (count < 3
? const Color(0xFF4DD0E1)
: (count <= 5 ? const Color(0xFF81C784) : const Color(0xFFFFB74D)));
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
Expanded(
flex: 6,
child: Text(
title,
style: const TextStyle(color: Color(0xFFECEFF1), fontSize: 11),
),
),
Expanded(
flex: 3,
child: ClipRRect(
borderRadius: BorderRadius.circular(4),
child: LinearProgressIndicator(
value: ratio,
minHeight: 6,
backgroundColor: const Color(0xFF1E2238),
valueColor: AlwaysStoppedAnimation(color),
),
),
),
const SizedBox(width: 8),
SizedBox(
width: 32,
child: Text(
'$count ц.',
textAlign: TextAlign.right,
style: TextStyle(color: color, fontSize: 11, fontWeight: FontWeight.bold),
),
),
],
),
);
}
// --- String Helpers for Matrix Interpretation ---
String _getCellShortName(int cell) {
switch (cell) {
case 1: return '1. Характер';
case 2: return '2. Энергия';
case 3: return '3. Интерес';
case 4: return '4. Здоровье';
case 5: return '5. Логика';
case 6: return '6. Труд';
case 7: return '7. Удача';
case 8: return '8. Долг';
case 9: return '9. Память';
default: return '$cell';
}
}
String _getCellName(int cell) {
switch (cell) {
case 1: return 'Характер, воля и эго';
case 2: return 'Энергия действия и биополе';
case 3: return 'Интерес, наука и творчество';
case 4: return 'Здоровье, тело и витальность';
case 5: return 'Логика, интуиция и расчёт';
case 6: return 'Труд, заземление и ремесло';
case 7: return 'Удача, везение и ангел-хранитель';
case 8: return 'Долг, ответственность и совесть';
case 9: return 'Память, ум и проницательность';
default: return 'Стихия $cell';
}
}
String _getCellQualitySummary(int cell, int count) {
if (count == 0) return 'Качество не дано от рождения, зона наработки';
if (count == 1) return 'Ослабленное качество, требует осознанных усилий';
if (count == 2) return 'Норма качества, гармоничное проявление';
if (count == 3) return 'Переменчивость («хочу — проявлю, хочу — нет»)';
if (count == 4) return 'Сильное проявление, яркая доминанта';
return 'Гипер-потенциал, требует сублимации во избежание кризиса';
}
String _getCellLore(int cell, int count) {
switch (cell) {
case 1:
if (count == 1) return '«1» — Мягкий характер, стремление к компромиссам, уступчивость, иногда нехватка напора.';
if (count == 2) return '«11» — Гармоничный характер, общительность, умение выслушать других и отстоять свои границы.';
if (count == 3) return '«111» — Золотая середина: лидер, умеющий подстраиваться под коллектив и брать ответственность.';
if (count == 4) return '«1111» — Волевой лидер, несгибаемый стержень, командирские качества, не терпит диктата.';
return '«11111+» — Властный характер, мощный эго-потенциал. Опасность авторитаризма и перенапряжения.';
case 2:
if (count == 0) return '«нет двоек» — Дефицит собственной энергии. Забор сил извне, быстрая утомляемость, нужна подзарядка через природу и спорт.';
if (count == 1) return '«2» — Дефицитная энергия: хватает на текущие задачи, но рывки вызывают истощение. Избегайте стрессов.';
if (count == 2) return '«22» — Норма энергии: оптимальный баланс для работы, общения и гармоничной жизни.';
if (count == 3) return '«222» — Экстрасенсорный потенциал: всплески энергии, способность заряжать окружающих.';
return '«2222+» — Донор энергии: мощный витальный заряд, природный магнетизм. Важно сбрасывать избыток через физический труд.';
case 3:
if (count == 0) return '«нет троек» — Гуманитарный склад ума, склонность к искусству, педантичность в порядке.';
if (count == 1) return '«3» — Способности к точным или гуманитарным наукам зависят от личного настроения.';
if (count == 2) return '«33» — Аналитический ум, способность к наукам, технике, исследовательской работе.';
return '«333+» — Врождённый изобретатель, жажда познания, нестандартный творческий интеллект.';
case 4:
if (count == 0) return '«нет четверок» — Слабое от природы тело, необходимо сознательно укреплять здоровье и режим.';
if (count == 1) return '«4» — Нормальное здоровье, требует регулярной профилактики и заботы.';
return '«44+» — Крепкий физический иммунитет, высокая выносливость, спортивный потенциал.';
case 5:
if (count == 0) return '«нет пятерок» — Практическое мышление, ориентация на опыт. Интуиция включается редко.';
if (count == 1) return '«5» — Развитая интуиция, умение предчувствовать события и просчитывать шаги.';
if (count == 2) return '«55» — Сильная интуиция и логика, дар прогнозирования, минимизация ошибок.';
return '«555+» — Ясновидение, глубокое проникновение в скрытые взаимосвязи мира.';
case 6:
if (count == 0) return '«нет шестерок» — Нелюбовь к монотонному физическому труду, тяга к творчеству и мысли.';
if (count == 1) return '«6» — Труд по настроению: работа руками требует вдохновения.';
if (count == 2) return '«66» — «Золотые руки», заземление, практичность, высокое мастерство ремесленника.';
return '«666+» — Предельная материальная концентрация, манипуляция плотными формами.';
case 7:
if (count == 0) return '«нет семерок» — Удача зарабатывается исключительно собственным трудом без подарков судьбы.';
if (count == 1) return '«7» — Небольшое везение, защита в критических ситуациях, скрытый талант.';
if (count == 2) return '«77» — Баловень судьбы, открытые пути, интуитивное избегание катастроф.';
return '«777+» — Особая кармическая миссия, знак высшего покровительства.';
case 8:
if (count == 0) return '«нет восьмерок» — Ослабленное чувство долга: человек никому ничем не обязан, важна личная свобода.';
if (count == 1) return '«8» — Развитое чувство долга, забота о близких, честность и надежность.';
if (count == 2) return '«88» — Врождённая справедливость, готовность служить людям, преданность слову.';
return '«888+» — Самоотверженное служение, кармический долг перед родом или народом.';
case 9:
if (count == 1) return '«9» — Память требует тренировки, забывчивость к мелочам при фокусе на главном.';
if (count == 2) return '«99» — Отличная память, быстрая обучаемость, ясный и цепкий аналитический ум.';
if (count == 3) return '«999» — Блестящий интеллект, высокая скорость усвоения любой информации.';
return '«9999+» — Гениальность восприятия, проницательность, опасность перегрузки рассудка.';
default:
return 'Стихия $cell: числовой архетип матрицы.';
}
}
String _getLifePathTitle(int lp) {
switch (lp) {
case 1: return '1 — Первопроходец и Лидер (Монада)';
case 2: return '2 — Дипломат и Миротворец (Дуада)';
case 3: return '3 — Творец и Вдохновитель (Триада)';
case 4: return '4 — Строитель и Хранитель Порядка (Тетрада)';
case 5: return '5 — Искатель Свободы и Странник (Пентада)';
case 6: return '6 — Наставник и Хранитель Очага (Гексада)';
case 7: return '7 — Философ и Искатель Истины (Септенер)';
case 8: return '8 — Владыка Материи и Стратег (Октава)';
case 9: return '9 — Мудрец и Гуманист (Эннеада)';
case 11: return '11 — Духовный Проводник (Мастер-Число)';
case 22: return '22 — Великий Архитектор (Мастер-Число)';
case 33: return '33 — Аватар Сострадания (Мастер-Число)';
default: return '$lp — Сакральное Число';
}
}
String _getLifePathLore(int lp) {
switch (lp) {
case 1:
return 'Ваш путь — путь первопроходца, инициатора и лидера. Вы призваны прокладывать новые тропы, опираться на независимость духа и вести за собой.';
case 2:
return 'Ваш путь — гармонизация противоположностей, тонкая дипломатия, сотрудничество и интуитивное исцеление разобщенности.';
case 3:
return 'Ваш путь — самовыражение через слово, образ, радость и творчество. Вы пробуждаете мир от серости и косности.';
case 4:
return 'Ваш путь — созидание несокрушимого фундамента, дисциплина, честный труд и превращение хаоса в устойчивую структуру.';
case 5:
return 'Ваш путь — динамические перемены, расширение горизонтов, путешествия, разрушение отживших догм и свобода опыта.';
case 6:
return 'Ваш путь — служение любви, воспитание, забота о семье и гармонизация пространства вокруг себя.';
case 7:
return 'Ваш путь — глубинное исследование тайн мироздания, медитация, наука и отделение непреходящей истины от иллюзий.';
case 8:
return 'Ваш путь — овладение законами материального изобилия, масштабное управление ресурсами и восстановление кармической справедливости.';
case 9:
return 'Ваш путь — безусловное сострадание, завершение старых циклов, космическая мудрость и служение благу всего человечества.';
case 11:
return 'Мастер-визионер. Вы обладаете прямым каналом озарения свыше. Ваша задача — нести духовный свет и вдохновлять людей на пробуждение.';
case 22:
return 'Мастер-строитель. Способность превращать самые смелые идеалистические мечты в осязаемые материальные системы планетарного масштаба.';
case 33:
return 'Мастер духовного служения. Высшее сострадание, самоотдача и исцеление сердец через чистую любовь.';
default:
return 'Сакральный вектор вашего воплощения.';
}
}
}