111
This commit is contained in:
Binary file not shown.
@@ -7,26 +7,28 @@ class MarketProvider extends ChangeNotifier {
|
||||
final MarketService _marketService;
|
||||
|
||||
List<Coin> _allCoins = [];
|
||||
List<Coin> _filteredCoins = [];
|
||||
String _activeTab = 'all';
|
||||
String _searchKeyword = '';
|
||||
bool _isLoading = false;
|
||||
String? _error;
|
||||
bool _coinsLoaded = false; // 标记是否已加载
|
||||
bool _coinsLoaded = false;
|
||||
|
||||
MarketProvider(this._marketService);
|
||||
|
||||
// Getters
|
||||
List<Coin> get coins => _filteredCoins;
|
||||
List<Coin> get allCoins => _allCoins;
|
||||
bool get isLoading => _isLoading;
|
||||
String? get error => _error;
|
||||
String get activeTab => _activeTab;
|
||||
String get searchKeyword => _searchKeyword;
|
||||
|
||||
/// BTC 和 ETH(上半区展示)
|
||||
List<Coin> get featuredCoins =>
|
||||
_allCoins.where((c) => c.code == 'BTC' || c.code == 'ETH').toList();
|
||||
|
||||
/// 排除 BTC、ETH、USDT 的代币列表(下半区展示)
|
||||
List<Coin> get otherCoins => _allCoins
|
||||
.where((c) => !{'BTC', 'ETH', 'USDT'}.contains(c.code))
|
||||
.toList();
|
||||
|
||||
/// 加载币种列表
|
||||
Future<void> loadCoins({bool force = false}) async {
|
||||
// 如果已经加载过且不是强制刷新,则跳过
|
||||
if (_coinsLoaded && !force && _allCoins.isNotEmpty) {
|
||||
return;
|
||||
}
|
||||
@@ -40,7 +42,6 @@ class MarketProvider extends ChangeNotifier {
|
||||
|
||||
if (response.success) {
|
||||
_allCoins = response.data ?? [];
|
||||
_filterCoins();
|
||||
_coinsLoaded = true;
|
||||
} else {
|
||||
_error = response.message;
|
||||
@@ -53,49 +54,6 @@ class MarketProvider extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 设置分类标签
|
||||
void setTab(String tab) {
|
||||
_activeTab = tab;
|
||||
_filterCoins();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 搜索
|
||||
void search(String keyword) {
|
||||
_searchKeyword = keyword;
|
||||
_filterCoins();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 清除搜索
|
||||
void clearSearch() {
|
||||
_searchKeyword = '';
|
||||
_filterCoins();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// 筛选币种
|
||||
void _filterCoins() {
|
||||
List<Coin> result = List.from(_allCoins);
|
||||
|
||||
// 按分类筛选
|
||||
if (_activeTab == 'realtime') {
|
||||
result = result.where((c) => c.isRealtime).toList();
|
||||
} else if (_activeTab == 'hot') {
|
||||
result = result.take(6).toList();
|
||||
}
|
||||
|
||||
// 按关键词筛选
|
||||
if (_searchKeyword.isNotEmpty) {
|
||||
final kw = _searchKeyword.toLowerCase();
|
||||
result = result.where((c) =>
|
||||
c.code.toLowerCase().contains(kw) ||
|
||||
c.name.toLowerCase().contains(kw)).toList();
|
||||
}
|
||||
|
||||
_filteredCoins = result;
|
||||
}
|
||||
|
||||
/// 根据代码获取币种
|
||||
Coin? getCoinByCode(String code) {
|
||||
try {
|
||||
@@ -114,7 +72,6 @@ class MarketProvider extends ChangeNotifier {
|
||||
void resetLoadState() {
|
||||
_coinsLoaded = false;
|
||||
_allCoins = [];
|
||||
_filteredCoins = [];
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import '../../../providers/market_provider.dart';
|
||||
import '../../components/glass_panel.dart';
|
||||
import '../main/main_page.dart';
|
||||
|
||||
/// 行情页面 - Material Design 3 风格
|
||||
/// 行情页面
|
||||
class MarketPage extends StatefulWidget {
|
||||
const MarketPage({super.key});
|
||||
|
||||
@@ -17,9 +17,8 @@ class MarketPage extends StatefulWidget {
|
||||
State<MarketPage> createState() => _MarketPageState();
|
||||
}
|
||||
|
||||
class _MarketPageState extends State<MarketPage> with AutomaticKeepAliveClientMixin {
|
||||
final _searchController = TextEditingController();
|
||||
|
||||
class _MarketPageState extends State<MarketPage>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
@@ -31,196 +30,120 @@ class _MarketPageState extends State<MarketPage> with AutomaticKeepAliveClientMi
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: colorScheme.background,
|
||||
body: Consumer<MarketProvider>(
|
||||
builder: (context, provider, _) {
|
||||
return Column(
|
||||
children: [
|
||||
_buildSearchBar(provider, colorScheme),
|
||||
_buildTabs(provider, colorScheme, isDark),
|
||||
Expanded(
|
||||
child: _buildCoinList(provider, colorScheme, isDark),
|
||||
if (provider.isLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
if (provider.error != null) {
|
||||
return _buildErrorState(provider);
|
||||
}
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: () => provider.refresh(),
|
||||
color: colorScheme.primary,
|
||||
backgroundColor: colorScheme.surfaceContainerHighest,
|
||||
child: SingleChildScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
padding: AppSpacing.pagePadding,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 上半区:BTC + ETH 突出展示
|
||||
_buildFeaturedSection(provider),
|
||||
SizedBox(height: AppSpacing.lg),
|
||||
// 下半区标题
|
||||
Text(
|
||||
'代币列表',
|
||||
style: GoogleFonts.spaceGrotesk(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
SizedBox(height: AppSpacing.md),
|
||||
// 下半区:代币列表
|
||||
_buildCoinList(provider),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSearchBar(MarketProvider provider, ColorScheme colorScheme) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.fromLTRB(AppSpacing.md, AppSpacing.md, AppSpacing.md, 0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.surfaceContainerLowest,
|
||||
borderRadius: BorderRadius.circular(AppRadius.xl),
|
||||
border: Border.all(
|
||||
color: colorScheme.outlineVariant.withOpacity(0.15),
|
||||
),
|
||||
),
|
||||
child: TextField(
|
||||
controller: _searchController,
|
||||
onChanged: provider.search,
|
||||
style: TextStyle(color: colorScheme.onSurface),
|
||||
decoration: InputDecoration(
|
||||
hintText: '搜索市场...',
|
||||
hintStyle: TextStyle(color: colorScheme.onSurfaceVariant),
|
||||
prefixIcon: Icon(
|
||||
LucideIcons.search,
|
||||
size: 18,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
suffixIcon: _searchController.text.isNotEmpty
|
||||
? GestureDetector(
|
||||
onTap: () {
|
||||
_searchController.clear();
|
||||
provider.clearSearch();
|
||||
},
|
||||
child: Icon(
|
||||
LucideIcons.x,
|
||||
size: 18,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
vertical: AppSpacing.md + AppSpacing.xs,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
/// 上半区:BTC + ETH 大卡片
|
||||
Widget _buildFeaturedSection(MarketProvider provider) {
|
||||
final featured = provider.featuredCoins;
|
||||
if (featured.isEmpty) return const SizedBox.shrink();
|
||||
|
||||
final btc = featured.where((c) => c.code == 'BTC').firstOrNull;
|
||||
final eth = featured.where((c) => c.code == 'ETH').firstOrNull;
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
if (btc != null)
|
||||
Expanded(child: _FeaturedCard(coin: btc))
|
||||
else
|
||||
const Expanded(child: SizedBox.shrink()),
|
||||
SizedBox(width: AppSpacing.md),
|
||||
if (eth != null)
|
||||
Expanded(child: _FeaturedCard(coin: eth))
|
||||
else
|
||||
const Expanded(child: SizedBox.shrink()),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTabs(MarketProvider provider, ColorScheme colorScheme, bool isDark) {
|
||||
final tabs = [
|
||||
{'key': 'all', 'label': '全部'},
|
||||
{'key': 'realtime', 'label': '实时'},
|
||||
{'key': 'hot', 'label': '热门'},
|
||||
];
|
||||
/// 下半区:代币列表
|
||||
Widget _buildCoinList(MarketProvider provider) {
|
||||
final coins = provider.otherCoins;
|
||||
|
||||
return Container(
|
||||
height: 48,
|
||||
margin: EdgeInsets.fromLTRB(AppSpacing.md, AppSpacing.md, AppSpacing.md, 0),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: tabs.map((tab) {
|
||||
final isActive = provider.activeTab == tab['key'];
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => provider.setTab(tab['key']!),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
margin: EdgeInsets.only(right: AppSpacing.sm),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.lg,
|
||||
vertical: AppSpacing.sm + AppSpacing.xs,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: isActive
|
||||
? colorScheme.primary.withOpacity(0.1)
|
||||
: colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(AppRadius.full),
|
||||
border: isActive
|
||||
? Border.all(
|
||||
color: colorScheme.primary.withOpacity(0.2),
|
||||
)
|
||||
: null,
|
||||
boxShadow: isActive
|
||||
? [
|
||||
BoxShadow(
|
||||
color: colorScheme.primary.withOpacity(isDark ? 0.15 : 0.08),
|
||||
blurRadius: 15,
|
||||
),
|
||||
]
|
||||
: null,
|
||||
),
|
||||
child: Text(
|
||||
tab['label']!,
|
||||
style: TextStyle(
|
||||
color: isActive
|
||||
? colorScheme.primary
|
||||
: colorScheme.onSurfaceVariant,
|
||||
fontWeight: isActive ? FontWeight.w700 : FontWeight.normal,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCoinList(MarketProvider provider, ColorScheme colorScheme, bool isDark) {
|
||||
if (provider.isLoading) {
|
||||
return Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
if (coins.isEmpty) {
|
||||
return _EmptyState(
|
||||
icon: LucideIcons.coins,
|
||||
message: '暂无数据',
|
||||
onRetry: () => provider.refresh(),
|
||||
);
|
||||
}
|
||||
|
||||
if (provider.error != null) {
|
||||
return _buildErrorState(provider, colorScheme);
|
||||
}
|
||||
|
||||
final coins = provider.coins;
|
||||
if (coins.isEmpty) {
|
||||
return _buildEmptyState(colorScheme);
|
||||
}
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: provider.refresh,
|
||||
color: colorScheme.primary,
|
||||
backgroundColor: colorScheme.surfaceContainer,
|
||||
child: ListView.builder(
|
||||
padding: EdgeInsets.all(AppSpacing.md),
|
||||
itemCount: coins.length,
|
||||
itemBuilder: (context, index) => _buildCoinItem(coins[index], colorScheme),
|
||||
),
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: coins.length,
|
||||
separatorBuilder: (_, __) => SizedBox(height: AppSpacing.sm),
|
||||
itemBuilder: (context, index) => _CoinListItem(coin: coins[index]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildErrorState(MarketProvider provider, ColorScheme colorScheme) {
|
||||
/// 错误状态
|
||||
Widget _buildErrorState(MarketProvider provider) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: AppSpacing.pagePadding,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
LucideIcons.circleAlert,
|
||||
size: 48,
|
||||
color: colorScheme.error,
|
||||
),
|
||||
Icon(LucideIcons.circleAlert, size: 48, color: colorScheme.error),
|
||||
SizedBox(height: AppSpacing.md),
|
||||
Text(
|
||||
provider.error!,
|
||||
provider.error ?? '加载失败',
|
||||
style: TextStyle(color: colorScheme.error),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: AppSpacing.lg),
|
||||
SizedBox(height: AppSpacing.md),
|
||||
ShadButton(
|
||||
onPressed: provider.loadCoins,
|
||||
onPressed: () => provider.refresh(),
|
||||
child: const Text('重试'),
|
||||
),
|
||||
],
|
||||
@@ -228,57 +151,150 @@ class _MarketPageState extends State<MarketPage> with AutomaticKeepAliveClientMi
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildEmptyState(ColorScheme colorScheme) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
LucideIcons.coins,
|
||||
size: 48,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
SizedBox(height: AppSpacing.md),
|
||||
Text(
|
||||
'暂无数据',
|
||||
style: TextStyle(color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
],
|
||||
/// 上半区大卡片:BTC / ETH
|
||||
class _FeaturedCard extends StatelessWidget {
|
||||
final Coin coin;
|
||||
|
||||
const _FeaturedCard({required this.coin});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
final isUp = coin.isUp;
|
||||
final changeColor =
|
||||
isUp ? AppColorScheme.getUpColor(isDark) : AppColorScheme.down;
|
||||
final changeBgColor = isUp
|
||||
? AppColorScheme.getUpBackgroundColor(isDark)
|
||||
: colorScheme.error.withOpacity(0.1);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => _navigateToTrade(context),
|
||||
child: GlassPanel(
|
||||
padding: EdgeInsets.all(AppSpacing.lg),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 图标 + 币种代码
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 44,
|
||||
height: 44,
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.primary.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(AppRadius.xl),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
coin.displayIcon,
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: AppSpacing.sm),
|
||||
Expanded(
|
||||
child: Text(
|
||||
coin.code,
|
||||
style: GoogleFonts.spaceGrotesk(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: AppSpacing.md),
|
||||
// 当前价格
|
||||
Text(
|
||||
'\$${coin.formattedPrice}',
|
||||
style: GoogleFonts.spaceGrotesk(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
SizedBox(height: AppSpacing.xs),
|
||||
// 24h 涨跌幅
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.sm,
|
||||
vertical: AppSpacing.xs,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: changeBgColor,
|
||||
borderRadius: BorderRadius.circular(AppRadius.sm),
|
||||
border: Border.all(color: changeColor.withOpacity(0.2)),
|
||||
),
|
||||
child: Text(
|
||||
coin.formattedChange,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: changeColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCoinItem(Coin coin, ColorScheme colorScheme) {
|
||||
final changeColor = coin.isUp ? AppColorScheme.up : AppColorScheme.down;
|
||||
final changeBgColor = coin.isUp
|
||||
? AppColorScheme.up.withOpacity(0.1)
|
||||
void _navigateToTrade(BuildContext context) {
|
||||
final mainState = context.findAncestorStateOfType<MainPageState>();
|
||||
mainState?.switchToTrade(coin.code);
|
||||
}
|
||||
}
|
||||
|
||||
/// 下半区列表项
|
||||
class _CoinListItem extends StatelessWidget {
|
||||
final Coin coin;
|
||||
|
||||
const _CoinListItem({required this.coin});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
final isUp = coin.isUp;
|
||||
final changeColor =
|
||||
isUp ? AppColorScheme.getUpColor(isDark) : AppColorScheme.down;
|
||||
final changeBgColor = isUp
|
||||
? AppColorScheme.getUpBackgroundColor(isDark)
|
||||
: colorScheme.error.withOpacity(0.1);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => _navigateToTrade(coin),
|
||||
child: GlassCard(
|
||||
margin: EdgeInsets.only(bottom: AppSpacing.sm),
|
||||
onTap: () => _navigateToTrade(context),
|
||||
child: GlassPanel(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
vertical: AppSpacing.sm + AppSpacing.xs,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
// 图标容器
|
||||
// 币种图标
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
color: colorScheme.primary.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(AppRadius.xl),
|
||||
border: Border.all(
|
||||
color: colorScheme.outlineVariant.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
coin.displayIcon,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
color: coin.isUp ? colorScheme.primary : colorScheme.secondary,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -294,8 +310,8 @@ class _MarketPageState extends State<MarketPage> with AutomaticKeepAliveClientMi
|
||||
Text(
|
||||
coin.code,
|
||||
style: GoogleFonts.spaceGrotesk(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
@@ -303,13 +319,12 @@ class _MarketPageState extends State<MarketPage> with AutomaticKeepAliveClientMi
|
||||
Text(
|
||||
'/USDT',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontSize: 11,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: AppSpacing.xs / 2),
|
||||
Text(
|
||||
coin.name,
|
||||
style: TextStyle(
|
||||
@@ -328,22 +343,19 @@ class _MarketPageState extends State<MarketPage> with AutomaticKeepAliveClientMi
|
||||
'\$${coin.formattedPrice}',
|
||||
style: GoogleFonts.spaceGrotesk(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
SizedBox(height: AppSpacing.xs),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.sm,
|
||||
vertical: AppSpacing.xs,
|
||||
vertical: 2,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: changeBgColor,
|
||||
borderRadius: BorderRadius.circular(AppRadius.sm),
|
||||
border: Border.all(
|
||||
color: changeColor.withOpacity(0.2),
|
||||
),
|
||||
border: Border.all(color: changeColor.withOpacity(0.2)),
|
||||
),
|
||||
child: Text(
|
||||
coin.formattedChange,
|
||||
@@ -362,11 +374,45 @@ class _MarketPageState extends State<MarketPage> with AutomaticKeepAliveClientMi
|
||||
);
|
||||
}
|
||||
|
||||
void _navigateToTrade(Coin coin) {
|
||||
// 切换到交易页面并选中该币种
|
||||
MainPageState? mainPageState = context.findAncestorStateOfType<MainPageState>();
|
||||
if (mainPageState != null) {
|
||||
mainPageState.switchToTrade(coin.code);
|
||||
}
|
||||
void _navigateToTrade(BuildContext context) {
|
||||
final mainState = context.findAncestorStateOfType<MainPageState>();
|
||||
mainState?.switchToTrade(coin.code);
|
||||
}
|
||||
}
|
||||
|
||||
/// 空状态
|
||||
class _EmptyState extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String message;
|
||||
final VoidCallback? onRetry;
|
||||
|
||||
const _EmptyState({required this.icon, required this.message, this.onRetry});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(AppSpacing.xl),
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(icon, size: 48, color: colorScheme.onSurfaceVariant),
|
||||
SizedBox(height: AppSpacing.sm + AppSpacing.xs),
|
||||
Text(
|
||||
message,
|
||||
style: TextStyle(color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
if (onRetry != null) ...[
|
||||
SizedBox(height: AppSpacing.md),
|
||||
ShadButton(
|
||||
onPressed: onRetry,
|
||||
child: const Text('重试'),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user