flutter架 构项目基础

This commit is contained in:
崔宝新
2026-09-04 15:22:20 +08:00
parent 70967229d0
commit 0c5aee59c0
265 changed files with 35484 additions and 0 deletions
+90
View File
@@ -0,0 +1,90 @@
import 'package:new_learn/core/theme/brand_theme.dart';
/// ============================================================
/// 【AppBrand】品牌配置表 —— 运行时静态表(对标 uni-app 的 appList)
///
/// 一、概念:
/// 每所学校/客户 = 一个品牌(App 一个包)。品牌决定三件事:
/// appName 桌面/标题显示的 App 名;
/// baseUrl 正式环境的专属接口地址(prod 时覆盖通用地址);
/// themeId 关联 core/theme/brand_theme.dart 的主题注册表,
/// 换品牌 = 换主题色板 + 换接口 + 换名字,一套全换。
///
/// 二、新增品牌(三步,复刻 uni-app appList 的加一行风格):
/// 1. 本文件 appBrands 里加一行;
/// 2. 若要用新主题,core/theme/brands/ 下建主题文件并注册;
/// 3. 构建时 --dart-define=BRAND=新id。
///
/// 三、与主题的关系:
/// 主题走运行时(BrandTheme,可切换/持久化);
/// 品牌走发布期(打包定死)。品牌. themeId 指定该品牌默认主题,
/// 运行时仍可在 brandThemes 里切换(互不干扰)。
/// ============================================================
class AppBrand {
const AppBrand({
required this.id,
required this.appName,
this.baseUrl,
required this.themeId,
});
/// 品牌标识(--dart-define=BRAND= 用的值,必须与 Map key 一致)
final String id;
/// App 显示名(桌面图标名 + MaterialApp.title)
final String appName;
/// 正式环境专属接口地址(prod 时覆盖通用地址;null = 走环境通用地址)
final String? baseUrl;
/// 关联主题:brand_theme.dart 注册表里 BrandTheme.name
final String themeId;
}
/// 品牌注册表:与 uni-app appList 一一对应。
/// themeId 用 BrandNames 常量(一个主题可对应多个品牌,改一处全同步)。
const Map<String, AppBrand> appBrands = {
'aixue': AppBrand(
id: 'aixue',
appName: '爱学蝶变',
baseUrl: 'https://ylax-api.cqbzyx.com',
themeId: BrandNames.skyBlue,
),
'chucai': AppBrand(
id: 'chucai',
appName: '楚才爱学',
baseUrl: 'https://ccax-api.hbxyszzhyx.com',
themeId: BrandNames.red,
),
'yule': AppBrand(
id: 'yule',
appName: '渝乐爱学',
baseUrl: 'https://ylax-api.cqbzyx.com',
themeId: BrandNames.skyBlue,
),
'yuzhi': AppBrand(
id: 'yuzhi',
appName: '豫志爱学',
baseUrl: 'https://yzax-api.cnzhyx.cn',
themeId: BrandNames.skyBlue,
),
'qinxiao': AppBrand(
id: 'qinxiao',
appName: '秦骁爱学-智略行',
baseUrl: 'https://qxax-api.xatyzzhyx.com',
themeId: BrandNames.skyBlue,
),
'wansi': AppBrand(
id: 'wansi',
appName: '皖思爱学',
baseUrl: 'https://wsax-api.ahsdfzyx.com',
themeId: BrandNames.skyBlue,
),
'shucai': AppBrand(
id: 'shucai',
appName: '蜀才爱学-弘昇智',
baseUrl: 'https://scax-api.mynszxyx.com',
themeId: BrandNames.skyBlue,
),
// 新增品牌:直接加一行即可(对标 uni-app appList 的注释示例)
};
+49
View File
@@ -0,0 +1,49 @@
import 'package:new_learn/core/config/app_brand.dart';
import 'package:new_learn/core/config/app_env.dart';
import 'package:new_learn/core/theme/brand_theme.dart';
/// ============================================================
/// 【AppConfig】配置聚合层 —— 全项目唯一取配置入口
///
/// 组合规则(复刻 uni-app:appList × envList):
/// baseUrl = 品牌专属正式地址(prod 时覆盖) ?? 环境通用地址
/// appName = 品牌的名字(桌面图标名 / title)
/// theme = 按品牌的 themeId 查主题注册表(现有 BrandTheme 体系)
///
/// 消费方(Dio / TabApp / 页面)只 import 这里,不碰 AppEnv/AppBrand:
/// 1. ApiConfig.baseUrl → AppConfig.baseUrl
/// 2. TabApp theme/title → AppConfig.theme / AppConfig.appName
///
/// 三层解耦:
/// 环境 = 接口在哪(编译期定死) 品牌 = 这是谁家的 App(发布期定死)
/// 主题 = 长什么样(运行时可切换,与上面两层互不依赖)
/// ============================================================
class AppConfig {
AppConfig._();
/// 当前品牌(--dart-define=BRAND 指定;未知标识兜底回 aixue 防崩)
static AppBrand get currentBrand =>
appBrands[AppEnv.brand] ?? appBrands['aixue']!;
/// App 显示名(桌面图标名 / MaterialApp.title)
static String get appName => currentBrand.appName;
/// 接口地址:prod 用品牌专属地址(同 uni-app 按 appType 覆盖),
/// 其他环境用环境通用地址。
static String get baseUrl =>
AppEnv.isProd ? (currentBrand.baseUrl ?? AppEnv.baseUrl) : AppEnv.baseUrl;
/// 当前品牌对应的主题(查现有 BrandTheme 注册表)
static BrandTheme get theme {
for (final t in brandThemes) {
if (t.name == currentBrand.themeId) return t;
}
return brandThemes.first; // 未知 themeId 兜底第一个主题,防崩
}
/// 小游戏地址(预留,对标 envList.gameUrl)
static String get gameUrl => AppEnv.gameUrl;
/// 学习报告地址(预留,对标 envList.reportUrl)
static String get reportUrl => AppEnv.reportUrl;
}
+63
View File
@@ -0,0 +1,63 @@
/// ============================================================
/// 【AppEnv】环境配置 —— 编译期注入(对标 uni-app 的 envList)
///
/// 一、核心概念:
/// 环境是"打包时定死"的维度:测试包连测试地址,正式包连正式地址。
/// 所以用 `--dart-define` 在编译期注入,代码里是 const,零运行时开销。
///
/// 二、怎么用:
/// flutter run --dart-define=ENV=dev
/// flutter build apk --release --dart-define=ENV=prod
/// 不传时默认 prod(日常 flutter run 不用带参数,行为不变)。
///
/// 三、对标 uni-app(app.config.js envList):
/// dev = 开发/本地调试 test = 测试服 prod = 正式服
/// 每个环境一套 {baseUrl / gameUrl / reportUrl}。
/// ============================================================
class AppEnv {
AppEnv._();
/// 环境:dev / test / prod(默认 prod,日常运行零参数)
static const String env = String.fromEnvironment('ENV', defaultValue: 'prod');
/// 品牌标识:aixue / chucai / yule / ...(默认 aixue=爱学蝶变)
/// 品牌表见 app_brand.dart;主题/接口/App 名都随它走(AppConfig 聚合)
static const String brand =
String.fromEnvironment('BRAND', defaultValue: 'aixue');
/// 三套环境地址表(与 uni-app envList 完全对齐)
static const Map<String, Map<String, String>> _envTable = {
'dev': {
'baseUrl': 'https://k12-student-api-test.23544.com:16400',
'gameUrl': 'https://cocos-test.cnzhyx.cn',
'reportUrl': 'https://report-test.cnzhyx.cn/',
},
'test': {
'baseUrl': 'https://axdb-test.cnzhyx.cn',
'gameUrl': 'https://cocos-test.cnzhyx.cn',
'reportUrl': 'https://report-test.cnzhyx.cn/',
},
'prod': {
// 正式环境的 baseUrl 在 AppConfig 里会被品牌专属地址覆盖(同 uni-app)
'baseUrl': 'https://ylax-api.cqbzyx.com',
'gameUrl': 'https://axdb-cocos.cnzhyx.cn/',
'reportUrl': 'https://axdb-report.cnzhyx.cn/',
},
};
/// 取当前环境某个 key 的地址(key 不存在返回空串,避免线上崩)
static String value(String key) =>
_envTable[env]?[key] ?? _envTable['prod']?[key] ?? '';
/// 当前环境接口地址
static String get baseUrl => value('baseUrl');
/// 当前环境小游戏地址(预留,对标 envList.gameUrl)
static String get gameUrl => value('gameUrl');
/// 当前环境学习报告地址(预留,对标 envList.reportUrl)
static String get reportUrl => value('reportUrl');
/// 是否正式环境(组合规则里判断用:prod 才走品牌专属地址)
static bool get isProd => env == 'prod';
}