feat(app): responsive asset detail layout, full width chart, reactive hero header, shimmer loaders and enriched fundamentals

This commit is contained in:
2026-08-14 23:57:03 +02:00
parent f94e3b8164
commit 1d244b338a
22 changed files with 1950 additions and 1074 deletions
@@ -5,6 +5,8 @@ import '../../../../core/widgets/asset_logo_widget.dart';
import '../../../../shared/widgets/favorite_star_button.dart';
import '../../bloc/header/asset_header_bloc.dart';
import '../../bloc/header/asset_header_state.dart';
import '../../bloc/technical/asset_technical_bloc.dart';
import '../../bloc/technical/asset_technical_state.dart';
import '../../models/asset_model.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -146,45 +148,66 @@ class AssetHeroHeader extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'AKTUELLER PREIS',
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.bold,
color: theme.primaryColor,
letterSpacing: 1.5,
),
),
const SizedBox(height: 4),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
BlocBuilder<AssetTechnicalBloc, AssetTechnicalState>(
builder: (context, taState) {
double? livePrice = price;
String liveCurrency = selectedOption.tradingCurrency.isNotEmpty
? selectedOption.tradingCurrency
: currency;
if (taState is AssetTechnicalLoaded && taState.data != null) {
if (taState.data!.candles.isNotEmpty) {
final lastClose = taState.data!.candles.last.close;
if (lastClose > 0) {
livePrice = lastClose;
}
}
if (taState.data!.currency.isNotEmpty) {
liveCurrency = taState.data!.currency;
}
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SelectableText(
price != null && price > 0 ? price.toStringAsFixed(2) : '---',
Text(
'AKTUELLER PREIS',
style: TextStyle(
fontSize: 32,
fontSize: 11,
fontWeight: FontWeight.bold,
color: theme.textPrimary,
color: theme.primaryColor,
letterSpacing: 1.5,
),
),
const SizedBox(width: 6),
Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Text(
selectedOption.tradingCurrency.isNotEmpty ? selectedOption.tradingCurrency : currency,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: theme.primaryColor,
const SizedBox(height: 4),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
SelectableText(
livePrice != null && livePrice > 0 ? livePrice.toStringAsFixed(2) : '---',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: theme.textPrimary,
),
),
),
const SizedBox(width: 6),
Padding(
padding: const EdgeInsets.only(bottom: 4),
child: Text(
liveCurrency,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: theme.primaryColor,
),
),
),
],
),
],
),
],
);
},
),
// Interactive Ticker & Exchange Selector Dropdown
PopupMenuButton<String>(