feat(App): update Finlytic Flutter app UI and blocs
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../../core/theme/app_theme.dart';
|
||||
|
||||
class CandleData {
|
||||
final DateTime time;
|
||||
final double open;
|
||||
final double high;
|
||||
final double low;
|
||||
final double close;
|
||||
|
||||
CandleData({
|
||||
required this.time,
|
||||
required this.open,
|
||||
required this.high,
|
||||
required this.low,
|
||||
required this.close,
|
||||
});
|
||||
|
||||
factory CandleData.fromJson(Map<String, dynamic> json) {
|
||||
return CandleData(
|
||||
time: json['timestamp'] != null ? DateTime.parse(json['timestamp'].toString()) : DateTime.now(),
|
||||
open: (json['open'] as num? ?? 0.0).toDouble(),
|
||||
high: (json['high'] as num? ?? 0.0).toDouble(),
|
||||
low: (json['low'] as num? ?? 0.0).toDouble(),
|
||||
close: (json['close'] as num? ?? 0.0).toDouble(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CandleChartWidget extends StatelessWidget {
|
||||
final List<CandleData> candles;
|
||||
final double? supportLevel;
|
||||
final double? resistanceLevel;
|
||||
|
||||
const CandleChartWidget({
|
||||
super.key,
|
||||
this.candles = const [],
|
||||
this.supportLevel,
|
||||
this.resistanceLevel,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (candles.isEmpty) {
|
||||
return Container(
|
||||
color: AppTheme.cardSurface,
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.show_chart, color: AppTheme.textMuted, size: 48),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'Keine Candlestick-Daten verfgbar',
|
||||
style: TextStyle(color: AppTheme.textMuted, fontSize: 13),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Container(
|
||||
color: Colors.black.withValues(alpha: 0.6),
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: CustomPaint(
|
||||
painter: _CandlePainter(
|
||||
candles: candles,
|
||||
supportLevel: supportLevel,
|
||||
resistanceLevel: resistanceLevel,
|
||||
),
|
||||
child: Container(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CandlePainter extends CustomPainter {
|
||||
final List<CandleData> candles;
|
||||
final double? supportLevel;
|
||||
final double? resistanceLevel;
|
||||
|
||||
_CandlePainter({
|
||||
required this.candles,
|
||||
this.supportLevel,
|
||||
this.resistanceLevel,
|
||||
});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
if (candles.isEmpty) return;
|
||||
|
||||
double minPrice = candles.first.low;
|
||||
double maxPrice = candles.first.high;
|
||||
for (var c in candles) {
|
||||
if (c.low < minPrice) minPrice = c.low;
|
||||
if (c.high > maxPrice) maxPrice = c.high;
|
||||
}
|
||||
|
||||
if (supportLevel != null && supportLevel! < minPrice) minPrice = supportLevel!;
|
||||
if (resistanceLevel != null && resistanceLevel! > maxPrice) maxPrice = resistanceLevel!;
|
||||
|
||||
final priceRange = (maxPrice - minPrice) == 0 ? 1.0 : (maxPrice - minPrice);
|
||||
final padding = size.height * 0.05;
|
||||
final usableHeight = size.height - (padding * 2);
|
||||
|
||||
double getY(double price) {
|
||||
final normalized = (price - minPrice) / priceRange;
|
||||
return size.height - padding - (normalized * usableHeight);
|
||||
}
|
||||
|
||||
// Gridlines
|
||||
final gridPaint = Paint()
|
||||
..color = Colors.white10
|
||||
..strokeWidth = 1;
|
||||
|
||||
for (int i = 1; i <= 4; i++) {
|
||||
final y = size.height * (i / 5);
|
||||
canvas.drawLine(Offset(0, y), Offset(size.width, y), gridPaint);
|
||||
}
|
||||
|
||||
// Support Line
|
||||
if (supportLevel != null) {
|
||||
final supPaint = Paint()
|
||||
..color = AppTheme.primaryEmerald.withValues(alpha: 0.6)
|
||||
..strokeWidth = 1.5
|
||||
..style = PaintingStyle.stroke;
|
||||
final y = getY(supportLevel!);
|
||||
canvas.drawLine(Offset(0, y), Offset(size.width, y), supPaint);
|
||||
}
|
||||
|
||||
// Resistance Line
|
||||
if (resistanceLevel != null) {
|
||||
final resPaint = Paint()
|
||||
..color = AppTheme.accentRed.withValues(alpha: 0.6)
|
||||
..strokeWidth = 1.5
|
||||
..style = PaintingStyle.stroke;
|
||||
final y = getY(resistanceLevel!);
|
||||
canvas.drawLine(Offset(0, y), Offset(size.width, y), resPaint);
|
||||
}
|
||||
|
||||
// Candlesticks
|
||||
final candleWidth = (size.width / candles.length) * 0.7;
|
||||
final candleSpacing = size.width / candles.length;
|
||||
|
||||
for (int i = 0; i < candles.length; i++) {
|
||||
final candle = candles[i];
|
||||
final x = (i * candleSpacing) + (candleSpacing / 2);
|
||||
final isBullish = candle.close >= candle.open;
|
||||
final candleColor = isBullish ? AppTheme.primaryEmerald : AppTheme.accentRed;
|
||||
|
||||
final wickPaint = Paint()
|
||||
..color = candleColor
|
||||
..strokeWidth = 1.5;
|
||||
|
||||
final highY = getY(candle.high);
|
||||
final lowY = getY(candle.low);
|
||||
canvas.drawLine(Offset(x, highY), Offset(x, lowY), wickPaint);
|
||||
|
||||
final openY = getY(candle.open);
|
||||
final closeY = getY(candle.close);
|
||||
final topY = openY < closeY ? openY : closeY;
|
||||
final bodyHeight = (openY - closeY).abs();
|
||||
|
||||
final bodyPaint = Paint()
|
||||
..color = candleColor
|
||||
..style = PaintingStyle.fill;
|
||||
|
||||
canvas.drawRect(
|
||||
Rect.fromLTWH(
|
||||
x - (candleWidth / 2),
|
||||
topY,
|
||||
candleWidth,
|
||||
bodyHeight < 1 ? 1 : bodyHeight,
|
||||
),
|
||||
bodyPaint,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant _CandlePainter oldDelegate) => true;
|
||||
}
|
||||
Reference in New Issue
Block a user