feat(fundamentals): preserve ticker selection and add deterministic key executives sorting
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
import '../../favorites/models/favorite_asset_model.dart';
|
||||
import '../models/fundamental_data_model.dart';
|
||||
|
||||
class TickerResolver {
|
||||
/// Resolves the optimal ticker according to user prioritization:
|
||||
/// 1. Candidate / active user-selected symbol (if valid and not equal to ISIN)
|
||||
/// 2. Favorite selected ticker (if asset is in favorites and has a valid ticker)
|
||||
/// 3. Primary Ticker (from Fundamentals header)
|
||||
/// 4. First available ticker from AvailableTickers list
|
||||
/// 5. ISIN fallback
|
||||
static String? resolve({
|
||||
required String isin,
|
||||
String? candidateSymbol,
|
||||
List<FavoriteAssetModel>? favoriteDetails,
|
||||
FundamentalDataModel? fundamentals,
|
||||
}) {
|
||||
final cleanIsin = isin.trim().toUpperCase();
|
||||
|
||||
// 1. Check Candidate / User-selected symbol
|
||||
if (candidateSymbol != null && candidateSymbol.trim().isNotEmpty) {
|
||||
final cClean = candidateSymbol.trim();
|
||||
if (cClean.toUpperCase() != cleanIsin) {
|
||||
return cClean;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Check Favorite selected ticker
|
||||
if (favoriteDetails != null && favoriteDetails.isNotEmpty) {
|
||||
for (final f in favoriteDetails) {
|
||||
final fIsin = f.isin.trim().toUpperCase();
|
||||
final fSym = f.symbol.trim().toUpperCase();
|
||||
if (fIsin == cleanIsin || fSym == cleanIsin) {
|
||||
if (f.symbol.trim().isNotEmpty && f.symbol.trim().toUpperCase() != cleanIsin) {
|
||||
return f.symbol.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Check Fundamentals Primary Ticker
|
||||
if (fundamentals != null) {
|
||||
final primary = fundamentals.primaryTicker.trim();
|
||||
if (primary.isNotEmpty && primary.toUpperCase() != cleanIsin) {
|
||||
return primary;
|
||||
}
|
||||
|
||||
final fTicker = fundamentals.ticker.trim();
|
||||
if (fTicker.isNotEmpty && fTicker.toUpperCase() != cleanIsin) {
|
||||
return fTicker;
|
||||
}
|
||||
|
||||
// 4. First available ticker in availableTickers
|
||||
for (final t in fundamentals.availableTickers) {
|
||||
final tTick = t.ticker.trim();
|
||||
if (tTick.isNotEmpty && tTick.toUpperCase() != cleanIsin) {
|
||||
return tTick;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Fallback: ISIN
|
||||
return isin.trim().isNotEmpty ? isin.trim() : null;
|
||||
}
|
||||
}
|
||||
+45
-14
@@ -4,10 +4,12 @@ import '../../../../core/theme/app_theme.dart';
|
||||
import '../../../favorites/cubit/favorites_cubit.dart';
|
||||
import '../../bloc/fundamentals/asset_fundamentals_bloc.dart';
|
||||
import '../../bloc/fundamentals/asset_fundamentals_event.dart';
|
||||
import '../../bloc/fundamentals/asset_fundamentals_state.dart';
|
||||
import '../../bloc/technical/asset_technical_bloc.dart';
|
||||
import '../../bloc/technical/asset_technical_event.dart';
|
||||
import '../../bloc/trades/asset_trades_bloc.dart';
|
||||
import '../../bloc/trades/asset_trades_event.dart';
|
||||
import '../../utils/ticker_resolver.dart';
|
||||
import '../../widgets/header/asset_hero_header.dart';
|
||||
import '../tabs/fundamentals_tab.dart';
|
||||
import '../tabs/technical_tab.dart';
|
||||
@@ -74,19 +76,48 @@ class _AssetPageDesktopLayoutState extends State<AssetPageDesktopLayout>
|
||||
Widget build(BuildContext context) {
|
||||
final theme = AppTheme.activePreset;
|
||||
|
||||
return SingleChildScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 1. Hero Header
|
||||
AssetHeroHeader(
|
||||
isin: widget.isin,
|
||||
name: widget.name ?? widget.isin,
|
||||
symbol: _selectedTicker ?? widget.selectedTicker,
|
||||
onExchangeChanged: _handleExchangeChanged,
|
||||
onForceRefresh: _handleForceRefresh,
|
||||
),
|
||||
return BlocListener<AssetFundamentalsBloc, AssetFundamentalsState>(
|
||||
listener: (context, state) {
|
||||
if (state is AssetFundamentalsLoaded && state.data != null) {
|
||||
if (_selectedTicker == null ||
|
||||
_selectedTicker!.trim().isEmpty ||
|
||||
_selectedTicker!.trim().toUpperCase() == widget.isin.trim().toUpperCase()) {
|
||||
final favList = context.read<FavoritesCubit>().state.favoriteDetails;
|
||||
final bestTicker = TickerResolver.resolve(
|
||||
isin: widget.isin,
|
||||
candidateSymbol: widget.selectedTicker,
|
||||
favoriteDetails: favList,
|
||||
fundamentals: state.data,
|
||||
);
|
||||
|
||||
if (bestTicker != null &&
|
||||
bestTicker.isNotEmpty &&
|
||||
bestTicker.toUpperCase() != widget.isin.toUpperCase()) {
|
||||
setState(() {
|
||||
_selectedTicker = bestTicker;
|
||||
});
|
||||
context.read<AssetTechnicalBloc>().add(LoadAssetTechnical(
|
||||
widget.isin,
|
||||
ticker: bestTicker,
|
||||
forceRefresh: false,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
child: SingleChildScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 1. Hero Header
|
||||
AssetHeroHeader(
|
||||
isin: widget.isin,
|
||||
name: widget.name ?? widget.isin,
|
||||
symbol: _selectedTicker ?? widget.selectedTicker,
|
||||
onExchangeChanged: _handleExchangeChanged,
|
||||
onForceRefresh: _handleForceRefresh,
|
||||
),
|
||||
|
||||
// 2. Full-Width Interactive Chart Section
|
||||
Container(
|
||||
@@ -169,6 +200,6 @@ class _AssetPageDesktopLayoutState extends State<AssetPageDesktopLayout>
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
),
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+123
-91
@@ -4,10 +4,12 @@ import '../../../../core/theme/app_theme.dart';
|
||||
import '../../../favorites/cubit/favorites_cubit.dart';
|
||||
import '../../bloc/fundamentals/asset_fundamentals_bloc.dart';
|
||||
import '../../bloc/fundamentals/asset_fundamentals_event.dart';
|
||||
import '../../bloc/fundamentals/asset_fundamentals_state.dart';
|
||||
import '../../bloc/technical/asset_technical_bloc.dart';
|
||||
import '../../bloc/technical/asset_technical_event.dart';
|
||||
import '../../bloc/trades/asset_trades_bloc.dart';
|
||||
import '../../bloc/trades/asset_trades_event.dart';
|
||||
import '../../utils/ticker_resolver.dart';
|
||||
import '../../widgets/header/asset_hero_header.dart';
|
||||
import '../tabs/fundamentals_tab.dart';
|
||||
import '../tabs/technical_tab.dart';
|
||||
@@ -74,102 +76,132 @@ class _AssetPageMobileLayoutState extends State<AssetPageMobileLayout>
|
||||
Widget build(BuildContext context) {
|
||||
final theme = AppTheme.activePreset;
|
||||
|
||||
return SingleChildScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 1. Hero Header
|
||||
AssetHeroHeader(
|
||||
isin: widget.isin,
|
||||
name: widget.name ?? widget.isin,
|
||||
symbol: _selectedTicker ?? widget.selectedTicker,
|
||||
onExchangeChanged: _handleExchangeChanged,
|
||||
onForceRefresh: _handleForceRefresh,
|
||||
),
|
||||
|
||||
// 2. Interactive Chart
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.cardSurface,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: theme.glassBorder),
|
||||
),
|
||||
child: TechnicalTab(
|
||||
return BlocListener<AssetFundamentalsBloc, AssetFundamentalsState>(
|
||||
listener: (context, state) {
|
||||
if (state is AssetFundamentalsLoaded && state.data != null) {
|
||||
if (_selectedTicker == null ||
|
||||
_selectedTicker!.trim().isEmpty ||
|
||||
_selectedTicker!.trim().toUpperCase() == widget.isin.trim().toUpperCase()) {
|
||||
final favList = context.read<FavoritesCubit>().state.favoriteDetails;
|
||||
final bestTicker = TickerResolver.resolve(
|
||||
isin: widget.isin,
|
||||
symbol: _selectedTicker,
|
||||
showChartOnly: true,
|
||||
chartHeight: 320,
|
||||
),
|
||||
),
|
||||
candidateSymbol: widget.selectedTicker,
|
||||
favoriteDetails: favList,
|
||||
fundamentals: state.data,
|
||||
);
|
||||
|
||||
const SizedBox(height: 8),
|
||||
if (bestTicker != null &&
|
||||
bestTicker.isNotEmpty &&
|
||||
bestTicker.toUpperCase() != widget.isin.toUpperCase()) {
|
||||
setState(() {
|
||||
_selectedTicker = bestTicker;
|
||||
});
|
||||
context.read<AssetTechnicalBloc>().add(LoadAssetTechnical(
|
||||
widget.isin,
|
||||
ticker: bestTicker,
|
||||
forceRefresh: false,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
child: SingleChildScrollView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 1. Hero Header
|
||||
AssetHeroHeader(
|
||||
isin: widget.isin,
|
||||
name: widget.name ?? widget.isin,
|
||||
symbol: _selectedTicker ?? widget.selectedTicker,
|
||||
onExchangeChanged: _handleExchangeChanged,
|
||||
onForceRefresh: _handleForceRefresh,
|
||||
),
|
||||
|
||||
// 3. Tabbed Detailed Analysis
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.cardSurface,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: theme.glassBorder),
|
||||
// 2. Interactive Chart
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.cardSurface,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: theme.glassBorder),
|
||||
),
|
||||
child: TechnicalTab(
|
||||
isin: widget.isin,
|
||||
symbol: _selectedTicker,
|
||||
showChartOnly: true,
|
||||
chartHeight: 320,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TabBar(
|
||||
controller: _tabController,
|
||||
labelColor: theme.primaryColor,
|
||||
unselectedLabelColor: theme.textMuted,
|
||||
indicatorColor: theme.primaryColor,
|
||||
dividerColor: theme.glassBorder,
|
||||
isScrollable: true,
|
||||
tabAlignment: TabAlignment.start,
|
||||
labelStyle: const TextStyle(
|
||||
fontWeight: FontWeight.bold, fontSize: 12),
|
||||
tabs: const [
|
||||
Tab(
|
||||
icon: Icon(Icons.analytics_outlined, size: 16),
|
||||
text: 'FUNDAMENTALS'),
|
||||
Tab(
|
||||
icon: Icon(Icons.architecture_outlined, size: 16),
|
||||
text: 'MUSTER & SIGNALE'),
|
||||
Tab(
|
||||
icon: Icon(Icons.candlestick_chart_outlined, size: 16),
|
||||
text: 'TRADES'),
|
||||
],
|
||||
),
|
||||
AnimatedBuilder(
|
||||
animation: _tabController,
|
||||
builder: (context, _) {
|
||||
switch (_tabController.index) {
|
||||
case 0:
|
||||
return FundamentalsTab(
|
||||
isin: widget.isin,
|
||||
symbol: _selectedTicker,
|
||||
isEmbedded: true,
|
||||
);
|
||||
case 1:
|
||||
return TechnicalTab(
|
||||
isin: widget.isin,
|
||||
symbol: _selectedTicker,
|
||||
showDetailsOnly: true,
|
||||
);
|
||||
case 2:
|
||||
return SizedBox(
|
||||
height: 500,
|
||||
child: TradesTab(symbol: widget.isin),
|
||||
);
|
||||
default:
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// 3. Tabbed Detailed Analysis
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.cardSurface,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: theme.glassBorder),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
TabBar(
|
||||
controller: _tabController,
|
||||
isScrollable: true,
|
||||
tabAlignment: TabAlignment.start,
|
||||
labelColor: theme.primaryColor,
|
||||
unselectedLabelColor: theme.textSecondary,
|
||||
indicatorColor: theme.primaryColor,
|
||||
dividerColor: theme.glassBorder,
|
||||
labelStyle: const TextStyle(
|
||||
fontWeight: FontWeight.bold, fontSize: 13),
|
||||
tabs: const [
|
||||
Tab(
|
||||
icon: Icon(Icons.analytics_outlined, size: 18),
|
||||
text: 'ÜBERSICHT'),
|
||||
Tab(
|
||||
icon: Icon(Icons.architecture_outlined, size: 18),
|
||||
text: 'MUSTER'),
|
||||
Tab(
|
||||
icon:
|
||||
Icon(Icons.candlestick_chart_outlined, size: 18),
|
||||
text: 'TRADES'),
|
||||
],
|
||||
),
|
||||
AnimatedBuilder(
|
||||
animation: _tabController,
|
||||
builder: (context, _) {
|
||||
switch (_tabController.index) {
|
||||
case 0:
|
||||
return FundamentalsTab(
|
||||
isin: widget.isin,
|
||||
symbol: _selectedTicker,
|
||||
isEmbedded: true,
|
||||
);
|
||||
case 1:
|
||||
return TechnicalTab(
|
||||
isin: widget.isin,
|
||||
symbol: _selectedTicker,
|
||||
showDetailsOnly: true,
|
||||
);
|
||||
case 2:
|
||||
return SizedBox(
|
||||
height: 500,
|
||||
child: TradesTab(symbol: widget.isin),
|
||||
);
|
||||
default:
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ class AssetHeroHeader extends StatelessWidget {
|
||||
return BlocBuilder<AssetFundamentalsBloc, AssetFundamentalsState>(
|
||||
builder: (context, fundState) {
|
||||
String displayName = name;
|
||||
String primaryTicker = '';
|
||||
final String? logoUrl = isin.isNotEmpty ? '/api/v1/logo/$isin' : null;
|
||||
List<TickerModel> tickerOptions = [
|
||||
TickerModel(ticker: symbol ?? 'Loading', exchange: 'Loading', tradingCurrency: '', currentPrice: null)
|
||||
@@ -43,14 +44,21 @@ class AssetHeroHeader extends StatelessWidget {
|
||||
if (data.companyName.isNotEmpty) {
|
||||
displayName = data.companyName;
|
||||
}
|
||||
if (data.primaryTicker.isNotEmpty) {
|
||||
primaryTicker = data.primaryTicker;
|
||||
}
|
||||
if (data.availableTickers.isNotEmpty) {
|
||||
tickerOptions = data.availableTickers;
|
||||
}
|
||||
}
|
||||
|
||||
final selectedOption = tickerOptions.firstWhere(
|
||||
(t) => t.ticker == symbol || t.exchange == symbol,
|
||||
orElse: () => tickerOptions.first,
|
||||
(t) => (symbol != null && symbol!.isNotEmpty) &&
|
||||
(t.ticker.toLowerCase() == symbol!.toLowerCase() || (t.exchange != null && t.exchange!.toLowerCase() == symbol!.toLowerCase())),
|
||||
orElse: () => tickerOptions.firstWhere(
|
||||
(t) => primaryTicker.isNotEmpty && t.ticker.toLowerCase() == primaryTicker.toLowerCase(),
|
||||
orElse: () => tickerOptions.first,
|
||||
),
|
||||
);
|
||||
|
||||
return Container(
|
||||
@@ -203,73 +211,125 @@ class AssetHeroHeader extends StatelessWidget {
|
||||
);
|
||||
},
|
||||
),
|
||||
// Interactive Ticker & Exchange Selector Dropdown
|
||||
PopupMenuButton<String>(
|
||||
initialValue: selectedOption.ticker,
|
||||
tooltip: 'Select Exchange & Ticker',
|
||||
onSelected: (newTicker) {
|
||||
if (onExchangeChanged != null) {
|
||||
final opt = tickerOptions.firstWhere(
|
||||
(t) => t.ticker == newTicker,
|
||||
orElse: () => tickerOptions.first,
|
||||
);
|
||||
onExchangeChanged!(opt.exchange ?? 'Unknown', opt.ticker);
|
||||
}
|
||||
},
|
||||
itemBuilder: (context) {
|
||||
return tickerOptions.map((opt) {
|
||||
final ex = opt.exchange ?? 'Unknown';
|
||||
final tick = opt.ticker;
|
||||
final label = '$tick ($ex)';
|
||||
final isSelected = tick == symbol || ex == symbol;
|
||||
PopupMenuButton<String>(
|
||||
initialValue: selectedOption.ticker,
|
||||
tooltip: 'Börsenplatz & Ticker auswählen',
|
||||
color: theme.cardSurface,
|
||||
onSelected: (newTicker) {
|
||||
if (onExchangeChanged != null) {
|
||||
final opt = tickerOptions.firstWhere(
|
||||
(t) => t.ticker == newTicker,
|
||||
orElse: () => tickerOptions.first,
|
||||
);
|
||||
onExchangeChanged!(opt.exchange ?? 'Unknown', opt.ticker);
|
||||
}
|
||||
},
|
||||
itemBuilder: (context) {
|
||||
return tickerOptions.map((opt) {
|
||||
final ex = opt.exchange ?? 'Unknown';
|
||||
final tick = opt.ticker;
|
||||
final isPrimary = primaryTicker.isNotEmpty &&
|
||||
(tick.toLowerCase() == primaryTicker.toLowerCase());
|
||||
final isSelected = tick == symbol || ex == symbol;
|
||||
|
||||
return PopupMenuItem<String>(
|
||||
value: tick,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.business,
|
||||
size: 16,
|
||||
color: isSelected ? theme.primaryColor : theme.textMuted,
|
||||
return PopupMenuItem<String>(
|
||||
value: tick,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 2),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
isPrimary ? Icons.star_rounded : Icons.business,
|
||||
size: 18,
|
||||
color: isPrimary
|
||||
? AppTheme.accentCyan
|
||||
: (isSelected ? theme.primaryColor : theme.textMuted),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'$tick ($ex)',
|
||||
style: TextStyle(
|
||||
fontWeight: (isSelected || isPrimary) ? FontWeight.bold : FontWeight.normal,
|
||||
color: isSelected
|
||||
? theme.primaryColor
|
||||
: (isPrimary ? Colors.white : theme.textPrimary),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isPrimary) ...[
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: AppTheme.accentCyan.withValues(alpha: 0.18),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
border: Border.all(color: AppTheme.accentCyan.withValues(alpha: 0.5)),
|
||||
),
|
||||
child: Text(
|
||||
'PRIMARY',
|
||||
style: TextStyle(
|
||||
color: AppTheme.accentCyan,
|
||||
fontSize: 9,
|
||||
fontWeight: FontWeight.w900,
|
||||
letterSpacing: 0.6,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
||||
color: isSelected ? theme.primaryColor : theme.textPrimary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: theme.accentColor.withValues(alpha: 0.15),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(color: theme.accentColor.withValues(alpha: 0.4)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.business, size: 14, color: theme.accentColor),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
'${selectedOption.ticker} (${selectedOption.exchange ?? 'Unknown'})',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: theme.accentColor,
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: (primaryTicker.isNotEmpty && selectedOption.ticker.toLowerCase() == primaryTicker.toLowerCase())
|
||||
? AppTheme.accentCyan.withValues(alpha: 0.15)
|
||||
: theme.accentColor.withValues(alpha: 0.15),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(
|
||||
color: (primaryTicker.isNotEmpty && selectedOption.ticker.toLowerCase() == primaryTicker.toLowerCase())
|
||||
? AppTheme.accentCyan.withValues(alpha: 0.5)
|
||||
: theme.accentColor.withValues(alpha: 0.4),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Icon(Icons.arrow_drop_down, size: 16, color: theme.accentColor),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
(primaryTicker.isNotEmpty && selectedOption.ticker.toLowerCase() == primaryTicker.toLowerCase())
|
||||
? Icons.star_rounded
|
||||
: Icons.business,
|
||||
size: 15,
|
||||
color: (primaryTicker.isNotEmpty && selectedOption.ticker.toLowerCase() == primaryTicker.toLowerCase())
|
||||
? AppTheme.accentCyan
|
||||
: theme.accentColor,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
'${selectedOption.ticker} (${selectedOption.exchange ?? 'Unknown'})',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: (primaryTicker.isNotEmpty && selectedOption.ticker.toLowerCase() == primaryTicker.toLowerCase())
|
||||
? AppTheme.accentCyan
|
||||
: theme.accentColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Icon(
|
||||
Icons.arrow_drop_down,
|
||||
size: 16,
|
||||
color: (primaryTicker.isNotEmpty && selectedOption.ticker.toLowerCase() == primaryTicker.toLowerCase())
|
||||
? AppTheme.accentCyan
|
||||
: theme.accentColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user