import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../../core/theme/app_theme.dart'; import '../../../../core/widgets/glass_container.dart'; import '../../../../core/widgets/status_badge.dart'; import '../../bloc/technical/asset_technical_bloc.dart'; import '../../bloc/technical/asset_technical_event.dart'; import '../../bloc/technical/asset_technical_state.dart'; import '../../utils/metric_explanations.dart'; import '../../utils/pattern_explanations.dart'; import '../../widgets/chart/candlestick_chart.dart'; class TechnicalTab extends StatefulWidget { final String symbol; final bool isDesktopLeftPanel; const TechnicalTab({ super.key, required this.symbol, this.isDesktopLeftPanel = false, }); @override State createState() => _TechnicalTabState(); } class _TechnicalTabState extends State { bool _showSma50 = true; bool _showSma200 = true; bool _showEma = true; bool _showPatterns = true; bool _showSignals = true; bool _showSupertrend = true; // Set of disabled pattern indices for individual toggling final Set _disabledPatternIndices = {}; @override void initState() { super.initState(); context.read().add(LoadAssetTechnical(widget.symbol, forceRefresh: false)); } @override void didUpdateWidget(covariant TechnicalTab oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.symbol != widget.symbol) { context.read().add(LoadAssetTechnical(widget.symbol, forceRefresh: false)); } } @override Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { if (state is AssetTechnicalLoading) { return Center(child: CircularProgressIndicator(color: AppTheme.primaryEmerald)); } if (state is AssetTechnicalError) { return Center( child: GlassContainer( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.show_chart, color: AppTheme.accentRed, size: 48), const SizedBox(height: 12), Text('Fehler beim Laden der Technischen Analyse: ${state.message}', style: const TextStyle(color: Colors.white70)), const SizedBox(height: 16), ElevatedButton.icon( onPressed: () => context.read().add(LoadAssetTechnical(widget.symbol, forceRefresh: true)), icon: const Icon(Icons.refresh), label: const Text('Erneut versuchen'), ), ], ), ), ); } if (state is AssetTechnicalLoaded) { final data = state.data; List candles = []; List patterns = []; List signals = []; List indicators = []; if (data != null) { candles = data.candles.map((c) => CandleModel(time: c.timestamp, open: c.open, high: c.high, low: c.low, close: c.close, volume: c.volume)).toList(); patterns = []; // Since data.patterns is a List of Strings, we don't have point coordinates to draw them on the chart signals = data.signals.map((s) => StrategySignalModel(type: 'strategy', timestamp: s.date, direction: s.type, price: s.price, description: s.title)).toList(); indicators = data.indicators.map((i) => IndicatorModel(timestamp: i.timestamp, ema20: i.ema20, sma50: i.sma50, sma200: i.sma200, supertrendUpper: i.supertrendUpper, supertrendLower: i.supertrendLower, supertrendDirection: i.supertrendDirection)).toList(); } // Filter patterns according to individual checkbox states final activePatterns = [ for (int i = 0; i < patterns.length; i++) if (!_disabledPatternIndices.contains(i)) patterns[i] ]; return SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Glassmorphic Indicator & Pattern Control Ribbon GlassContainer( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: [ _buildIndicatorChip('EMA (20)', _showEma, (v) => setState(() => _showEma = v), Colors.blueAccent), const SizedBox(width: 6), _buildIndicatorChip('SMA (50)', _showSma50, (v) => setState(() => _showSma50 = v), Colors.orangeAccent), const SizedBox(width: 6), _buildIndicatorChip('SMA (200)', _showSma200, (v) => setState(() => _showSma200 = v), Colors.redAccent), const SizedBox(width: 6), _buildIndicatorChip('Supertrend', _showSupertrend, (v) => setState(() => _showSupertrend = v), AppTheme.primaryEmerald), const SizedBox(width: 6), _buildIndicatorChip('Alle Muster', _showPatterns, (v) => setState(() => _showPatterns = v), Colors.amberAccent), const SizedBox(width: 6), _buildIndicatorChip('Signale', _showSignals, (v) => setState(() => _showSignals = v), AppTheme.accentCyan), ], ), ), ), const SizedBox(height: 8), // Interactive Candlestick Chart SizedBox( height: 380, child: CandlestickChart( candles: candles, patterns: activePatterns, signals: signals, indicators: indicators, showPatterns: _showPatterns, showEma: _showEma, showSma50: _showSma50, showSma200: _showSma200, showSignals: _showSignals, showSupertrend: _showSupertrend, ), ), const SizedBox(height: 16), // Dedicated Chart Patterns & Signal Description List Section Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Icon(Icons.architecture_outlined, color: AppTheme.primaryEmerald, size: 20), const SizedBox(width: 8), const Text('Erkannte Chart-Muster & Signale', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white)), ], ), if (patterns.isNotEmpty) TextButton.icon( onPressed: () { setState(() { if (_disabledPatternIndices.length == patterns.length) { _disabledPatternIndices.clear(); } else { _disabledPatternIndices.addAll(List.generate(patterns.length, (i) => i)); } }); }, icon: Icon(_disabledPatternIndices.isEmpty ? Icons.deselect : Icons.select_all, size: 16, color: Colors.amberAccent), label: Text(_disabledPatternIndices.isEmpty ? 'Alle abwählen' : 'Alle anwählen', style: const TextStyle(color: Colors.amberAccent, fontSize: 12)), ), ], ), const SizedBox(height: 12), if (patterns.isEmpty && signals.isEmpty) GlassContainer( padding: const EdgeInsets.all(16), child: Center( child: Text('Zurzeit wurden keine akuten Formationen oder Strategie-Signale identifiziert.', style: TextStyle(color: AppTheme.textMuted, fontSize: 12)), ), ) else ...[ if (patterns.isNotEmpty) ...[ Text('Formationen & Trendlinien (Mit Checkbox im Chart schalten):', style: TextStyle(color: AppTheme.textSecondary, fontWeight: FontWeight.w600, fontSize: 13)), const SizedBox(height: 6), ...List.generate(patterns.length, (index) => _buildPatternCard(patterns[index], index)), const SizedBox(height: 12), ], if (signals.isNotEmpty) ...[ Text('Strategie-Signale:', style: TextStyle(color: AppTheme.textSecondary, fontWeight: FontWeight.w600, fontSize: 13)), const SizedBox(height: 6), ...signals.map((s) => _buildSignalCard(s)), ], ], ], ), ), const SizedBox(height: 16), ], ), ); } return Center( child: Text('Keine technisches Indikatoren verfügbar', style: TextStyle(color: AppTheme.textMuted)), ); }, ); } Widget _buildPatternCard(ChartPatternModel pattern, int index) { final isEnabled = !_disabledPatternIndices.contains(index); return Padding( padding: const EdgeInsets.only(bottom: 8), child: GlassContainer( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6), child: Row( children: [ // Checkbox for individual pattern toggling on the chart Checkbox( value: isEnabled, activeColor: Colors.amberAccent, checkColor: Colors.black, side: BorderSide(color: Colors.amberAccent.withValues(alpha: 0.6)), onChanged: (bool? val) { setState(() { if (val == true) { _disabledPatternIndices.remove(index); } else { _disabledPatternIndices.add(index); } }); }, ), Expanded( child: InkWell( onTap: () => PatternExplanations.showPatternDetails(context, pattern.type), borderRadius: BorderRadius.circular(8), child: Padding( padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 4), child: Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: isEnabled ? Colors.amberAccent.withValues(alpha: 0.15) : AppTheme.glassSurface, borderRadius: BorderRadius.circular(8), ), child: Icon(Icons.polyline_outlined, color: isEnabled ? Colors.amberAccent : AppTheme.textMuted, size: 20), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( pattern.type, style: TextStyle( fontWeight: FontWeight.bold, color: isEnabled ? Colors.white : AppTheme.textMuted, fontSize: 14, decoration: isEnabled ? null : TextDecoration.lineThrough, ), ), const SizedBox(width: 6), Icon(Icons.info_outline, size: 14, color: AppTheme.textMuted), ], ), const SizedBox(height: 4), Text( 'Formationspunkte: Oberer Trendkanal (${pattern.upperLine.length} Pkt.) / Unterer Trendkanal (${pattern.lowerLine.length} Pkt.)', style: TextStyle(color: AppTheme.textMuted, fontSize: 11), ), ], ), ), StatusBadge( label: isEnabled ? 'AKTIV' : 'AUS', color: isEnabled ? Colors.amberAccent : AppTheme.textMuted, ), ], ), ), ), ), ], ), ), ); } Widget _buildSignalCard(StrategySignalModel signal) { final isBuy = signal.type.toUpperCase() == 'BUY'; final color = isBuy ? AppTheme.primaryEmerald : AppTheme.accentRed; return Padding( padding: const EdgeInsets.only(bottom: 8), child: GlassContainer( padding: const EdgeInsets.all(12), child: Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: color.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(8), ), child: Icon(isBuy ? Icons.north_east : Icons.south_east, color: color, size: 20), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text(signal.type.toUpperCase(), style: TextStyle(fontWeight: FontWeight.bold, color: color, fontSize: 14)), const SizedBox(width: 8), Text('@ €${signal.price.toStringAsFixed(2)}', style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13)), ], ), const SizedBox(height: 4), Text(signal.description.isNotEmpty ? signal.description : 'Strategisches Kaufsignal ausgelöst durch technische Indikatoren.', style: TextStyle(color: AppTheme.textSecondary, fontSize: 12)), ], ), ), StatusBadge(label: 'SIGNAL', color: color), ], ), ), ); } Widget _buildIndicatorChip(String label, bool isSelected, ValueChanged onChanged, Color color) { return Row( mainAxisSize: MainAxisSize.min, children: [ FilterChip( selected: isSelected, label: Text(label, style: TextStyle(color: isSelected ? Colors.black : color, fontSize: 11, fontWeight: FontWeight.bold)), selectedColor: color, backgroundColor: color.withValues(alpha: 0.15), side: BorderSide(color: color.withValues(alpha: 0.4)), showCheckmark: false, onSelected: onChanged, ), InkWell( onTap: () => MetricExplanations.show(context, label), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 2), child: Icon(Icons.info_outline, size: 14, color: AppTheme.textMuted), ), ), ], ); } }