Flutter Template 是一个用于快速启动 Flutter 应用开发的项目模板。项目已经预置路由、网络请求、Redux 状态管理、主题配置、响应式适配、JSON 序列化、本地存储、设备信息、应用信息等常见基础能力。
本项目已经在 Windows 环境下通过 Android 模拟器运行验证。由于 Flutter、Dart、Android Gradle Plugin、Kotlin、Gradle、Android SDK 之间存在较强的版本耦合,请不要随意升级或降级关键依赖版本。版本不匹配可能导致 flutter run 阶段出现 Gradle 编译失败、插件接口缺失、Kotlin 编译失败、JDK class file 不兼容、下载超时等问题。
- 修改代码前优先使用 CodeGraph 分析相关调用链,不先做全仓库 grep。
- 修改代码前需要先分析影响范围。
- 修改完成后需要运行测试。
- 每次修改、编辑、新增或删除时,需要同步更新
README.md。
2026-07-04 00:22:00
- iOS CocoaPods 构建关闭
ENABLE_MODULE_VERIFIER,规避media_kit_libs_ios_video在真机 Release 构建中校验模块时找不到Fluttermodule 的问题。
2026-07-04 00:08:00
- Home 页「动态」入口支持长按二次确认清除本地保存的 Client API Key。
AppSheet增加键盘避让,修复移动端新增动态外链弹层被键盘遮挡的问题。
2026-07-03 23:55:00
- 移除
AppConfig.apheliosClientApiKey中硬编码的 Aphelios Client API Key。 - 动态列表页和新增动态页进入前会要求输入 Client API Key,使用
AppField输入并保存到本地。 - 客户端动态和音乐请求改为从本地读取
x-oolaf-client-key,未保存 key 时不发起对应请求。
- Android
- iOS
- Web
- Windows
- macOS
- Linux
当前 iOS 工程已经按自用真机安装做了基础配置:
- Bundle ID:
com.oolaf.flutted - iPhone 显示名称:
Oolaf - iOS 最低版本:
13.0 - CocoaPods 入口:
ios/Podfile
在 Mac mini 上安装到自己的 iPhone 时,仍需要在 Xcode 中完成本机相关配置:
- 打开
ios/Runner.xcworkspace。 - 在
Runner > Signing & Capabilities中选择自己的 Apple ID Team。 - 连接 iPhone,开启开发者模式并信任 Mac。
- 使用 Xcode 选择真机后直接 Run。
不上 TestFlight 和 App Store 时,不需要配置 App Store Connect、上架证书或审核信息。
以下版本是本项目当前能够在 Android 模拟器上运行的参考环境。
| 项目 | 版本或路径 |
|---|---|
| 操作系统 | Windows |
| Flutter SDK | 3.41.9 stable |
| Dart SDK | Flutter 3.41.9 自带 Dart 版本 |
| 项目 Dart SDK 约束 | >=3.0.5 <4.0.0 |
| Flutter SDK 路径示例 | D:\flutter_windows_3.41.9-stable\flutter |
| Git 路径示例 | D:\Git\cmd\git.exe |
| Android SDK 路径示例 | D:\Android-SDK |
| Android Studio JBR | D:\Android Studio\jbr |
| Android Gradle Plugin | 8.6.0 |
| Kotlin Gradle Plugin | 2.1.0 |
| Gradle Wrapper | 8.11.1 |
| Gradle 分发地址 | https://mirrors.cloud.tencent.com/gradle/gradle-8.11.1-all.zip |
| Flutter Android 引擎制品镜像 | https://storage.flutter-io.cn/download.flutter.io |
| Maven 镜像 | 阿里云 Maven 仓库 |
本项目的 Android 构建链路已经针对较新的 Flutter SDK 做过兼容调整,关键版本如下。
文件位置:
android/gradle/wrapper/gradle-wrapper.properties
当前配置:
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.11.1-all.zip使用 Gradle 8.11.1 的原因:
- 旧版本 Gradle 与 JDK 21 不兼容时,可能出现
Unsupported class file major version 65。 - 使用腾讯云 Gradle 镜像可以避免从
services.gradle.org下载超时。
文件位置:
android/settings.gradle
android/build.gradle
当前版本:
id "com.android.application" version "8.6.0" apply false
id "org.jetbrains.kotlin.android" version "2.1.0" apply falseext.kotlin_version = '2.1.0'
classpath 'com.android.tools.build:gradle:8.6.0'这些版本不能随意改动。过低的 AGP 或 Kotlin 版本可能导致 Flutter 提示即将停止支持,也可能在新版本 Flutter、Gradle、JDK 下出现构建错误。
新版本 Flutter 不再支持旧的 apply from 方式加载 Flutter Gradle 插件。本项目已经迁移为 plugins 块声明方式。
android/settings.gradle 中使用:
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.6.0" apply false
id "org.jetbrains.kotlin.android" version "2.1.0" apply false
}android/app/build.gradle 中使用:
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'dev.flutter.flutter-gradle-plugin'
}如果改回旧写法,可能出现以下错误:
You are applying Flutter's app_plugin_loader Gradle plugin imperatively using the apply script method, which is not possible anymore.
以下依赖与当前 Flutter SDK 兼容性关系较强,不建议随意降级。
dependencies:
path_provider: ^2.1.5
flutter_screenutil: ^5.9.3
shared_preferences: ^2.5.5
device_info_plus: ^11.5.0
package_info_plus: ^8.3.0path_provider 已升级到 ^2.1.5。旧版本可能间接锁定到 path_provider_android 2.0.27,该版本仍引用 Flutter v1 embedding 的 PluginRegistry.Registrar,在新版本 Flutter 中会编译失败。
典型错误:
cannot find symbol
PluginRegistry.Registrar
解决方式是升级 path_provider,并重新执行:
flutter clean
flutter pub getshared_preferences 已升级到 ^2.5.5,避免旧 Android 插件版本在新 Flutter 和新 Android 构建链路下出现兼容问题。
当前配置:
device_info_plus: ^11.5.0
package_info_plus: ^8.3.0这两个插件包含 Android Kotlin 代码,和 Kotlin Gradle Plugin、Gradle 缓存关系较密切。Windows 下如果项目位于 D: 盘,而 Pub Cache 位于 C: 盘,Kotlin 增量编译可能出现跨盘符缓存路径错误。
本项目已在 android/gradle.properties 中关闭 Kotlin 增量编译:
kotlin.incremental=false典型错误:
this and base files have different roots
Could not close incremental caches
在国内网络环境下,Flutter Android 构建经常需要下载以下内容:
- Gradle 分发包
- Android Gradle Plugin
- Kotlin Gradle Plugin
- Maven 依赖
- Flutter Android debug 引擎包,例如
x86_64_debug、arm64_v8a_debug
如果默认访问 Google、Maven Central 或 Gradle 官方源,可能出现超时。
典型错误:
java.net.ConnectException: Connection timed out
本项目已经将 Gradle Wrapper 改为腾讯云镜像:
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.11.1-all.zip2026-06-23 对 Oolaf 音频播放链路做了一次 iOS 真机兼容修复,重点是 lib/utils/oolaf_audio_player.dart 与 lib/utils/oolaf_audio_cache_proxy.dart。
- 点击音乐列表项后的调用链为
lib/components/oolaf_music/music_list.dart -> lib/utils/oolaf_audio_player.dart。 - iOS 下如果
just_audio.setUrl()直连远端 FLAC 超时,不再继续把同一条 FLAC URL 交给本地localhostHTTP 代理播放。 - 新逻辑会先把远端音频完整下载到应用缓存目录,再通过
setFilePath()播放本地文件。 - 如果 iOS 首次装载音频仍然卡死,播放器会自动销毁旧的
AudioPlayer实例并重建后重试一次,覆盖冷启动首播偶发失败场景。 - 这样可以规避 iOS 真机上远端 FLAC URL 长时间 loading、点击后一直转圈、最终播放失败的问题。
影响范围:
- iOS Oolaf 音乐播放失败回退路径。
- 音频缓存代理新增“下载到本地缓存文件”的复用能力。
平台说明:
- Android / macOS 仍保持原有缓存代理与直连回退逻辑。
- Web 不走这个本地缓存文件回退。
本项目已经在 android/build.gradle 和 android/settings.gradle 中加入阿里云 Maven 镜像:
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/central' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
maven { url 'https://maven.aliyun.com/repository/public' }典型错误:
Could not download x86_64_debug-1.0.0-xxxx.jar
Could not get resource 'https://storage.googleapis.com/download.flutter.io/...'
Read timed out
本项目已经加入 Flutter 国内 Maven 镜像:
maven { url 'https://storage.flutter-io.cn/download.flutter.io' }同时建议运行命令前设置环境变量:
$env:PUB_HOSTED_URL="https://pub.flutter-io.cn"
$env:FLUTTER_STORAGE_BASE_URL="https://storage.flutter-io.cn"
flutter run也可以一行执行:
$env:PUB_HOSTED_URL="https://pub.flutter-io.cn"; $env:FLUTTER_STORAGE_BASE_URL="https://storage.flutter-io.cn"; flutter runWindows 下建议确认以下环境变量或 PATH 配置。
Flutter SDK 的 bin 目录需要加入 PATH。
示例:
D:\flutter_windows_3.41.9-stable\flutter\bin
验证命令:
flutter --versionFlutter 依赖 Git。Git 的 cmd 目录需要加入 PATH。
示例:
D:\Git\cmd
验证命令:
git --version如果 Flutter 提示找不到 Git,需要检查 PATH 中是否包含 Git,并确认 C:\Windows\System32 也在 PATH 中。
典型错误:
Error: Unable to find git in your PATH.
如果 Flutter 没有识别 Android SDK,可以手动指定:
flutter config --android-sdk "D:\Android-SDK"验证命令:
flutter doctor -v查看设备:
flutter devices如果能看到 Android 模拟器,例如:
sdk gphone64 x86 64
可以运行:
flutter run如果有多个设备,指定设备运行:
flutter run -d emulator-5554安装依赖:
flutter pub get清理构建缓存:
flutter clean重新拉取依赖:
flutter pub get运行项目:
flutter run静态检查:
flutter analyze生成 JSON 序列化代码:
flutter pub run build_runner build --delete-conflicting-outputs构建 Android APK:
flutter build apk如果需要让产物文件名自动带上版本号和时间,并在打包成功后自动执行一次版本自增,请使用仓库脚本,而不是直接执行裸 flutter build。
当前版本来源只使用:
pubspec.yaml的version
产物会输出到:
dist/releases
命名格式:
oolaf flutted <version> <yyyyMMdd HH>
示例:
oolaf flutted 1.0.0 20260601 14.apk
oolaf flutted 1.0.0 20260601 14
其中:
apk会输出成单个.apk文件windows会输出成同名目录,目录内是完整 Windows 发布文件web会输出成同名目录,目录内是完整 Web 发布文件
构建 Android APK 并在成功后自动升级到下一个 patch 版本:
powershell -ExecutionPolicy Bypass -File .\tool\release_build.ps1 -Platform apk构建 Windows:
powershell -ExecutionPolicy Bypass -File .\tool\release_build.ps1 -Platform windows构建 Web:
powershell -ExecutionPolicy Bypass -File .\tool\release_build.ps1 -Platform web一次构建 apk + windows + web:
powershell -ExecutionPolicy Bypass -File .\tool\release_build.ps1 -Platform all如果当前版本是:
1.0.0+1
那么脚本成功后会自动更新为:
1.0.1+2
也就是:
pubspec.yaml的version从1.0.0+1变成1.0.1+2
也可以指定升级策略:
powershell -ExecutionPolicy Bypass -File .\tool\release_build.ps1 -Platform all -Bump patch
powershell -ExecutionPolicy Bypass -File .\tool\release_build.ps1 -Platform all -Bump minor
powershell -ExecutionPolicy Bypass -File .\tool\release_build.ps1 -Platform all -Bump major规则如下:
patch:1.2.3->1.2.4minor:1.2.3->1.3.0major:1.2.3->2.0.0
只验证脚本流程,不执行版本升级:
powershell -ExecutionPolicy Bypass -File .\tool\release_build.ps1 -Platform apk -SkipVersionBump如果你已经提前手动执行过 flutter build,只做产物复制和命名:
powershell -ExecutionPolicy Bypass -File .\tool\release_build.ps1 -Platform apk -SkipBuild错误示例:
Timeout waiting to lock build logic queue
Lock file: android/.gradle/noVersion/buildLogic.lock
原因通常是上一次 Gradle 或 Java 构建进程未退出。
可以在 android 目录执行:
$env:JAVA_HOME="D:\Android Studio\jbr"
$env:PATH="$env:JAVA_HOME\bin;$env:PATH"
.\gradlew --stop然后重新执行:
flutter run建议执行:
flutter clean
flutter pub get
flutter run必要时停止 Gradle daemon 后再试:
.\gradlew --stop第一次执行 flutter run 很慢是正常现象,尤其是在以下场景:
- 刚安装 Flutter SDK
- 刚安装 Android SDK
- 刚创建或首次打开模拟器
- 刚升级 Gradle、AGP、Kotlin
- 刚执行
flutter clean - 需要下载 Flutter Android 引擎包
- 需要下载大量 Maven 依赖
只要不是长时间停在同一个 timeout 错误,首次构建可以等待较久。成功一次后,后续构建会明显变快。
lib/
├── main.dart 应用入口
├── app.config.dart 全局配置常量
├── layouts/ 应用外壳、主题、Store 注入、路由初始化
├── router/ Fluro 路由定义、Handler 和配置
├── store/ Redux 状态管理
├── pages/ 页面级组件
├── components/ 可复用 UI 组件
├── api/ API 请求封装
├── model/ 数据模型和 JSON 序列化
└── utils/ 工具类、DioClient、Cookie 管理等
首页首屏直接展示“基础示例”分组,当前主要用于按任务查找可复用组件和常见交互,不需要先从业务页兜进去。动感音频正在播放提示条固定展示在“基础示例”和“业务模块”之间,避免被业务模块展开状态影响,并与上下分组保持相同横向宽度。
基础示例按“什么时候该用这个组件”组织,而不是按组件名称堆叠:
- 搭页面骨架:页面导航、头部、底栏、tab、侧栏和基础数据流。
- 展示状态与内容:图片、图标、标签、流程状态、长文本、公告和动态数字。
- 收集输入:文本输入、搜索、提交按钮、开关、数量步进和固定长度密码输入。
- 让用户做选择:单选、多选、日期、地区、级联选择和列表顶部筛选。
- 弹出反馈:确认框、轻提示、菜单、动作面板、顶部/底部弹层。
- 处理列表手势:长列表索引、图片预览、滑动行操作和横向轮播。
相关 demo 页面统一放在:
lib/pages/component_demo/
适合做这些事情:
- 组件回归验证
- 交互细节单测前的人眼验收
- 业务接入前先看组件最小行为
Auth 示例模块已接入 /auth route,当前作为账号能力入口页,包含启动鉴权、登录、注册、OTP、忘记密码、重置密码、修改密码、账号绑定/解绑、补全资料、权限申请说明、验证状态、MFA、会话过期、账号安全、登录设备管理、注销账号、条款隐私、第三方登录回调和异常状态等静态流程示例。Auth 页面只做本地表单校验、倒计时和状态演示,不接真实账号接口;业务登录页不要直接复用该示例模块。
虎扑登录页已独立放在 lib/pages/hupu/hupu_login_page.dart,路由为 /hupu/login。虎扑关注页的“立即登录”入口只跳转虎扑登录页,不再依赖 Auth 示例页。
虎扑“关注”页已接入关注流接口,默认展示未登录提示和“为你推荐”用户卡片,支持下拉刷新,推荐用户可进入用户详情,推荐帖子可进入帖子详情。
虎扑“体育”里的“中国篮球”和“国际足球”页已接入体育新闻流,tab 内展示置顶新闻、2 条热门资讯和普通新闻分页流,点击“查看更多”进入热门资讯详情页;详情页默认请求 hotList 第一页并支持上拉加载,列表项复用帖子详情跳转。“中国足球”和“英雄联盟”页分别接入 csl、lol 默认新闻流,新闻列表支持上拉分页且不展示置顶和热门资讯模块,其中“中国足球”顶部展示热门话题 tag。
- 桌面端和移动端交互差异排查
目录:
lib/components/route_page_header/
作用:
- 通用页面顶部 header
- 左侧返回按钮
- 中间标题
- 头像 + 标题 + 副标题组合
- follow / more / trailing 扩展区
项目中已经用于:
- 通用
PageScaffold - Hupu 详情页和部分业务页
component_demo示例页
目录:
lib/components/route_bottom_nav_bar/
当前结构:
lib/components/route_bottom_nav_bar/
├── index.dart
├── route_bottom_nav_bar_item.dart
├── route_bottom_nav_bar_item_state.dart
├── route_bottom_nav_bar_style.dart
└── route_bottom_nav_bar_tile.dart
这个组件最初来自 hupu 的底部导航实现,后来抽成公共组件。现在已经去掉业务命名,只保留通用展示、状态和交互钩子。
不改现有调用也能继续工作的基础能力:
itemsactiveKeyonTap- 中间主操作按钮
isCenterAction activeIcon- 单个 item 的
activeColor/inactiveColor
onReselect当前 tab 已经激活时再次点击的回调。respectBottomSafeArea控制底部安全区 inset 是否算进导航栏高度和 padding。badgeText数字提醒,例如未读数。showDot纯红点提醒,不展示数字。enabled允许临时禁用某个 tab,但不打乱布局。onLongPress只透出长按钩子,业务层自己决定是否弹bottom sheet或别的面板。onDoubleTap适合“回顶部”“重新刷新”“重新聚焦当前 tab”这类行为。itemBuilder对任意状态下的默认 item 渲染做外层包装或整体替换。selectedBuilder只对激活态做定制渲染,不影响普通态。centerActionChild中间按钮可以不再是固定加号,由业务传自定义内容。semanticLabel用于补充无障碍语义。
RouteBottomNavBarStyle 负责管理外观参数。当前可调项包括:
- 导航栏高度
- 内边距
- 背景色
- 顶部分割线颜色和宽度
- 激活 / 非激活 / 禁用颜色
- icon 大小
- 文案字号和字重
- 中间按钮尺寸与配色
- badge 配色
- 红点尺寸和偏移
- 动画开关、时长、曲线、激活缩放比例
当前不是单独维护两套组件,而是在同一个组件上提供两套风格参数:
const RouteBottomNavBarStyle()const RouteBottomNavBarStyle.cupertino()
cupertino() 更偏 iOS 风格的默认高度、间距、颜色和中心按钮视觉,但仍然走同一套交互和同一套扩展点。
如果业务只是想加一个角标、额外文案、胶囊态背景,优先用 builder,而不是 fork 一个新底部导航组件。
itemBuilder 示例:
RouteBottomNavBarItem(
key: 'mine',
label: '我的',
icon: Icons.person_outline,
activeIcon: Icons.person,
showDot: true,
itemBuilder: (context, state, defaultChild) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
defaultChild,
const SizedBox(height: 2),
const Text('NEW'),
],
);
},
)selectedBuilder 示例:
RouteBottomNavBarItem(
key: 'explore',
label: '探索',
icon: Icons.explore_outlined,
activeIcon: Icons.explore,
selectedBuilder: (context, state, defaultChild) {
return DecoratedBox(
decoration: BoxDecoration(
color: const Color(0x141F2329),
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
child: defaultChild,
),
);
},
)为了保持公共性,下面这些内容不应该内置进 route_bottom_nav_bar:
- 业务
bottom sheet - 发帖逻辑
- 登录校验
- 页面切换实现
- 埋点
- 业务跳转
组件只负责:
- 渲染
- 激活态表达
- 交互事件透出
业务层负责:
- 点击后做什么
- 长按后弹什么
- 双击后做什么
- 当前 activeKey 如何维护
- 虎扑主页面:
lib/pages/hupu/index.dart - 组件示例页:
lib/pages/component_demo/route_bottom_nav_bar_demo_page.dart
目录:
lib/components/gallery_preview/
当前公共 API:
GalleryPreviewImageopenGalleryPreview(...)wrapWithGalleryPreviewScrollBehavior(...)
用途:
- 点击缩略图进入全屏图集
- 左右切图
- 双击放大
- 下滑关闭
- 兼容长图阅读
- 桌面端鼠标 / 触控板 / 触笔手势兼容
当前实现拆分为:
lib/components/gallery_preview/
├── index.dart
├── gallery_preview_image.dart
├── gallery_preview_navigation.dart
├── gallery_preview_page.dart
└── gallery_zoomable_image.dart
交互细节:
- 普通图默认居中展示
- 真正长图在未缩放时优先按阅读模式处理
- 缩放后切回
InteractiveViewer做平移 - 顶部关闭按钮和底部提示浮层由预览页内部维护
为了兼容旧引用,lib/pages/video_tabs/short_video_gallery_preview.dart 仍保留一层 deprecated 转发;新代码应直接引用 lib/components/gallery_preview/。
项目使用 redux 和 flutter_redux 做全局状态管理。根状态是强类型的 AppState,当前主要包含:
todosuserInfooolafMusicshortVideo
状态更新通过 copyWith 和分领域 reducer 完成,例如:
todoListReduceruserReduceroolafMusicReducershortVideoReducer
这部分已经不是早期那种 Map<String, dynamic> 挂全局数据的模式。
项目封装了基于 Dio 的 DioClient,包含以下能力:
- 请求错误分类处理
- 请求和响应日志
- 请求取消
- 动态请求头
- 响应数据拦截处理
- 文件上传和下载
- 非 Web 平台 Cookie 持久化
- 401 token 刷新逻辑
- 请求锁和并发控制
项目使用 Fluro 管理路由。路由路径集中定义在 Routes 中,并通过 configureRoutes 注册。不同页面通过独立 Handler 处理,支持查询参数、自定义转场动画和函数式弹窗处理。
项目使用 AppTheme 定义全局浅色主题,默认字体为 NotoSansSC。界面尺寸和字体大小使用 flutter_screenutil 做响应式适配。
全局配置位于:
lib/app.config.dart
示例:
class AppConfig {
static const appEnv = String.fromEnvironment(
'APP_ENV',
defaultValue: 'development',
);
static const customBaseUrl = String.fromEnvironment('BASE_URL');
static const developmentBaseUrl = String.fromEnvironment(
'DEV_BASE_URL',
defaultValue: 'http://10.0.2.2:3000',
);
static const testBaseUrl = String.fromEnvironment('TEST_BASE_URL');
static const productionBaseUrl = String.fromEnvironment(
'PROD_BASE_URL',
defaultValue: 'https://aphelios-api.oolaf.top',
);
}项目不在代码中固定线上 API 域名,通过 --dart-define 注入环境配置。
开发环境默认使用 Android 模拟器访问宿主机的地址:
flutter run等价于:
flutter run --dart-define=APP_ENV=development --dart-define=DEV_BASE_URL=http://10.0.2.2:3000测试环境:
flutter run --dart-define=APP_ENV=test --dart-define=TEST_BASE_URL=https://test-api.example.com生产环境:
flutter run --dart-define=APP_ENV=production生产环境也可以覆盖默认 API 地址:
flutter run --dart-define=APP_ENV=production --dart-define=PROD_BASE_URL=https://api.example.com临时覆盖任意环境的 API 地址:
flutter run --dart-define=BASE_URL=https://mock-api.example.comWeb 打包时同样通过 --dart-define 注入环境配置。生产环境构建:
flutter build web --release --dart-define=APP_ENV=productionWeb 平台如果需要通过代理服务转发接口请求,需要同时打开代理开关并传入代理地址。代理只在 Web 生效:
flutter build web --release --dart-define=APP_ENV=production --dart-define=PROXY=true --dart-define=PROXY_BASE_URL=https://proxy.example.com本地 Web 调试也可以带同样的代理参数运行:
flutter run -d chrome --dart-define=PROXY=true --dart-define=PROXY_BASE_URL=http://101.200.123.220:9212/ --dart-define=PROXY_TOKEN=oolaf-oolaf-oolaf-oolaf如果需要模拟子目录部署路径,可以同时加上 Web renderer 启动参数和页面路径调试,但多数接口、图片、音频代理问题只需要上面的 flutter run -d chrome 命令即可复现。
开启代理后,Web 端的接口请求、CustomNetworkImage 远端图片请求,以及 Oolaf 音乐远端音频播放请求都会改写到代理服务,路径格式为:
{PROXY_BASE_URL}/proxy/{method}/{targetUrl}
Oolaf 自有音乐 CDN 不走 Web 代理,会保持直连。默认跳过的域名来自:
OOLAF_MUSIC_CDN_BASE_URL=https://s1.oolaf.top
常见图片资源也不走 Web 代理,会按 URL path 扩展名直连,例如 .png、.jpg、.jpeg、.webp、.gif、.svg、.avif、.apng、.bmp、.ico。带查询参数的图片地址同样会按 path 判断,例如:
https://i5.hoopchina.com.cn/news-editor/example.png?x-oss-process=image/resize,w_250/format,webp
代理服务需要 token 时,继续传入 PROXY_TOKEN。应用会把该值放到请求头 x-proxy-token:
flutter build web --release --dart-define=APP_ENV=production --dart-define=PROXY=true --dart-define=PROXY_BASE_URL=https://proxy.example.com --dart-define=PROXY_TOKEN=your-proxy-token当前也兼容旧参数名:--dart-define=proxy=true、--dart-define=url=https://proxy.example.com、--dart-define=proxyToken=your-proxy-token。新命令优先使用 PROXY、PROXY_BASE_URL、PROXY_TOKEN。
生产环境并覆盖默认 API 地址:
flutter build web --release --dart-define=APP_ENV=production --dart-define=PROD_BASE_URL=https://api.example.com测试环境构建:
flutter build web --release --dart-define=APP_ENV=test --dart-define=TEST_BASE_URL=https://test-api.example.com临时指定任意 API 地址:
flutter build web --release --dart-define=BASE_URL=https://mock-api.example.com仓库包含两个 GitHub Actions 工作流。
Web Pages 发布:
.github/workflows/flutter-web-publish.yml
该工作流只会在 main-v2 分支收到 push 且本次提交包含 .run-release 文件改动时自动执行,也可以在 GitHub Actions 页面手动触发。它会执行带 Pages 子路径和 Web 代理参数的 Web 构建,并将产物发布到:
dirkhe1051931999/dirkhe1051931999.github.io/flutter-template
Release 打包发布:
.github/workflows/flutter-release.yml
该工作流只会在 main-v2 分支收到 push 且本次提交包含 .run-release 文件改动时自动执行,也可以在 GitHub Actions 页面手动触发。普通代码提交不会触发 Release 打包。
需要触发自动发布时,修改 .run-release 的内容并提交,例如写入版本号、时间或发布说明:
echo "release 2026-06-23 15:30" > .run-release
git add .run-release
git commit -m "chore: trigger release build"
git push自动发布流程:
- 执行
flutter pub get - 执行
flutter build apk - 执行
flutter build web - 在 Windows runner 执行
flutter build windows - 自动计算下一个
vX.Y.Ztag - 推送 tag 到远端
- 创建 GitHub Release
- 上传 APK、Web zip 包和 Windows zip 包
tag 自增规则会优先读取仓库里最新的 vX.Y.Z tag,并将 patch 版本加一。例如最新 tag 是 v1.0.3,下一次 .run-release 触发的 main-v2 提交成功构建后会发布 v1.0.4。如果仓库还没有符合规则的 tag,则从 pubspec.yaml 的 version 起步。
该工作流会分别在 Ubuntu runner 打 APK/Web 包、在 Windows runner 打 Windows 桌面包,不带 --dart-define、--base-href 或其他构建参数。
GitHub Actions 的海外 runner 访问腾讯云 Gradle 镜像可能出现网络问题;同时仓库本地开发配置中的 127.0.0.1:7890 Gradle 代理在 runner 上不可用,会导致 java.net.ConnectException: Connection refused。Release 工作流会在构建 APK 前临时移除 android/gradle.properties 里的 Gradle 代理配置,把 Gradle JVM heap 调整为 -Xmx4096M,并把 android/gradle/wrapper/gradle-wrapper.properties 中的 Gradle 分发地址切换为 https://services.gradle.org/distributions/gradle-8.11.1-all.zip,这些修改只发生在 CI 工作区,不会提交回仓库。
- 不要随意升级或降级 Flutter、Gradle、AGP、Kotlin 和 Android 插件依赖。
- 修改
pubspec.yaml后需要执行flutter pub get。 - 修改 Android Gradle 配置后建议执行
flutter clean。 - iOS/macOS 视频播放使用
media_kit_video的默认libmpv输出配置,不要复用 Android 的gpu输出配置;远程视频会附带移动端User-Agent、Accept-Language和Referer请求头,便于兼容虎扑、凤凰短视频等媒体源。 - iOS 动感音频播放优先直连远程 HTTPS 音频,并附带移动端浏览器请求头;本地缓存代理只作为失败后的兜底,避免 iPhone 因 localhost 代理链路异常导致无声或一直缓冲。
- 排查 iOS 动感音频卡加载时,查看
flutter run控制台里的audio setUrl start/ready日志:source=ios-direct表示直连远程音频,source=ios-localhost-proxy表示已经回退到本地缓存代理;本地代理还会输出audio cache proxy request/upstream和上游 HTTP 状态码。 - 动感音频没有当前播放内容且队列为空时,应立即清理播放持久化数据,不要启动延迟保存 timer,避免页面销毁或测试结束后仍有挂起任务。
- 如果遇到下载超时,优先检查镜像配置和
FLUTTER_STORAGE_BASE_URL。 - 如果遇到 Gradle 锁占用,先停止 Gradle daemon。
- Web 平台下 Cookie 持久化逻辑需要单独适配。
- 修改
lib/model/下带 JSON 序列化注解的模型后,需要重新执行build_runner。
2026-07-03 00:00:00
- 修复动态流正文和链接文字继承异常主题色导致显示为红色的问题。
- 修复动态流首条内容被固定导航栏遮住的问题。
- 将动态流刷新控件改为支持 Windows 鼠标拖拽的刷新实现。
- 清理动态模块
CupertinoButton.minSize弃用警告和缺失const提示。