111
This commit is contained in:
@@ -1,111 +1,111 @@
|
||||
/// API 端点配置
|
||||
/// API 端點配置
|
||||
class ApiEndpoints {
|
||||
ApiEndpoints._();
|
||||
|
||||
/// 环境类型
|
||||
/// 環境類型
|
||||
static const String _env = String.fromEnvironment('ENV', defaultValue: 'dev');
|
||||
|
||||
/// 基础URL - 根据环境自动切换
|
||||
/// 基礎URL - 根據環境自動切換
|
||||
static const String baseUrl = _env == 'prod'
|
||||
? 'http://8.155.172.147:5010'
|
||||
: 'http://localhost:5010';
|
||||
|
||||
/// 是否为生产环境
|
||||
/// 是否為生產環境
|
||||
static const bool isProduction = _env == 'prod';
|
||||
|
||||
// ==================== 用户模块 ====================
|
||||
// ==================== 用戶模塊 ====================
|
||||
|
||||
/// 用户登录
|
||||
/// 用戶登錄
|
||||
static const String login = '/api/user/login';
|
||||
|
||||
/// 用户注册
|
||||
/// 用戶註冊
|
||||
static const String register = '/api/user/register';
|
||||
|
||||
/// 获取用户信息
|
||||
/// 獲取用戶信息
|
||||
static const String userInfo = '/api/user/info';
|
||||
|
||||
/// 上传KYC资料
|
||||
/// 上傳KYC資料
|
||||
static const String kyc = '/api/user/kyc';
|
||||
|
||||
/// 获取推广码
|
||||
/// 獲取推廣碼
|
||||
static const String referralCode = '/api/user/referral-code';
|
||||
|
||||
/// 退出登录
|
||||
/// 退出登錄
|
||||
static const String logout = '/api/user/logout';
|
||||
|
||||
// ==================== 行情模块 ====================
|
||||
// ==================== 行情模塊 ====================
|
||||
|
||||
/// 获取币种列表
|
||||
/// 獲取幣種列表
|
||||
static const String coinList = '/api/market/list';
|
||||
|
||||
/// 获取币种详情
|
||||
/// 獲取幣種詳情
|
||||
static const String coinDetail = '/api/market/detail';
|
||||
|
||||
/// 搜索币种
|
||||
/// 搜索幣種
|
||||
static const String coinSearch = '/api/market/search';
|
||||
|
||||
// ==================== 交易模块 ====================
|
||||
// ==================== 交易模塊 ====================
|
||||
|
||||
/// 买入
|
||||
/// 買入
|
||||
static const String buy = '/api/trade/buy';
|
||||
|
||||
/// 卖出
|
||||
/// 賣出
|
||||
static const String sell = '/api/trade/sell';
|
||||
|
||||
/// 获取交易记录
|
||||
/// 獲取交易記錄
|
||||
static const String tradeOrders = '/api/trade/orders';
|
||||
|
||||
/// 获取订单详情
|
||||
/// 獲取訂單詳情
|
||||
static const String tradeOrderDetail = '/api/trade/order/detail';
|
||||
|
||||
// ==================== 资产模块 ====================
|
||||
// ==================== 資產模塊 ====================
|
||||
|
||||
/// 获取资产总览
|
||||
/// 獲取資產總覽
|
||||
static const String assetOverview = '/api/asset/overview';
|
||||
|
||||
/// 获取资金账户
|
||||
/// 獲取資金賬戶
|
||||
static const String fundAccount = '/api/asset/fund';
|
||||
|
||||
/// 获取交易账户
|
||||
/// 獲取交易賬戶
|
||||
static const String tradeAccount = '/api/asset/trade';
|
||||
|
||||
/// 资金划转
|
||||
/// 資金劃轉
|
||||
static const String transfer = '/api/asset/transfer';
|
||||
|
||||
/// 获取资金流水
|
||||
/// 獲取資金流水
|
||||
static const String assetFlow = '/api/asset/flow';
|
||||
|
||||
// ==================== 充提模块 ====================
|
||||
// ==================== 充提模塊 ====================
|
||||
|
||||
/// 申请充值
|
||||
/// 申請充值
|
||||
static const String deposit = '/api/fund/deposit';
|
||||
|
||||
/// 确认已打款
|
||||
/// 確認已打款
|
||||
static const String confirmPay = '/api/fund/confirmPay';
|
||||
|
||||
/// 申请提现
|
||||
/// 申請提現
|
||||
static const String withdraw = '/api/fund/withdraw';
|
||||
|
||||
/// 取消订单
|
||||
/// 取消訂單
|
||||
static const String cancelOrder = '/api/fund/cancel';
|
||||
|
||||
/// 获取可用提现网络列表
|
||||
/// 獲取可用提現網絡列表
|
||||
static const String walletNetworks = '/api/wallet/networks';
|
||||
|
||||
/// 获取充提记录
|
||||
/// 獲取充提記錄
|
||||
static const String fundOrders = '/api/fund/orders';
|
||||
|
||||
/// 获取默认钱包地址
|
||||
/// 獲取默認錢包地址
|
||||
static const String defaultWallet = '/api/wallet/default';
|
||||
|
||||
// ==================== 福利模块 ====================
|
||||
// ==================== 福利模塊 ====================
|
||||
|
||||
/// 福利中心状态
|
||||
/// 福利中心狀態
|
||||
static const String bonusWelfare = '/api/bonus/welfare';
|
||||
|
||||
/// 领取奖励
|
||||
/// 領取獎勵
|
||||
static const String bonusClaim = '/api/bonus/claim';
|
||||
|
||||
/// 每日盈亏
|
||||
/// 每日盈虧
|
||||
static const String dailyProfit = '/api/asset/daily-profit';
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import 'dart:async';
|
||||
|
||||
/// 应用事件类型
|
||||
/// 應用事件類型
|
||||
enum AppEventType {
|
||||
/// 资产变动(余额、持仓等)
|
||||
/// 資產變動(餘額、持倉等)
|
||||
assetChanged,
|
||||
|
||||
/// 订单变动(充提订单状态变化)
|
||||
/// 訂單變動(充提訂單狀態變化)
|
||||
orderChanged,
|
||||
}
|
||||
|
||||
/// 应用事件
|
||||
/// 應用事件
|
||||
class AppEvent {
|
||||
final AppEventType type;
|
||||
final Map<String, dynamic>? data;
|
||||
@@ -17,20 +17,20 @@ class AppEvent {
|
||||
const AppEvent(this.type, {this.data});
|
||||
}
|
||||
|
||||
/// 轻量级应用内事件总线
|
||||
/// 基于 StreamController.broadcast,零外部依赖
|
||||
/// 輕量級應用內事件總線
|
||||
/// 基於 StreamController.broadcast,零外部依賴
|
||||
class AppEventBus {
|
||||
final StreamController<AppEvent> _controller =
|
||||
StreamController<AppEvent>.broadcast();
|
||||
|
||||
/// 广播事件
|
||||
/// 廣播事件
|
||||
void fire(AppEventType type, {Map<String, dynamic>? data}) {
|
||||
if (!_controller.isClosed) {
|
||||
_controller.add(AppEvent(type, data: data));
|
||||
}
|
||||
}
|
||||
|
||||
/// 监听指定类型事件
|
||||
/// 監聽指定類型事件
|
||||
StreamSubscription<AppEvent> on(
|
||||
AppEventType type,
|
||||
void Function(AppEvent) callback,
|
||||
@@ -40,10 +40,10 @@ class AppEventBus {
|
||||
.listen(callback);
|
||||
}
|
||||
|
||||
/// 监听任意事件
|
||||
/// 監聽任意事件
|
||||
Stream<AppEvent> get stream => _controller.stream;
|
||||
|
||||
/// 销毁
|
||||
/// 銷燬
|
||||
void dispose() {
|
||||
_controller.close();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// API 异常类
|
||||
/// API 異常類
|
||||
class ApiException implements Exception {
|
||||
final String message;
|
||||
final String code;
|
||||
@@ -12,7 +12,7 @@ class ApiException implements Exception {
|
||||
|
||||
factory ApiException.unauthorized([String? message]) {
|
||||
return ApiException(
|
||||
message: message ?? '未授权',
|
||||
message: message ?? '未授權',
|
||||
code: '0002',
|
||||
statusCode: 401,
|
||||
);
|
||||
@@ -20,14 +20,14 @@ class ApiException implements Exception {
|
||||
|
||||
factory ApiException.networkError([String? message]) {
|
||||
return ApiException(
|
||||
message: message ?? '网络错误',
|
||||
message: message ?? '網絡錯誤',
|
||||
code: 'NETWORK_ERROR',
|
||||
);
|
||||
}
|
||||
|
||||
factory ApiException.serverError([String? message]) {
|
||||
return ApiException(
|
||||
message: message ?? '服务器错误',
|
||||
message: message ?? '服務器錯誤',
|
||||
code: 'SERVER_ERROR',
|
||||
statusCode: 500,
|
||||
);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/// API 响应状态码
|
||||
/// API 響應狀態碼
|
||||
class ResponseCode {
|
||||
static const String success = '0000';
|
||||
static const String unauthorized = '0002';
|
||||
static const String kycRequired = 'KYC_REQUIRED';
|
||||
}
|
||||
|
||||
/// API 响应模型
|
||||
/// API 響應模型
|
||||
class ApiResponse<T> {
|
||||
final bool success;
|
||||
final String? message;
|
||||
|
||||
@@ -5,7 +5,7 @@ import '../storage/local_storage.dart';
|
||||
import 'api_exception.dart';
|
||||
import 'api_response.dart';
|
||||
|
||||
/// 网络配置常量
|
||||
/// 網絡配置常量
|
||||
class NetworkConfig {
|
||||
static const String baseUrl = ApiEndpoints.baseUrl;
|
||||
static const Duration connectTimeout = Duration(seconds: 15);
|
||||
@@ -13,11 +13,11 @@ class NetworkConfig {
|
||||
static const Duration sendTimeout = Duration(seconds: 15);
|
||||
}
|
||||
|
||||
/// Dio 网络客户端
|
||||
/// Dio 網絡客戶端
|
||||
class DioClient {
|
||||
late final Dio _dio;
|
||||
|
||||
/// 未授权回调(token 过期时触发)
|
||||
/// 未授權回調(token 過期時觸發)
|
||||
VoidCallback? onUnauthorized;
|
||||
|
||||
DioClient() {
|
||||
@@ -43,7 +43,7 @@ class DioClient {
|
||||
]);
|
||||
}
|
||||
|
||||
/// GET 请求
|
||||
/// GET 請求
|
||||
Future<ApiResponse<T>> get<T>(
|
||||
String path, {
|
||||
Map<String, dynamic>? queryParameters,
|
||||
@@ -57,7 +57,7 @@ class DioClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// POST 请求
|
||||
/// POST 請求
|
||||
Future<ApiResponse<T>> post<T>(
|
||||
String path, {
|
||||
dynamic data,
|
||||
@@ -71,7 +71,7 @@ class DioClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// Multipart 文件上传
|
||||
/// Multipart 文件上傳
|
||||
Future<ApiResponse<T>> upload<T>(
|
||||
String path, {
|
||||
required FormData formData,
|
||||
@@ -96,21 +96,21 @@ class DioClient {
|
||||
final data = response.data;
|
||||
if (data is Map<String, dynamic>) {
|
||||
final apiResponse = ApiResponse.fromJson(data, fromJson);
|
||||
// 检测业务层未授权(后端返回 HTTP 200 + code "0002")
|
||||
// 注意:不再自动清除用户数据,避免误判
|
||||
// 只有在 HTTP 401 时才清除用户数据
|
||||
// 檢測業務層未授權(後端返回 HTTP 200 + code "0002")
|
||||
// 注意:不再自動清除用戶數據,避免誤判
|
||||
// 只有在 HTTP 401 時才清除用戶數據
|
||||
if (apiResponse.isUnauthorized) {
|
||||
debugPrint('业务层未授权响应: ${apiResponse.message}');
|
||||
// 不再自动调用 onUnauthorized,避免刷新时误判
|
||||
debugPrint('業務層未授權響應: ${apiResponse.message}');
|
||||
// 不再自動調用 onUnauthorized,避免刷新時誤判
|
||||
// onUnauthorized?.call();
|
||||
}
|
||||
return apiResponse;
|
||||
}
|
||||
return ApiResponse.fail('响应数据格式错误');
|
||||
return ApiResponse.fail('響應數據格式錯誤');
|
||||
}
|
||||
|
||||
ApiResponse<T> _handleError<T>(DioException e) {
|
||||
// 详细错误日志
|
||||
// 詳細錯誤日誌
|
||||
debugPrint('=== Network Error ===');
|
||||
debugPrint('Type: ${e.type}');
|
||||
debugPrint('Message: ${e.message}');
|
||||
@@ -122,7 +122,7 @@ class DioClient {
|
||||
if (_isUnauthorized(e)) {
|
||||
_clearUserData();
|
||||
onUnauthorized?.call();
|
||||
return ApiResponse.unauthorized('登录已过期,请重新登录');
|
||||
return ApiResponse.unauthorized('登錄已過期,請重新登錄');
|
||||
}
|
||||
|
||||
final message = _getErrorMessage(e);
|
||||
@@ -140,28 +140,28 @@ class DioClient {
|
||||
String _getErrorMessage(DioException e) {
|
||||
switch (e.type) {
|
||||
case DioExceptionType.connectionTimeout:
|
||||
return '连接超时,请检查网络';
|
||||
return '連接超時,請檢查網絡';
|
||||
case DioExceptionType.sendTimeout:
|
||||
return '发送超时,请重试';
|
||||
return '發送超時,請重試';
|
||||
case DioExceptionType.receiveTimeout:
|
||||
return '响应超时,请重试';
|
||||
return '響應超時,請重試';
|
||||
case DioExceptionType.connectionError:
|
||||
return '网络连接失败,请检查网络设置';
|
||||
return '網絡連接失敗,請檢查網絡設置';
|
||||
case DioExceptionType.badResponse:
|
||||
final statusCode = e.response?.statusCode;
|
||||
if (statusCode == 500) {
|
||||
return '服务器内部错误';
|
||||
return '服務器內部錯誤';
|
||||
} else if (statusCode == 502 || statusCode == 503) {
|
||||
return '服务暂时不可用';
|
||||
return '服務暫時不可用';
|
||||
}
|
||||
return '服务器错误 ($statusCode)';
|
||||
return '服務器錯誤 ($statusCode)';
|
||||
default:
|
||||
return e.message ?? '网络请求失败';
|
||||
return e.message ?? '網絡請求失敗';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 日志拦截器
|
||||
/// 日誌攔截器
|
||||
class _LoggingInterceptor extends Interceptor {
|
||||
@override
|
||||
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
||||
@@ -195,7 +195,7 @@ class _LoggingInterceptor extends Interceptor {
|
||||
}
|
||||
}
|
||||
|
||||
/// 认证拦截器
|
||||
/// 認證攔截器
|
||||
class _AuthInterceptor extends Interceptor {
|
||||
@override
|
||||
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:convert';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// 本地存储服务
|
||||
/// 本地存儲服務
|
||||
class LocalStorage {
|
||||
LocalStorage._();
|
||||
|
||||
@@ -15,7 +15,7 @@ class LocalStorage {
|
||||
_prefs = await SharedPreferences.getInstance();
|
||||
}
|
||||
|
||||
/// 获取实例
|
||||
/// 獲取實例
|
||||
static SharedPreferences get prefs {
|
||||
if (_prefs == null) {
|
||||
throw Exception('LocalStorage not initialized. Call init() first.');
|
||||
@@ -30,7 +30,7 @@ class LocalStorage {
|
||||
await prefs.setString(_tokenKey, token);
|
||||
}
|
||||
|
||||
/// 获取 Token
|
||||
/// 獲取 Token
|
||||
static String? getToken() {
|
||||
return prefs.getString(_tokenKey);
|
||||
}
|
||||
@@ -40,17 +40,17 @@ class LocalStorage {
|
||||
await prefs.remove(_tokenKey);
|
||||
}
|
||||
|
||||
/// 是否已登录
|
||||
/// 是否已登錄
|
||||
static bool get isLoggedIn => getToken() != null && getToken()!.isNotEmpty;
|
||||
|
||||
// ==================== 用户信息管理 ====================
|
||||
// ==================== 用戶信息管理 ====================
|
||||
|
||||
/// 保存用户信息
|
||||
/// 保存用戶信息
|
||||
static Future<void> saveUserInfo(Map<String, dynamic> userInfo) async {
|
||||
await prefs.setString(_userInfoKey, jsonEncode(userInfo));
|
||||
}
|
||||
|
||||
/// 获取用户信息
|
||||
/// 獲取用戶信息
|
||||
static Map<String, dynamic>? getUserInfo() {
|
||||
final str = prefs.getString(_userInfoKey);
|
||||
if (str == null) return null;
|
||||
@@ -61,7 +61,7 @@ class LocalStorage {
|
||||
}
|
||||
}
|
||||
|
||||
/// 移除用户信息
|
||||
/// 移除用戶信息
|
||||
static Future<void> removeUserInfo() async {
|
||||
await prefs.remove(_userInfoKey);
|
||||
}
|
||||
@@ -73,47 +73,47 @@ class LocalStorage {
|
||||
await prefs.setString(key, value);
|
||||
}
|
||||
|
||||
/// 获取字符串
|
||||
/// 獲取字符串
|
||||
static String? getString(String key) {
|
||||
return prefs.getString(key);
|
||||
}
|
||||
|
||||
/// 保存布尔值
|
||||
/// 保存布爾值
|
||||
static Future<void> setBool(String key, bool value) async {
|
||||
await prefs.setBool(key, value);
|
||||
}
|
||||
|
||||
/// 获取布尔值
|
||||
/// 獲取布爾值
|
||||
static bool? getBool(String key) {
|
||||
return prefs.getBool(key);
|
||||
}
|
||||
|
||||
/// 清除所有数据
|
||||
/// 清除所有數據
|
||||
static Future<void> clearAll() async {
|
||||
await prefs.clear();
|
||||
}
|
||||
|
||||
/// 清除用户数据(退出登录时调用)
|
||||
/// 清除用戶數據(退出登錄時調用)
|
||||
static Future<void> clearUserData() async {
|
||||
await removeToken();
|
||||
await removeUserInfo();
|
||||
}
|
||||
|
||||
// ==================== 引导页状态 ====================
|
||||
// ==================== 引導頁狀態 ====================
|
||||
|
||||
static const String _onboardingKey = 'onboarding_completed';
|
||||
|
||||
/// 检查是否已完成引导页
|
||||
/// 檢查是否已完成引導頁
|
||||
static bool get isOnboardingCompleted {
|
||||
return getBool(_onboardingKey) ?? false;
|
||||
}
|
||||
|
||||
/// 标记引导页已完成
|
||||
/// 標記引導頁已完成
|
||||
static Future<void> setOnboardingCompleted() async {
|
||||
await setBool(_onboardingKey, true);
|
||||
}
|
||||
|
||||
/// 重置引导页状态(用于测试)
|
||||
/// 重置引導頁狀態(用於測試)
|
||||
static Future<void> resetOnboarding() async {
|
||||
await prefs.remove(_onboardingKey);
|
||||
}
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
/// Material Design 3 颜色系统 - Pencil Design Theme
|
||||
/// Material Design 3 顏色系統 - Pencil Design Theme
|
||||
///
|
||||
/// 深色主题: "黑金传奇" (Black & Gold)
|
||||
/// 深邃黑底 + 真金主色 + 翠绿盈利 → 金融奢华感
|
||||
/// 深色主題: "黑金傳奇" (Black & Gold)
|
||||
/// 深邃黑底 + 真金主色 + 翠綠盈利 → 金融奢華感
|
||||
/// 背景 #0B1120 | 主色 #D4AF37 | 盈利 #4ADE80
|
||||
///
|
||||
/// 浅色主题: "白金殿堂" (White & Gold)
|
||||
/// 纯净白底 + 深灰主色 + 琥珀强调 → 高端金融科技
|
||||
/// 背景 #F8FAFC | 主色 #1F2937 | 强调 #F59E0B
|
||||
/// 淺色主題: "白金殿堂" (White & Gold)
|
||||
/// 純淨白底 + 深灰主色 + 琥珀強調 → 高端金融科技
|
||||
/// 背景 #F8FAFC | 主色 #1F2937 | 強調 #F59E0B
|
||||
///
|
||||
/// 设计原则:
|
||||
/// 設計原則:
|
||||
/// - Material Design 3 配色方案
|
||||
/// - Glass Panel 毛玻璃效果
|
||||
/// - Gold Glow 金色光效(原 Neon Glow)
|
||||
/// - 渐变 CTA: primary → primary_container (135度)
|
||||
/// - 漸變 CTA: primary → primary_container (135度)
|
||||
class AppColorScheme {
|
||||
AppColorScheme._();
|
||||
|
||||
// ============================================
|
||||
// 深色主题 - "Slate Dark" (Material Design 3)
|
||||
// 背景 #0B1120 | 主色 #1E3A8A | 强调 #D4AF37
|
||||
// 深色主題 - "Slate Dark" (Material Design 3)
|
||||
// 背景 #0B1120 | 主色 #1E3A8A | 強調 #D4AF37
|
||||
// ============================================
|
||||
|
||||
/// 背景基色 - Slate 深蓝黑
|
||||
/// 背景基色 - Slate 深藍黑
|
||||
static const Color darkBackground = Color(0xFF0B1120);
|
||||
|
||||
/// Surface 层次 (从低到高) - Material Design 3 规范
|
||||
/// Surface 層次 (從低到高) - Material Design 3 規範
|
||||
static const Color darkSurfaceDim = Color(0xFF0B1120);
|
||||
static const Color darkSurfaceLowest = Color(0xFF000000);
|
||||
static const Color darkSurfaceLow = Color(0xFF0F172A);
|
||||
@@ -38,7 +38,7 @@ class AppColorScheme {
|
||||
static const Color darkSurfaceBright = Color(0xFF334155);
|
||||
static const Color darkSurfaceVariant = Color(0xFF1E293B);
|
||||
|
||||
/// 兼容旧名称
|
||||
/// 兼容舊名稱
|
||||
static const Color darkSurfaceContainerLowest = darkSurfaceLowest;
|
||||
static const Color darkSurfaceContainerLow = darkSurfaceLow;
|
||||
static const Color darkSurfaceHigh = darkSurfaceContainerHigh;
|
||||
@@ -59,7 +59,7 @@ class AppColorScheme {
|
||||
static const Color darkOnPrimaryFixed = Color(0xFF1F2937);
|
||||
static const Color darkOnPrimaryFixedVariant = Color(0xFF374151);
|
||||
|
||||
/// Secondary - 真金 #D4AF37 (黑金强调色)
|
||||
/// Secondary - 真金 #D4AF37 (黑金強調色)
|
||||
static const Color darkSecondary = Color(0xFFD4AF37);
|
||||
static const Color darkSecondaryDim = Color(0xFFB8960E);
|
||||
static const Color darkSecondaryContainer = Color(0xFFE8C84A);
|
||||
@@ -81,7 +81,7 @@ class AppColorScheme {
|
||||
static const Color darkOnTertiaryFixed = Color(0xFF052E16);
|
||||
static const Color darkOnTertiaryFixedVariant = Color(0xFF14532D);
|
||||
|
||||
/// Error - Neon Red (红色 - 错误/卖出)
|
||||
/// Error - Neon Red (紅色 - 錯誤/賣出)
|
||||
static const Color darkError = Color(0xFFff716c);
|
||||
static const Color darkErrorDim = Color(0xFFd7383b);
|
||||
static const Color darkErrorContainer = Color(0xFF9f0519);
|
||||
@@ -99,14 +99,14 @@ class AppColorScheme {
|
||||
static const Color darkSurfaceTint = Color(0xFFD4AF37);
|
||||
|
||||
// ============================================
|
||||
// 浅色主题 - "Slate Light" (Material Design 3)
|
||||
// 背景 #F8FAFC | 主色 #1E40AF | 强调 #D4AF37
|
||||
// 淺色主題 - "Slate Light" (Material Design 3)
|
||||
// 背景 #F8FAFC | 主色 #1E40AF | 強調 #D4AF37
|
||||
// ============================================
|
||||
|
||||
/// 背景基色 - Slate 50
|
||||
static const Color lightBackground = Color(0xFFF8FAFC);
|
||||
|
||||
/// Surface 层次 (从低到高)
|
||||
/// Surface 層次 (從低到高)
|
||||
static const Color lightSurfaceLowest = Color(0xFFFFFFFF);
|
||||
static const Color lightSurfaceLow = Color(0xFFF1F5F9);
|
||||
static const Color lightSurface = Color(0xFFF8FAFC);
|
||||
@@ -134,41 +134,41 @@ class AppColorScheme {
|
||||
static const Color lightOnSurfaceMuted = Color(0xFF94A3B8);
|
||||
|
||||
// ============================================
|
||||
// Glass Panel 毛玻璃效果颜色
|
||||
// Glass Panel 毛玻璃效果顏色
|
||||
// ============================================
|
||||
|
||||
/// Glass Panel 背景色 - rgba(33, 37, 47, 0.4)
|
||||
static const Color glassPanelBackground = Color(0x6621252F);
|
||||
|
||||
/// Glass Panel 边框色 - rgba(69, 72, 79, 0.15)
|
||||
/// Glass Panel 邊框色 - rgba(69, 72, 79, 0.15)
|
||||
static const Color glassPanelBorder = Color(0x2645484f);
|
||||
|
||||
// ============================================
|
||||
// Gold Glow 金色光效颜色(原 Neon Glow)
|
||||
// Gold Glow 金色光效顏色(原 Neon Glow)
|
||||
// ============================================
|
||||
|
||||
/// Primary Glow - 金色光晕 rgba(212, 175, 55, 0.15)
|
||||
/// Primary Glow - 金色光暈 rgba(212, 175, 55, 0.15)
|
||||
static const Color neonGlowPrimary = Color(0x26D4AF37);
|
||||
|
||||
/// Secondary Glow - 蓝色光晕 rgba(30, 58, 138, 0.15)
|
||||
/// Secondary Glow - 藍色光暈 rgba(30, 58, 138, 0.15)
|
||||
static const Color neonGlowSecondary = Color(0x261E3A8A);
|
||||
|
||||
/// Tertiary Glow - 绿色光晕 rgba(175, 255, 209, 0.2)
|
||||
/// Tertiary Glow - 綠色光暈 rgba(175, 255, 209, 0.2)
|
||||
static const Color neonGlowTertiary = Color(0x33afffd1);
|
||||
|
||||
// ============================================
|
||||
// 语义色 - 明暗通用
|
||||
// 語義色 - 明暗通用
|
||||
// ============================================
|
||||
|
||||
/// 涨/买入/成功 (深色主题色,亮色模式下请使用 getUpColor)
|
||||
/// 漲/買入/成功 (深色主題色,亮色模式下請使用 getUpColor)
|
||||
static const Color up = darkTertiary;
|
||||
static const Color success = darkTertiary;
|
||||
|
||||
/// 跌/卖出/错误 - 使用 Material Design 3 error
|
||||
/// 跌/賣出/錯誤 - 使用 Material Design 3 error
|
||||
static const Color down = Color(0xFFff716c);
|
||||
static const Color error = down;
|
||||
|
||||
/// 静默/禁用/次要
|
||||
/// 靜默/禁用/次要
|
||||
static const Color muted = darkOnSurfaceVariant;
|
||||
|
||||
/// 警告
|
||||
@@ -178,50 +178,50 @@ class AppColorScheme {
|
||||
static const Color info = Color(0xFF2196F3);
|
||||
|
||||
// ============================================
|
||||
// 主题感知辅助函数 - 已迁移至 AppThemeColors ThemeExtension
|
||||
// 以下方法保留仅供 AppThemeColors 工厂内部使用
|
||||
// 主題感知輔助函數 - 已遷移至 AppThemeColors ThemeExtension
|
||||
// 以下方法保留僅供 AppThemeColors 工廠內部使用
|
||||
// ============================================
|
||||
static const Color buyButtonFill = Color(0xFF059669);
|
||||
|
||||
/// 交易按钮卖出色 - 深红色填充,确保白色文字可读
|
||||
/// 交易按鈕賣出色 - 深紅色填充,確保白色文字可讀
|
||||
static const Color sellButtonFill = Color(0xFFDC2626);
|
||||
|
||||
// ============================================
|
||||
// 渐变预设
|
||||
// 漸變預設
|
||||
// ============================================
|
||||
|
||||
/// 深色 CTA 渐变 (primary → primary_container, 135度)
|
||||
/// 深色 CTA 漸變 (primary → primary_container, 135度)
|
||||
static const LinearGradient darkCtaGradient = LinearGradient(
|
||||
colors: [darkPrimary, darkPrimaryContainer],
|
||||
begin: Alignment(-0.7, -0.7),
|
||||
end: Alignment(0.7, 0.7),
|
||||
);
|
||||
|
||||
/// 浅色 CTA 渐变
|
||||
/// 淺色 CTA 漸變
|
||||
static const LinearGradient lightCtaGradient = LinearGradient(
|
||||
colors: [lightPrimary, lightPrimaryContainer],
|
||||
begin: Alignment(-0.7, -0.7),
|
||||
end: Alignment(0.7, 0.7),
|
||||
);
|
||||
|
||||
/// 兼容别名
|
||||
/// 兼容別名
|
||||
static const LinearGradient ctaGradient = darkCtaGradient;
|
||||
|
||||
/// 买入按钮渐变
|
||||
/// 買入按鈕漸變
|
||||
static const LinearGradient buyGradient = LinearGradient(
|
||||
colors: [darkTertiary, darkTertiaryContainer],
|
||||
begin: Alignment(-0.7, -0.7),
|
||||
end: Alignment(0.7, 0.7),
|
||||
);
|
||||
|
||||
/// 卖出按钮渐变
|
||||
/// 賣出按鈕漸變
|
||||
static const LinearGradient sellGradient = LinearGradient(
|
||||
colors: [darkError, darkErrorDim],
|
||||
begin: Alignment(-0.7, -0.7),
|
||||
end: Alignment(0.7, 0.7),
|
||||
);
|
||||
|
||||
/// 资产卡片渐变 - 金色层次
|
||||
/// 資產卡片漸變 - 金色層次
|
||||
static const LinearGradient assetCardGradient = LinearGradient(
|
||||
colors: [darkPrimaryContainer, darkPrimary],
|
||||
begin: Alignment.topLeft,
|
||||
@@ -229,7 +229,7 @@ class AppColorScheme {
|
||||
);
|
||||
|
||||
// ============================================
|
||||
// Shadcn ColorScheme - 深色主题
|
||||
// Shadcn ColorScheme - 深色主題
|
||||
// ============================================
|
||||
|
||||
static ShadColorScheme get darkShad => ShadColorScheme(
|
||||
@@ -256,7 +256,7 @@ class AppColorScheme {
|
||||
);
|
||||
|
||||
// ============================================
|
||||
// Shadcn ColorScheme - 浅色主题
|
||||
// Shadcn ColorScheme - 淺色主題
|
||||
// ============================================
|
||||
|
||||
static ShadColorScheme get lightShad => ShadColorScheme(
|
||||
@@ -283,7 +283,7 @@ class AppColorScheme {
|
||||
);
|
||||
|
||||
// ============================================
|
||||
// Material ColorScheme - 深色主题 (Material Design 3)
|
||||
// Material ColorScheme - 深色主題 (Material Design 3)
|
||||
// ============================================
|
||||
|
||||
static ColorScheme get darkMaterial => ColorScheme.dark(
|
||||
@@ -322,17 +322,17 @@ class AppColorScheme {
|
||||
);
|
||||
|
||||
// ============================================
|
||||
// Material ColorScheme - 浅色主题
|
||||
// Material ColorScheme - 淺色主題
|
||||
// ============================================
|
||||
|
||||
/// 浅色主题 Error 色
|
||||
/// 淺色主題 Error 色
|
||||
static const Color lightError = Color(0xFFDC2626);
|
||||
static const Color lightOnError = Color(0xFFFFFFFF);
|
||||
|
||||
/// 浅色主题 Outline 色 - Slate 500
|
||||
/// 淺色主題 Outline 色 - Slate 500
|
||||
static const Color lightOutline = Color(0xFF64748B);
|
||||
|
||||
/// 浅色主题 Surface 色 (扩展) - Slate 系列
|
||||
/// 淺色主題 Surface 色 (擴展) - Slate 系列
|
||||
static const Color lightSurfaceBright = Color(0xFFFFFFFF);
|
||||
static const Color lightSurfaceDim = Color(0xFFE2E8F0);
|
||||
static const Color lightSurfaceVariant = Color(0xFFE2E8F0);
|
||||
@@ -376,12 +376,12 @@ class AppColorScheme {
|
||||
);
|
||||
|
||||
// ============================================
|
||||
// 兼容性别名 - 仅保留有外部引用的
|
||||
// 兼容性別名 - 僅保留有外部引用的
|
||||
// (buyButtonFill, sellButtonFill, darkCtaGradient, lightCtaGradient,
|
||||
// buyGradient, sellGradient 已在上方定义,此处无需重复)
|
||||
// buyGradient, sellGradient 已在上方定義,此處無需重複)
|
||||
// ============================================
|
||||
|
||||
/// 获取买入按钮渐变(主题感知)- 用于盈利展示
|
||||
/// 獲取買入按鈕漸變(主題感知)- 用於盈利展示
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// 间距系统
|
||||
/// 間距系統
|
||||
///
|
||||
/// 使用 4px 基础单位,确保一致性
|
||||
/// 使用 4px 基礎單位,確保一致性
|
||||
class AppSpacing {
|
||||
AppSpacing._();
|
||||
|
||||
// ============================================
|
||||
// 基础间距 (Base Spacing)
|
||||
// 基礎間距 (Base Spacing)
|
||||
// ============================================
|
||||
|
||||
/// 极小 - 4px
|
||||
/// 極小 - 4px
|
||||
static const double xs = 4.0;
|
||||
|
||||
/// 小 - 8px
|
||||
static const double sm = 8.0;
|
||||
|
||||
/// 中 - 16px (默认)
|
||||
/// 中 - 16px (默認)
|
||||
static const double md = 16.0;
|
||||
|
||||
/// 大 - 24px
|
||||
@@ -29,28 +29,28 @@ class AppSpacing {
|
||||
static const double xxl = 48.0;
|
||||
|
||||
// ============================================
|
||||
// 预设 EdgeInsets
|
||||
// 預設 EdgeInsets
|
||||
// ============================================
|
||||
|
||||
/// 页面内边距
|
||||
/// 頁面內邊距
|
||||
static const EdgeInsets pagePadding = EdgeInsets.all(md);
|
||||
|
||||
/// 卡片内边距
|
||||
/// 卡片內邊距
|
||||
static const EdgeInsets cardPadding = EdgeInsets.all(md);
|
||||
|
||||
/// 列表项内边距
|
||||
/// 列表項內邊距
|
||||
static const EdgeInsets listItemPadding = EdgeInsets.symmetric(
|
||||
horizontal: md,
|
||||
vertical: sm,
|
||||
);
|
||||
|
||||
/// 按钮内边距
|
||||
/// 按鈕內邊距
|
||||
static const EdgeInsets buttonPadding = EdgeInsets.symmetric(
|
||||
horizontal: lg,
|
||||
vertical: 14,
|
||||
);
|
||||
|
||||
/// 输入框内边距
|
||||
/// 輸入框內邊距
|
||||
static const EdgeInsets inputPadding = EdgeInsets.symmetric(
|
||||
horizontal: md,
|
||||
vertical: 14,
|
||||
@@ -60,98 +60,98 @@ class AppSpacing {
|
||||
// 便捷方法
|
||||
// ============================================
|
||||
|
||||
/// 获取水平间距
|
||||
/// 獲取水平間距
|
||||
static SizedBox horizontal(double spacing) => SizedBox(width: spacing);
|
||||
|
||||
/// 获取垂直间距
|
||||
/// 獲取垂直間距
|
||||
static SizedBox vertical(double spacing) => SizedBox(height: spacing);
|
||||
}
|
||||
|
||||
/// 圆角系统 - "The Kinetic Vault" & "The Ethereal Terminal" 规范
|
||||
/// 圓角系統 - "The Kinetic Vault" & "The Ethereal Terminal" 規範
|
||||
///
|
||||
/// 设计规则:
|
||||
/// - 按钮: xxl (24px / 1.5rem)
|
||||
/// 設計規則:
|
||||
/// - 按鈕: xxl (24px / 1.5rem)
|
||||
/// - 卡片: xl (16px)
|
||||
/// - 输入框: md (8px)
|
||||
/// - 标签: sm (4px)
|
||||
/// - 輸入框: md (8px)
|
||||
/// - 標籤: sm (4px)
|
||||
class AppRadius {
|
||||
AppRadius._();
|
||||
|
||||
// ============================================
|
||||
// 基础圆角 (Base Radius)
|
||||
// 基礎圓角 (Base Radius)
|
||||
// ============================================
|
||||
|
||||
/// 小圆角 - 6px (标签、徽章) — 对齐 Pencil $radius-sm
|
||||
/// 小圓角 - 6px (標籤、徽章) — 對齊 Pencil $radius-sm
|
||||
static const double sm = 6.0;
|
||||
|
||||
/// 中圆角 - 10px (输入框、标签页) — 对齐 Pencil $radius-md
|
||||
/// 中圓角 - 10px (輸入框、標籤頁) — 對齊 Pencil $radius-md
|
||||
static const double md = 10.0;
|
||||
|
||||
/// 大圆角 - 14px (卡片、按钮、列表) — 对齐 Pencil $radius-lg
|
||||
/// 大圓角 - 14px (卡片、按鈕、列表) — 對齊 Pencil $radius-lg
|
||||
static const double lg = 14.0;
|
||||
|
||||
/// 特大圆角 - 20px (大卡片、弹窗) — 对齐 Pencil $radius-xl
|
||||
/// 特大圓角 - 20px (大卡片、彈窗) — 對齊 Pencil $radius-xl
|
||||
static const double xl = 20.0;
|
||||
|
||||
/// 超大圆角 - 24px (按钮、模态框、底部抽屉)
|
||||
/// 超大圓角 - 24px (按鈕、模態框、底部抽屜)
|
||||
static const double xxl = 24.0;
|
||||
|
||||
/// 圆形 - 9999px (Pill buttons)
|
||||
/// 圓形 - 9999px (Pill buttons)
|
||||
static const double full = 9999.0;
|
||||
|
||||
// ============================================
|
||||
// 预设 BorderRadius
|
||||
// 預設 BorderRadius
|
||||
// ============================================
|
||||
|
||||
/// 小圆角
|
||||
/// 小圓角
|
||||
static BorderRadius get radiusSm => BorderRadius.circular(sm);
|
||||
|
||||
/// 中圆角
|
||||
/// 中圓角
|
||||
static BorderRadius get radiusMd => BorderRadius.circular(md);
|
||||
|
||||
/// 大圆角
|
||||
/// 大圓角
|
||||
static BorderRadius get radiusLg => BorderRadius.circular(lg);
|
||||
|
||||
/// 特大圆角
|
||||
/// 特大圓角
|
||||
static BorderRadius get radiusXl => BorderRadius.circular(xl);
|
||||
|
||||
/// 超大圆角
|
||||
/// 超大圓角
|
||||
static BorderRadius get radiusXxl => BorderRadius.circular(xxl);
|
||||
|
||||
/// 圆形
|
||||
/// 圓形
|
||||
static BorderRadius get radiusFull => BorderRadius.circular(full);
|
||||
}
|
||||
|
||||
/// 响应式断点
|
||||
/// 響應式斷點
|
||||
class AppBreakpoints {
|
||||
AppBreakpoints._();
|
||||
|
||||
/// 手机竖屏
|
||||
/// 手機豎屏
|
||||
static const double mobile = 360;
|
||||
|
||||
/// 手机横屏/小平板
|
||||
/// 手機橫屏/小平板
|
||||
static const double tablet = 768;
|
||||
|
||||
/// 平板/桌面
|
||||
static const double desktop = 1024;
|
||||
|
||||
/// 检查是否为手机
|
||||
/// 檢查是否為手機
|
||||
static bool isMobile(BuildContext context) {
|
||||
return MediaQuery.of(context).size.width < tablet;
|
||||
}
|
||||
|
||||
/// 检查是否为平板
|
||||
/// 檢查是否為平板
|
||||
static bool isTablet(BuildContext context) {
|
||||
final width = MediaQuery.of(context).size.width;
|
||||
return width >= tablet && width < desktop;
|
||||
}
|
||||
|
||||
/// 检查是否为桌面
|
||||
/// 檢查是否為桌面
|
||||
static bool isDesktop(BuildContext context) {
|
||||
return MediaQuery.of(context).size.width >= desktop;
|
||||
}
|
||||
|
||||
/// 根据屏幕宽度获取响应式值
|
||||
/// 根據屏幕寬度獲取響應式值
|
||||
static T responsive<T>(
|
||||
BuildContext context, {
|
||||
required T mobile,
|
||||
@@ -168,14 +168,14 @@ class AppBreakpoints {
|
||||
}
|
||||
}
|
||||
|
||||
/// 触摸目标尺寸
|
||||
/// 觸摸目標尺寸
|
||||
class AppTouchTarget {
|
||||
AppTouchTarget._();
|
||||
|
||||
/// 最小触摸目标 - 44x44 (iOS HIG 标准)
|
||||
/// 最小觸摸目標 - 44x44 (iOS HIG 標準)
|
||||
static const double minSize = 44.0;
|
||||
|
||||
/// 确保触摸目标足够大
|
||||
/// 確保觸摸目標足夠大
|
||||
static Widget ensureMinSize({
|
||||
required Widget child,
|
||||
double minSize = AppTouchTarget.minSize,
|
||||
|
||||
@@ -4,12 +4,12 @@ import 'app_color_scheme.dart';
|
||||
import 'app_spacing.dart';
|
||||
import 'app_theme_extension.dart';
|
||||
|
||||
/// "The Kinetic Vault" & "The Ethereal Terminal" 主题配置
|
||||
/// "The Kinetic Vault" & "The Ethereal Terminal" 主題配置
|
||||
class AppTheme {
|
||||
AppTheme._();
|
||||
|
||||
// ============================================
|
||||
// 深色主题 - "The Kinetic Vault"
|
||||
// 深色主題 - "The Kinetic Vault"
|
||||
// ============================================
|
||||
|
||||
static ThemeData get darkTheme {
|
||||
@@ -23,7 +23,7 @@ class AppTheme {
|
||||
AppThemeColors.dark(),
|
||||
],
|
||||
|
||||
// AppBar - 无边框规则
|
||||
// AppBar - 無邊框規則
|
||||
appBarTheme: AppBarTheme(
|
||||
backgroundColor: AppColorScheme.darkBackground,
|
||||
foregroundColor: AppColorScheme.darkOnSurface,
|
||||
@@ -37,7 +37,7 @@ class AppTheme {
|
||||
),
|
||||
),
|
||||
|
||||
// 卡片 - 无边框,使用 surface 层次
|
||||
// 卡片 - 無邊框,使用 surface 層次
|
||||
cardTheme: CardThemeData(
|
||||
color: AppColorScheme.darkSurface,
|
||||
elevation: 0,
|
||||
@@ -46,7 +46,7 @@ class AppTheme {
|
||||
),
|
||||
),
|
||||
|
||||
// 输入框 - Ghost Border 风格
|
||||
// 輸入框 - Ghost Border 風格
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
filled: true,
|
||||
fillColor: AppColorScheme.darkSurfaceLow,
|
||||
@@ -73,7 +73,7 @@ class AppTheme {
|
||||
),
|
||||
),
|
||||
|
||||
// 按钮 - Pencil accent-primary (Gold)
|
||||
// 按鈕 - Pencil accent-primary (Gold)
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColorScheme.darkSecondary,
|
||||
@@ -97,13 +97,13 @@ class AppTheme {
|
||||
),
|
||||
),
|
||||
|
||||
// 分割线 - 使用层次而非边框
|
||||
// 分割線 - 使用層次而非邊框
|
||||
dividerTheme: DividerThemeData(
|
||||
color: AppColorScheme.darkOutlineVariant.withValues(alpha: 0.1),
|
||||
thickness: 1,
|
||||
),
|
||||
|
||||
// 底部导航
|
||||
// 底部導航
|
||||
bottomNavigationBarTheme: BottomNavigationBarThemeData(
|
||||
backgroundColor: AppColorScheme.darkSurface,
|
||||
selectedItemColor: AppColorScheme.darkPrimary,
|
||||
@@ -115,7 +115,7 @@ class AppTheme {
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 浅色主题 - "The Ethereal Terminal"
|
||||
// 淺色主題 - "The Ethereal Terminal"
|
||||
// ============================================
|
||||
|
||||
static ThemeData get lightTheme {
|
||||
@@ -152,7 +152,7 @@ class AppTheme {
|
||||
),
|
||||
),
|
||||
|
||||
// 输入框 - 底部线条风格
|
||||
// 輸入框 - 底部線條風格
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
filled: true,
|
||||
fillColor: AppColorScheme.lightSurfaceLow,
|
||||
@@ -181,7 +181,7 @@ class AppTheme {
|
||||
),
|
||||
),
|
||||
|
||||
// 按钮 - Pencil accent-primary (dark gray)
|
||||
// 按鈕 - Pencil accent-primary (dark gray)
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF1F2937),
|
||||
@@ -205,13 +205,13 @@ class AppTheme {
|
||||
),
|
||||
),
|
||||
|
||||
// 分割线 - 不使用,用留白代替
|
||||
// 分割線 - 不使用,用留白代替
|
||||
dividerTheme: DividerThemeData(
|
||||
color: AppColorScheme.lightOutlineVariant.withValues(alpha: 0.2),
|
||||
thickness: 1,
|
||||
),
|
||||
|
||||
// 底部导航
|
||||
// 底部導航
|
||||
bottomNavigationBarTheme: BottomNavigationBarThemeData(
|
||||
backgroundColor: AppColorScheme.lightSurfaceHighest,
|
||||
selectedItemColor: AppColorScheme.lightPrimary,
|
||||
@@ -223,19 +223,19 @@ class AppTheme {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pencil 设计系统字体规范
|
||||
/// Pencil 設計系統字體規範
|
||||
///
|
||||
/// 对齐 Pencil 变量: Inter 字体, 明确的字号/字重层级
|
||||
/// 28px (总资产) → 24px (精选价格) → 22px (页面标题) → 16px (区块标题)
|
||||
/// → 14px (卡片标题/价格) → 13px (正文) → 12px (标签/副标题) → 11px (小文字)
|
||||
/// 對齊 Pencil 變量: Inter 字體, 明確的字號/字重層級
|
||||
/// 28px (總資產) → 24px (精選價格) → 22px (頁面標題) → 16px (區塊標題)
|
||||
/// → 14px (卡片標題/價格) → 13px (正文) → 12px (標籤/副標題) → 11px (小文字)
|
||||
class AppTextStyles {
|
||||
AppTextStyles._();
|
||||
|
||||
// ============================================
|
||||
// Display - 核心数字
|
||||
// Display - 核心數字
|
||||
// ============================================
|
||||
|
||||
/// D1 - 总资产/余额 (28px w700) — Pencil $hero-value
|
||||
/// D1 - 總資產/餘額 (28px w700) — Pencil $hero-value
|
||||
static TextStyle displayLarge(BuildContext context) => GoogleFonts.inter(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w700,
|
||||
@@ -244,7 +244,7 @@ class AppTextStyles {
|
||||
letterSpacing: -0.5,
|
||||
);
|
||||
|
||||
/// D2 - 精选价格 (24px w700) — Pencil 行情卡片价格
|
||||
/// D2 - 精選價格 (24px w700) — Pencil 行情卡片價格
|
||||
static TextStyle displayMedium(BuildContext context) => GoogleFonts.inter(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w700,
|
||||
@@ -252,7 +252,7 @@ class AppTextStyles {
|
||||
height: 1.15,
|
||||
);
|
||||
|
||||
/// D3 - 页面标题 (22px w700) — Pencil 页面大标题
|
||||
/// D3 - 頁面標題 (22px w700) — Pencil 頁面大標題
|
||||
static TextStyle displaySmall(BuildContext context) => GoogleFonts.inter(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w700,
|
||||
@@ -261,10 +261,10 @@ class AppTextStyles {
|
||||
);
|
||||
|
||||
// ============================================
|
||||
// Headline - 标题
|
||||
// Headline - 標題
|
||||
// ============================================
|
||||
|
||||
/// 区块/导航标题 (16px w600) — Pencil $section-title
|
||||
/// 區塊/導航標題 (16px w600) — Pencil $section-title
|
||||
static TextStyle headlineLarge(BuildContext context) => GoogleFonts.inter(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -272,7 +272,7 @@ class AppTextStyles {
|
||||
height: 1.3,
|
||||
);
|
||||
|
||||
/// 卡片标题/价格/标签页 (14px w600) — Pencil $card-title
|
||||
/// 卡片標題/價格/標籤頁 (14px w600) — Pencil $card-title
|
||||
static TextStyle headlineMedium(BuildContext context) => GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -280,7 +280,7 @@ class AppTextStyles {
|
||||
height: 1.35,
|
||||
);
|
||||
|
||||
/// 副标题/持仓价值 (13px w500)
|
||||
/// 副標題/持倉價值 (13px w500)
|
||||
static TextStyle headlineSmall(BuildContext context) => GoogleFonts.inter(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -300,7 +300,7 @@ class AppTextStyles {
|
||||
height: 1.45,
|
||||
);
|
||||
|
||||
/// 次要正文/副标题 (12px w400) — Pencil $subtitle
|
||||
/// 次要正文/副標題 (12px w400) — Pencil $subtitle
|
||||
static TextStyle bodyMedium(BuildContext context) => GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
@@ -308,7 +308,7 @@ class AppTextStyles {
|
||||
height: 1.45,
|
||||
);
|
||||
|
||||
/// 辅助文字/币种全名 (11px w400) — Pencil $small-text
|
||||
/// 輔助文字/幣種全名 (11px w400) — Pencil $small-text
|
||||
static TextStyle bodySmall(BuildContext context) => GoogleFonts.inter(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w400,
|
||||
@@ -317,10 +317,10 @@ class AppTextStyles {
|
||||
);
|
||||
|
||||
// ============================================
|
||||
// Label - 标签
|
||||
// Label - 標籤
|
||||
// ============================================
|
||||
|
||||
/// 按钮/标签页标签 (12px w500) — Pencil $tab-label
|
||||
/// 按鈕/標籤頁標籤 (12px w500) — Pencil $tab-label
|
||||
static TextStyle labelLarge(BuildContext context) => GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -328,7 +328,7 @@ class AppTextStyles {
|
||||
height: 1.35,
|
||||
);
|
||||
|
||||
/// 涨跌幅标签 (11px w500) — Pencil $change-badge
|
||||
/// 漲跌幅標籤 (11px w500) — Pencil $change-badge
|
||||
static TextStyle labelMedium(BuildContext context) => GoogleFonts.inter(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -336,7 +336,7 @@ class AppTextStyles {
|
||||
height: 1.35,
|
||||
);
|
||||
|
||||
/// 涨跌幅标签-粗 (11px w600) — Pencil $change-badge-bold
|
||||
/// 漲跌幅標籤-粗 (11px w600) — Pencil $change-badge-bold
|
||||
static TextStyle labelSmall(BuildContext context) => GoogleFonts.inter(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -345,10 +345,10 @@ class AppTextStyles {
|
||||
);
|
||||
|
||||
// ============================================
|
||||
// 数字/金额 - Inter (等宽特性)
|
||||
// 數字/金額 - Inter (等寬特性)
|
||||
// ============================================
|
||||
|
||||
/// 大号数字 (28px w700) - 总资产、余额
|
||||
/// 大號數字 (28px w700) - 總資產、餘額
|
||||
static TextStyle numberLarge(BuildContext context) => GoogleFonts.inter(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w700,
|
||||
@@ -358,7 +358,7 @@ class AppTextStyles {
|
||||
fontFeatures: const [FontFeature.tabularFigures()],
|
||||
);
|
||||
|
||||
/// 中号数字 (14px w600) - 价格、金额
|
||||
/// 中號數字 (14px w600) - 價格、金額
|
||||
static TextStyle numberMedium(BuildContext context) => GoogleFonts.inter(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -367,7 +367,7 @@ class AppTextStyles {
|
||||
fontFeatures: const [FontFeature.tabularFigures()],
|
||||
);
|
||||
|
||||
/// 小号数字 (12px w500) - 涨跌幅、数量
|
||||
/// 小號數字 (12px w500) - 漲跌幅、數量
|
||||
static TextStyle numberSmall(BuildContext context) => GoogleFonts.inter(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
@@ -377,7 +377,7 @@ class AppTextStyles {
|
||||
);
|
||||
}
|
||||
|
||||
/// 动画时长
|
||||
/// 動畫時長
|
||||
class AnimationDurations {
|
||||
AnimationDurations._();
|
||||
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'app_color_scheme.dart';
|
||||
|
||||
/// 自定义主题扩展 — 注册到 ThemeData.extensions 中
|
||||
/// 自定義主題擴展 — 註冊到 ThemeData.extensions 中
|
||||
///
|
||||
/// 存放 ColorScheme 标准槽位无法覆盖的语义色:
|
||||
/// - 卡片背景语义色(dark/light 使用不同 surface 层级)
|
||||
/// - 涨跌金融色(含带透明度的背景色)
|
||||
/// - 渐变预设
|
||||
/// 存放 ColorScheme 標準槽位無法覆蓋的語義色:
|
||||
/// - 卡片背景語義色(dark/light 使用不同 surface 層級)
|
||||
/// - 漲跌金融色(含帶透明度的背景色)
|
||||
/// - 漸變預設
|
||||
/// - 光效透明度等
|
||||
@immutable
|
||||
class AppThemeColors extends ThemeExtension<AppThemeColors> {
|
||||
// ---- 卡片背景语义色 ----
|
||||
/// 标准卡片背景(dark: surfaceContainer, light: surfaceContainerHigh)
|
||||
// ---- 卡片背景語義色 ----
|
||||
/// 標準卡片背景(dark: surfaceContainer, light: surfaceContainerHigh)
|
||||
final Color surfaceCard;
|
||||
|
||||
/// 高亮卡片背景(dark: surfaceContainerHighest, light: surfaceContainerHigh)
|
||||
final Color surfaceCardHigh;
|
||||
|
||||
// ---- 文字语义 ----
|
||||
/// 弱化/辅助文字
|
||||
// ---- 文字語義 ----
|
||||
/// 弱化/輔助文字
|
||||
final Color onSurfaceMuted;
|
||||
|
||||
// ---- 涨跌金融色 ----
|
||||
/// 涨/盈利/买入
|
||||
// ---- 漲跌金融色 ----
|
||||
/// 漲/盈利/買入
|
||||
final Color up;
|
||||
final Color upBackground;
|
||||
|
||||
/// 跌/亏损/卖出
|
||||
/// 跌/虧損/賣出
|
||||
final Color down;
|
||||
final Color downBackground;
|
||||
|
||||
// ---- 强调色 ----
|
||||
/// 主强调色(dark: primary 金色, light: primary 深灰)
|
||||
// ---- 強調色 ----
|
||||
/// 主強調色(dark: primary 金色, light: primary 深灰)
|
||||
final Color accentPrimary;
|
||||
|
||||
// ---- 光效 ----
|
||||
/// 光晕透明度(dark: 0.15, light: 0.08)
|
||||
/// 光暈透明度(dark: 0.15, light: 0.08)
|
||||
final double glowOpacity;
|
||||
|
||||
// ---- 边框 ----
|
||||
/// Ghost Border(outlineVariant 带透明度)
|
||||
// ---- 邊框 ----
|
||||
/// Ghost Border(outlineVariant 帶透明度)
|
||||
final Color ghostBorder;
|
||||
|
||||
// ---- 渐变预设 ----
|
||||
// ---- 漸變預設 ----
|
||||
final LinearGradient ctaGradient;
|
||||
final LinearGradient buyGradient;
|
||||
final LinearGradient sellGradient;
|
||||
@@ -67,7 +67,7 @@ class AppThemeColors extends ThemeExtension<AppThemeColors> {
|
||||
required this.emeraldGradient,
|
||||
});
|
||||
|
||||
/// 深色主题工厂
|
||||
/// 深色主題工廠
|
||||
factory AppThemeColors.dark() => AppThemeColors(
|
||||
surfaceCard: AppColorScheme.darkSurfaceContainer,
|
||||
surfaceCardHigh: AppColorScheme.darkSurfaceContainerHighest,
|
||||
@@ -90,7 +90,7 @@ class AppThemeColors extends ThemeExtension<AppThemeColors> {
|
||||
),
|
||||
);
|
||||
|
||||
/// 浅色主题工厂
|
||||
/// 淺色主題工廠
|
||||
factory AppThemeColors.light() => AppThemeColors(
|
||||
surfaceCard: AppColorScheme.lightSurfaceLowest,
|
||||
surfaceCardHigh: AppColorScheme.lightSurfaceContainer,
|
||||
@@ -198,23 +198,23 @@ class AppThemeColors extends ThemeExtension<AppThemeColors> {
|
||||
static double lerpDouble(double a, double b, double t) => a + (b - a) * t;
|
||||
}
|
||||
|
||||
/// BuildContext 主题快捷扩展
|
||||
/// BuildContext 主題快捷擴展
|
||||
///
|
||||
/// 用法:
|
||||
/// context.colors.primary → Theme.of(context).colorScheme.primary
|
||||
/// context.appColors.up → AppThemeColors 中的涨色
|
||||
/// context.appColors.up → AppThemeColors 中的漲色
|
||||
/// context.isDark → 是否深色模式
|
||||
extension AppThemeContext on BuildContext {
|
||||
/// Material ColorScheme 快捷访问
|
||||
/// Material ColorScheme 快捷訪問
|
||||
ColorScheme get colors => Theme.of(this).colorScheme;
|
||||
|
||||
/// 自定义语义色快捷访问
|
||||
/// 自定義語義色快捷訪問
|
||||
AppThemeColors get appColors =>
|
||||
Theme.of(this).extension<AppThemeColors>()!;
|
||||
|
||||
/// 是否深色模式
|
||||
bool get isDark => Theme.of(this).brightness == Brightness.dark;
|
||||
|
||||
/// TextTheme 快捷访问
|
||||
/// TextTheme 快捷訪問
|
||||
TextTheme get textStyles => Theme.of(this).textTheme;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
|
||||
/// Toast 工具类 - 提供统一的 toast 提示功能
|
||||
/// Toast 工具類 - 提供統一的 toast 提示功能
|
||||
///
|
||||
/// 使用 bot_toast 实现,确保 toast 显示在所有弹窗之上
|
||||
/// 使用 bot_toast 實現,確保 toast 顯示在所有彈窗之上
|
||||
class ToastUtils {
|
||||
ToastUtils._();
|
||||
|
||||
/// 显示普通提示
|
||||
/// 顯示普通提示
|
||||
static void show(String message, {Duration? duration}) {
|
||||
BotToast.showCustomText(
|
||||
toastBuilder: (_) => _buildToastWidget(message),
|
||||
@@ -18,7 +18,7 @@ class ToastUtils {
|
||||
);
|
||||
}
|
||||
|
||||
/// 显示成功提示
|
||||
/// 顯示成功提示
|
||||
static void showSuccess(String message, {Duration? duration}) {
|
||||
BotToast.showCustomText(
|
||||
toastBuilder: (_) => _buildToastWidget(
|
||||
@@ -32,7 +32,7 @@ class ToastUtils {
|
||||
);
|
||||
}
|
||||
|
||||
/// 显示错误提示
|
||||
/// 顯示錯誤提示
|
||||
static void showError(String message, {Duration? duration}) {
|
||||
BotToast.showCustomText(
|
||||
toastBuilder: (_) => _buildToastWidget(
|
||||
@@ -46,7 +46,7 @@ class ToastUtils {
|
||||
);
|
||||
}
|
||||
|
||||
/// 显示警告提示
|
||||
/// 顯示警告提示
|
||||
static void showWarning(String message, {Duration? duration}) {
|
||||
BotToast.showCustomText(
|
||||
toastBuilder: (_) => _buildToastWidget(
|
||||
@@ -60,7 +60,7 @@ class ToastUtils {
|
||||
);
|
||||
}
|
||||
|
||||
/// 构建 toast widget
|
||||
/// 構建 toast widget
|
||||
static Widget _buildToastWidget(
|
||||
String message, {
|
||||
Color? backgroundColor,
|
||||
|
||||
Reference in New Issue
Block a user