Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45e867561d | ||
|
|
64fa3b1efe | ||
|
|
faaf2f7283 | ||
|
|
070ec78668 | ||
|
|
88a401512d | ||
|
|
a7ac3b7fc7 | ||
|
|
015677d1a8 |
No files matched your search
@@ -45,13 +45,7 @@ app.*.map.json
|
|||||||
/android/app/release
|
/android/app/release
|
||||||
|
|
||||||
# Local demo media and internal planning are absent from the public repo.
|
# Local demo media and internal planning are absent from the public repo.
|
||||||
# Launcher/splash source icons are required at build time, so keep them tracked.
|
/assets/
|
||||||
/assets/*
|
|
||||||
!/assets/icon/
|
|
||||||
!/assets/icon/*.png
|
|
||||||
!/assets/fonts/
|
|
||||||
!/assets/fonts/*.ttf
|
|
||||||
!/assets/fonts/OFL-*.txt
|
|
||||||
/server/
|
/server/
|
||||||
/docs/
|
/docs/
|
||||||
/AGENTS.md
|
/AGENTS.md
|
||||||
|
|||||||
@@ -1,176 +1,17 @@
|
|||||||
# FruitShorts(ReelShort)客户端
|
一个使用 Flutter 开发的短剧播放 App 原型,面向海外用户,重点探索沉浸式刷剧、找剧和连续观看的移动端体验
|
||||||
|
|
||||||
FruitShorts 是面向印尼市场的短剧 Flutter 客户端。当前版本已连接
|
## 主要功能
|
||||||
`FruitServer` 的 `/app/**` 接口,支持沉浸播放、找剧、账号、观看记录、收藏、
|
|
||||||
下载授权、评论互动、站内通知和中英印尼三语界面。
|
|
||||||
|
|
||||||
## 技术栈
|
- **沉浸式首页**:竖屏播放,上下滑切换短剧,左右滑切换内容类别;支持拖动进度、横屏全屏观看。
|
||||||
|
- **剧集播放**:从详情页进入后按集连续播放,支持选集和本地续看。
|
||||||
|
- **找剧与个人页**:浏览、搜索和查看剧集详情;在个人页查看观看记录、收藏与喜欢的内容。
|
||||||
|
- **互动与多语言**:本地演示评论、系统分享,以及英文和简体中文界面。
|
||||||
|
- **画中画**:Android 接入系统画中画;iOS 画中画尚未实现。
|
||||||
|
|
||||||
- Flutter 3.47.7 / Dart 3.13.5
|
## 技术实现
|
||||||
- Provider 状态管理与依赖注入
|
|
||||||
- dio 网络请求
|
|
||||||
- video_player 播放
|
|
||||||
- shared_preferences 与 `SharedPreferencesAsync` 本地配置
|
|
||||||
- flutter_localizations,支持 `en`、`zh`、`id`
|
|
||||||
- Android 系统画中画
|
|
||||||
|
|
||||||
## 目录结构
|
项目使用 Flutter、Provider 和 `video_player`。播放会话统一管理播放器生命周期;观看进度和演示互动数据保存在本机。首页、找剧、我的共用同一个根页面,切换时保留页面状态。
|
||||||
|
|
||||||
```text
|
## 当前说明
|
||||||
lib/
|
|
||||||
core/
|
|
||||||
config/ 地址、语言和运行时配置
|
|
||||||
data/ models、remote API、repository、controller
|
|
||||||
formatting/ 文案与日期格式化
|
|
||||||
platform/ 触觉等平台能力
|
|
||||||
theme/ 颜色与主题
|
|
||||||
widgets/ 跨页面复用组件
|
|
||||||
l10n/ 三语 ARB 与生成代码
|
|
||||||
pages/ 业务页面
|
|
||||||
test/ 单元、组件、页面和联调测试
|
|
||||||
integration_test/ 设备侧集成测试
|
|
||||||
android/ Android 工程
|
|
||||||
ios/ iOS 工程与 SPM 配置
|
|
||||||
assets/ 图标、图片和本地媒体文件
|
|
||||||
```
|
|
||||||
|
|
||||||
## 已接接口
|
现阶段的剧目与剧集是演示数据,多部剧暂时复用同一段测试视频。
|
||||||
|
|
||||||
以下接口均在 `lib/core/data/remote/` 中有实际调用,共 26 个不同路径、
|
|
||||||
31 个 HTTP 方法级调用。
|
|
||||||
|
|
||||||
| 分组 | 契约 | 接口 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| 账号 | §1.1 | `POST /app/auth/register`、`/login`、`/refresh`、`/logout` |
|
|
||||||
| 内容 | §1.3、§1.4 | `GET /app/home/feed`、`/app/discover/feed`、`/app/categories`、`/app/dramas/{id}`、`/app/episodes/{id}/play` |
|
|
||||||
| 观看记录 | §1.5 | `POST/GET /app/watch/progress`、`GET /app/watch/recent`、`DELETE /app/watch/progress/{dramaId}`、`DELETE /app/watch/progress` |
|
|
||||||
| 收藏 | §1.5 | `POST /app/favorites/toggle`、`GET /app/favorites`、`DELETE /app/favorites/{dramaId}` |
|
|
||||||
| 下载授权 | §1.6 | `GET /app/downloads/policy`、`PUT /app/downloads/settings`、`POST/GET /app/downloads`、`GET /app/downloads/{episodeId}/url`、`POST /app/downloads/{episodeId}/progress`、`DELETE /app/downloads/{episodeId}`、`DELETE /app/downloads` |
|
|
||||||
| 互动 | §1.7 | `GET/POST /app/comments`、`DELETE /app/comments/{id}`、`POST /app/likes/toggle`、`POST /app/reports` |
|
|
||||||
| 公告 | §1.9.2 | `GET /app/notices` |
|
|
||||||
|
|
||||||
客户端没有调用 `/app/config` 或创作者入驻接口。
|
|
||||||
|
|
||||||
## 运行
|
|
||||||
|
|
||||||
### 安装依赖
|
|
||||||
|
|
||||||
```bash
|
|
||||||
flutter pub get
|
|
||||||
```
|
|
||||||
|
|
||||||
### Android USB
|
|
||||||
|
|
||||||
手机开启 USB 调试并连接电脑后:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
adb devices
|
|
||||||
adb reverse tcp:8081 tcp:8081
|
|
||||||
flutter run -d <device> \
|
|
||||||
--dart-define=API_BASE_URL=http://127.0.0.1:8081
|
|
||||||
```
|
|
||||||
|
|
||||||
`adb reverse` 将手机的 `127.0.0.1:8081` 转发到电脑;USB 断开或重新连接后需要
|
|
||||||
重新执行。
|
|
||||||
|
|
||||||
Android 模拟器访问宿主机时使用:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
flutter run --dart-define=API_BASE_URL=http://10.0.2.2:8081
|
|
||||||
```
|
|
||||||
|
|
||||||
### iOS
|
|
||||||
|
|
||||||
手机与 Mac 需在同一网络,首次启动允许“本地网络”权限:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
flutter run -d <udid> \
|
|
||||||
--dart-define=API_BASE_URL=http://<Mac局域网IP>:8081
|
|
||||||
```
|
|
||||||
|
|
||||||
### Debug 运行时地址
|
|
||||||
|
|
||||||
debug 版本可在「我的 → 设置 → 服务器地址」修改后端地址,保存后重启 App 生效。
|
|
||||||
地址优先级:
|
|
||||||
|
|
||||||
```text
|
|
||||||
本地运行时设置 > --dart-define(API_BASE_URL) > 平台默认地址
|
|
||||||
```
|
|
||||||
|
|
||||||
因此不带 `--dart-define` 构建的 debug APK 也可在安装后改成当前电脑 IP。
|
|
||||||
release 版本不显示入口,也不会读取本地覆盖。
|
|
||||||
|
|
||||||
## 环境注意
|
|
||||||
|
|
||||||
### iOS Swift 版本
|
|
||||||
|
|
||||||
`pubspec.yaml` 将 `shared_preferences_foundation` 固定为 `2.5.6`。该版本兼容
|
|
||||||
Xcode 16.2 / Swift 6.0.3;`2.6.0` 的生成代码使用 Swift 6.1 才支持的 trailing
|
|
||||||
comma。不要手工升级该依赖,`flutter pub get` 后应显示
|
|
||||||
`shared_preferences_foundation 2.5.6 (overridden)`。
|
|
||||||
|
|
||||||
### iOS SPM 包缺失
|
|
||||||
|
|
||||||
如果 Xcode 报 `Missing package product FlutterGeneratedPluginSwiftPackage`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
rm -rf ~/Library/Developer/Xcode/DerivedData/Runner-*
|
|
||||||
flutter clean
|
|
||||||
flutter pub get
|
|
||||||
xcodebuild -resolvePackageDependencies \
|
|
||||||
-project ios/Runner.xcodeproj \
|
|
||||||
-scheme Runner
|
|
||||||
```
|
|
||||||
|
|
||||||
随后重新打开工程构建。`Runner.xcscheme` 的 PreAction 会先执行
|
|
||||||
`flutter pub get`,再执行 Flutter prepare。
|
|
||||||
|
|
||||||
### Android libdartjni
|
|
||||||
|
|
||||||
若启动时报 `libdartjni.so` 加载失败,通常是旧安装中的原生库残留:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
adb uninstall com.stormg.lumadrama
|
|
||||||
flutter run -d <device> \
|
|
||||||
--dart-define=API_BASE_URL=http://127.0.0.1:8081
|
|
||||||
```
|
|
||||||
|
|
||||||
## 生产打包(Android)
|
|
||||||
|
|
||||||
生产包只包含以下两个 ABI,**打成一个 APK**,不做 split:
|
|
||||||
|
|
||||||
| ABI | 适用设备 |
|
|
||||||
| --- | --- |
|
|
||||||
| `arm64-v8a` | 现代手机(64 位) |
|
|
||||||
| `armeabi-v7a` | 老 32 位机 |
|
|
||||||
|
|
||||||
```bash
|
|
||||||
flutter build apk --release --target-platform android-arm64,android-arm
|
|
||||||
# 产出单个:build/app/outputs/flutter-apk/app-release.apk(约 37MB)
|
|
||||||
```
|
|
||||||
|
|
||||||
说明:
|
|
||||||
|
|
||||||
- `android/app/build.gradle.kts` 的 `release` 已配置 `abiFilters`,只保留 `arm64-v8a` 与 `armeabi-v7a`,插件也不会再带入 `x86_64` 原生库。
|
|
||||||
- `x86_64` 只用于模拟器,不进入生产包。
|
|
||||||
- 不要直接运行 `flutter build apk --release`:默认会编译三套原生库,包体约 57MB。
|
|
||||||
- 只有需要进一步压缩单机下载体积时,才考虑 `--split-per-abi` 出分架构包(arm64 约 21MB、armv7 约 19MB),默认不用。
|
|
||||||
|
|
||||||
## 门禁
|
|
||||||
|
|
||||||
```bash
|
|
||||||
dart analyze lib test
|
|
||||||
flutter test
|
|
||||||
flutter build apk --debug
|
|
||||||
```
|
|
||||||
|
|
||||||
测试文件按模块放在 `test/` 下,用例名使用
|
|
||||||
`should_<期望>_when_<条件>`。真实后端联调测试在服务未启动时会跳过。
|
|
||||||
|
|
||||||
## 当前边界
|
|
||||||
|
|
||||||
- 下载仅管理服务端授权,不落盘、不续传、不支持离线播放。
|
|
||||||
- 没有独立搜索接口,搜索能力尚未形成完整闭环。
|
|
||||||
- 站内通知没有已读/未读和红点状态。
|
|
||||||
- 支付、金币和会员权益尚未形成客户端闭环。
|
|
||||||
- Android 支持系统画中画;iOS 画中画尚未接入。
|
|
||||||
@@ -29,13 +29,12 @@ android {
|
|||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
// 生产包只保留 arm64-v8a 与 armeabi-v7a,避免插件带入 x86_64 原生库
|
|
||||||
ndk {
|
|
||||||
abiFilters += listOf("arm64-v8a", "armeabi-v7a")
|
|
||||||
}
|
|
||||||
// TODO: Add your own signing config for the release build.
|
// TODO: Add your own signing config for the release build.
|
||||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
signingConfig = signingConfigs.getByName("debug")
|
signingConfig = signingConfigs.getByName("debug")
|
||||||
|
// 反编译保护(法律红线配套):开启 R8 混淆 + 资源压缩,发布前替换正式签名。
|
||||||
|
isMinifyEnabled = true
|
||||||
|
isShrinkResources = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,13 +45,6 @@ kotlin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生产包不允许出现 x86_64:部分插件 AAR 会带入少量 x86_64 原生库,这里显式剔除。
|
|
||||||
androidComponents {
|
|
||||||
onVariants(selector().withBuildType("release")) { variant ->
|
|
||||||
variant.packaging.jniLibs.excludes.add("lib/x86_64/**")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
flutter {
|
flutter {
|
||||||
source = "../.."
|
source = "../.."
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,4 @@
|
|||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<!-- 开发期后端是明文 HTTP,仅调试构建放开。 -->
|
|
||||||
<application android:usesCleartextTraffic="true"/>
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
<!-- Author: fruit.com.cn -->
|
<!-- Author: fruit.com.cn -->
|
||||||
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
|
||||||
<application
|
<application
|
||||||
android:label="FruitShorts"
|
android:label="Nontonki"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher">
|
||||||
android:usesCleartextTraffic="true">
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
@@ -17,7 +15,7 @@
|
|||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
android:windowSoftInputMode="adjustNothing">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
the Android process has started. This theme is visible to the user
|
the Android process has started. This theme is visible to the user
|
||||||
while the Flutter UI initializes. After that, this theme continues
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import android.content.res.Configuration
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Rational
|
import android.util.Rational
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import androidx.core.view.ViewCompat
|
|
||||||
import androidx.core.view.WindowInsetsCompat
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
import io.flutter.embedding.engine.FlutterEngine
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
@@ -18,8 +16,6 @@ class MainActivity : FlutterActivity() {
|
|||||||
private val shareRequestCode = 9041
|
private val shareRequestCode = 9041
|
||||||
private var pendingShare: MethodChannel.Result? = null
|
private var pendingShare: MethodChannel.Result? = null
|
||||||
private var pipChannel: MethodChannel? = null
|
private var pipChannel: MethodChannel? = null
|
||||||
private var keyboardChannel: MethodChannel? = null
|
|
||||||
private var imeInset = 0
|
|
||||||
|
|
||||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||||
super.configureFlutterEngine(flutterEngine)
|
super.configureFlutterEngine(flutterEngine)
|
||||||
@@ -66,37 +62,6 @@ class MainActivity : FlutterActivity() {
|
|||||||
result.error("share_failed", error.message, null)
|
result.error("share_failed", error.message, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
keyboardChannel = MethodChannel(
|
|
||||||
flutterEngine.dartExecutor.binaryMessenger,
|
|
||||||
"com.stormg.lumadrama/keyboard"
|
|
||||||
)
|
|
||||||
keyboardChannel?.setMethodCallHandler { call, result ->
|
|
||||||
if (call.method != "get") {
|
|
||||||
result.notImplemented()
|
|
||||||
return@setMethodCallHandler
|
|
||||||
}
|
|
||||||
val inset = currentImeInset()
|
|
||||||
emitImeInset(inset)
|
|
||||||
result.success(inset)
|
|
||||||
}
|
|
||||||
val decorView = window.decorView
|
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(decorView) { view, insets ->
|
|
||||||
emitImeInset(insets.getInsets(WindowInsetsCompat.Type.ime()).bottom)
|
|
||||||
ViewCompat.onApplyWindowInsets(view, insets)
|
|
||||||
}
|
|
||||||
ViewCompat.requestApplyInsets(decorView)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun currentImeInset(): Int =
|
|
||||||
ViewCompat.getRootWindowInsets(window.decorView)
|
|
||||||
?.getInsets(WindowInsetsCompat.Type.ime())
|
|
||||||
?.bottom
|
|
||||||
?: 0
|
|
||||||
|
|
||||||
private fun emitImeInset(value: Int) {
|
|
||||||
if (value == imeInset) return
|
|
||||||
imeInset = value
|
|
||||||
keyboardChannel?.invokeMethod("insetsChanged", value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) {
|
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) {
|
||||||
@@ -112,11 +77,4 @@ class MainActivity : FlutterActivity() {
|
|||||||
pendingShare = null
|
pendingShare = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(window.decorView, null)
|
|
||||||
keyboardChannel?.setMethodCallHandler(null)
|
|
||||||
keyboardChannel = null
|
|
||||||
super.onDestroy()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item android:drawable="@color/splash_background" />
|
|
||||||
</layer-list>
|
|
||||||
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 43 KiB |
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item android:drawable="@color/splash_background" />
|
|
||||||
</layer-list>
|
|
||||||
|
Before Width: | Height: | Size: 69 B |
@@ -1,9 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Author: fruit.com.cn -->
|
||||||
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@color/splash_background" />
|
||||||
<item>
|
<item>
|
||||||
<bitmap android:gravity="fill" android:src="@drawable/background"/>
|
<bitmap
|
||||||
</item>
|
android:gravity="center"
|
||||||
<item>
|
android:src="@drawable/splash_icon" />
|
||||||
<bitmap android:gravity="center" android:src="@drawable/splash"/>
|
|
||||||
</item>
|
</item>
|
||||||
</layer-list>
|
</layer-list>
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Author: fruit.com.cn -->
|
|
||||||
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item android:drawable="@color/splash_background" />
|
|
||||||
</layer-list>
|
|
||||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 69 B |
@@ -1,9 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Author: fruit.com.cn -->
|
||||||
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@color/splash_background" />
|
||||||
<item>
|
<item>
|
||||||
<bitmap android:gravity="fill" android:src="@drawable/background"/>
|
<bitmap
|
||||||
</item>
|
android:gravity="center"
|
||||||
<item>
|
android:src="@drawable/splash_icon" />
|
||||||
<bitmap android:gravity="center" android:src="@drawable/splash"/>
|
|
||||||
</item>
|
</item>
|
||||||
</layer-list>
|
</layer-list>
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Author: fruit.com.cn -->
|
||||||
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
<foreground>
|
||||||
|
<inset
|
||||||
|
android:drawable="@drawable/ic_launcher_foreground"
|
||||||
|
android:inset="16%" />
|
||||||
|
</foreground>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 813 B |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 24 KiB |
@@ -6,8 +6,6 @@
|
|||||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
<item name="android:windowSplashScreenBackground">@color/splash_background</item>
|
<item name="android:windowSplashScreenBackground">@color/splash_background</item>
|
||||||
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
|
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
|
||||||
</style>
|
<item name="android:windowSplashScreenIconBackgroundColor">@color/splash_background</item>
|
||||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
|
||||||
<item name="android:windowBackground">@color/splash_background</item>
|
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,15 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Author: fruit.com.cn -->
|
<!-- Author: fruit.com.cn -->
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
<!-- Show a splash screen on the activity. Automatically removed when
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
the Flutter engine draws its first frame -->
|
the Flutter engine draws its first frame -->
|
||||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
<item name="android:forceDarkAllowed">false</item>
|
|
||||||
<item name="android:windowFullscreen">false</item>
|
|
||||||
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
|
||||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
|
||||||
</style>
|
</style>
|
||||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
This theme determines the color of the Android Window while your
|
This theme determines the color of the Android Window while your
|
||||||
@@ -18,6 +15,6 @@
|
|||||||
|
|
||||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
<item name="android:windowBackground">@color/splash_background</item>
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -6,8 +6,6 @@
|
|||||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
<item name="android:windowSplashScreenBackground">@color/splash_background</item>
|
<item name="android:windowSplashScreenBackground">@color/splash_background</item>
|
||||||
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
|
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
|
||||||
</style>
|
<item name="android:windowSplashScreenIconBackgroundColor">@color/splash_background</item>
|
||||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
|
||||||
<item name="android:windowBackground">@color/splash_background</item>
|
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -2,5 +2,6 @@
|
|||||||
<!-- Author: fruit.com.cn -->
|
<!-- Author: fruit.com.cn -->
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
<color name="splash_background">#1F1B16</color>
|
<color name="splash_background">#FF000000</color>
|
||||||
</resources>
|
<color name="ic_launcher_background">#D74A7D</color>
|
||||||
|
</resources>
|
||||||
@@ -1,15 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Author: fruit.com.cn -->
|
<!-- Author: fruit.com.cn -->
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
<!-- Show a splash screen on the activity. Automatically removed when
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
the Flutter engine draws its first frame -->
|
the Flutter engine draws its first frame -->
|
||||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
<item name="android:forceDarkAllowed">false</item>
|
|
||||||
<item name="android:windowFullscreen">false</item>
|
|
||||||
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
|
|
||||||
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
|
||||||
</style>
|
</style>
|
||||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
This theme determines the color of the Android Window while your
|
This theme determines the color of the Android Window while your
|
||||||
@@ -18,6 +15,6 @@
|
|||||||
|
|
||||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
<item name="android:windowBackground">@color/splash_background</item>
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -6,6 +6,4 @@
|
|||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<!-- 开发期后端是明文 HTTP,仅调试构建放开。 -->
|
|
||||||
<application android:usesCleartextTraffic="true"/>
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
Copyright 2021 The Outfit Project Authors (https://github.com/Outfitio/Outfit-Fonts)
|
|
||||||
|
|
||||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
||||||
This license is copied below, and is also available with a FAQ at:
|
|
||||||
https://scripts.sil.org/OFL
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------------------------------
|
|
||||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
||||||
-----------------------------------------------------------
|
|
||||||
|
|
||||||
PREAMBLE
|
|
||||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
||||||
development of collaborative font projects, to support the font creation
|
|
||||||
efforts of academic and linguistic communities, and to provide a free and
|
|
||||||
open framework in which fonts may be shared and improved in partnership
|
|
||||||
with others.
|
|
||||||
|
|
||||||
The OFL allows the licensed fonts to be used, studied, modified and
|
|
||||||
redistributed freely as long as they are not sold by themselves. The
|
|
||||||
fonts, including any derivative works, can be bundled, embedded,
|
|
||||||
redistributed and/or sold with any software provided that any reserved
|
|
||||||
names are not used by derivative works. The fonts and derivatives,
|
|
||||||
however, cannot be released under any other type of license. The
|
|
||||||
requirement for fonts to remain under this license does not apply
|
|
||||||
to any document created using the fonts or their derivatives.
|
|
||||||
|
|
||||||
DEFINITIONS
|
|
||||||
"Font Software" refers to the set of files released by the Copyright
|
|
||||||
Holder(s) under this license and clearly marked as such. This may
|
|
||||||
include source files, build scripts and documentation.
|
|
||||||
|
|
||||||
"Reserved Font Name" refers to any names specified as such after the
|
|
||||||
copyright statement(s).
|
|
||||||
|
|
||||||
"Original Version" refers to the collection of Font Software components as
|
|
||||||
distributed by the Copyright Holder(s).
|
|
||||||
|
|
||||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
||||||
or substituting -- in part or in whole -- any of the components of the
|
|
||||||
Original Version, by changing formats or by porting the Font Software to a
|
|
||||||
new environment.
|
|
||||||
|
|
||||||
"Author" refers to any designer, engineer, programmer, technical
|
|
||||||
writer or other person who contributed to the Font Software.
|
|
||||||
|
|
||||||
PERMISSION & CONDITIONS
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
||||||
redistribute, and sell modified and unmodified copies of the Font
|
|
||||||
Software, subject to the following conditions:
|
|
||||||
|
|
||||||
1) Neither the Font Software nor any of its individual components,
|
|
||||||
in Original or Modified Versions, may be sold by itself.
|
|
||||||
|
|
||||||
2) Original or Modified Versions of the Font Software may be bundled,
|
|
||||||
redistributed and/or sold with any software, provided that each copy
|
|
||||||
contains the above copyright notice and this license. These can be
|
|
||||||
included either as stand-alone text files, human-readable headers or
|
|
||||||
in the appropriate machine-readable metadata fields within text or
|
|
||||||
binary files as long as those fields can be easily viewed by the user.
|
|
||||||
|
|
||||||
3) No Modified Version of the Font Software may use the Reserved Font
|
|
||||||
Name(s) unless explicit written permission is granted by the corresponding
|
|
||||||
Copyright Holder. This restriction only applies to the primary font name as
|
|
||||||
presented to the users.
|
|
||||||
|
|
||||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
||||||
Software shall not be used to promote, endorse or advertise any
|
|
||||||
Modified Version, except to acknowledge the contribution(s) of the
|
|
||||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
||||||
permission.
|
|
||||||
|
|
||||||
5) The Font Software, modified or unmodified, in part or in whole,
|
|
||||||
must be distributed entirely under this license, and must not be
|
|
||||||
distributed under any other license. The requirement for fonts to
|
|
||||||
remain under this license does not apply to any document created
|
|
||||||
using the Font Software.
|
|
||||||
|
|
||||||
TERMINATION
|
|
||||||
This license becomes null and void if any of the above conditions are
|
|
||||||
not met.
|
|
||||||
|
|
||||||
DISCLAIMER
|
|
||||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
||||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
||||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
||||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
||||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
Copyright 2020 The Plus Jakarta Sans Project Authors (https://github.com/tokotype/PlusJakartaSans)
|
|
||||||
|
|
||||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
||||||
This license is copied below, and is also available with a FAQ at:
|
|
||||||
http://scripts.sil.org/OFL
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------------------------------
|
|
||||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
||||||
-----------------------------------------------------------
|
|
||||||
|
|
||||||
PREAMBLE
|
|
||||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
||||||
development of collaborative font projects, to support the font creation
|
|
||||||
efforts of academic and linguistic communities, and to provide a free and
|
|
||||||
open framework in which fonts may be shared and improved in partnership
|
|
||||||
with others.
|
|
||||||
|
|
||||||
The OFL allows the licensed fonts to be used, studied, modified and
|
|
||||||
redistributed freely as long as they are not sold by themselves. The
|
|
||||||
fonts, including any derivative works, can be bundled, embedded,
|
|
||||||
redistributed and/or sold with any software provided that any reserved
|
|
||||||
names are not used by derivative works. The fonts and derivatives,
|
|
||||||
however, cannot be released under any other type of license. The
|
|
||||||
requirement for fonts to remain under this license does not apply
|
|
||||||
to any document created using the fonts or their derivatives.
|
|
||||||
|
|
||||||
DEFINITIONS
|
|
||||||
"Font Software" refers to the set of files released by the Copyright
|
|
||||||
Holder(s) under this license and clearly marked as such. This may
|
|
||||||
include source files, build scripts and documentation.
|
|
||||||
|
|
||||||
"Reserved Font Name" refers to any names specified as such after the
|
|
||||||
copyright statement(s).
|
|
||||||
|
|
||||||
"Original Version" refers to the collection of Font Software components as
|
|
||||||
distributed by the Copyright Holder(s).
|
|
||||||
|
|
||||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
||||||
or substituting -- in part or in whole -- any of the components of the
|
|
||||||
Original Version, by changing formats or by porting the Font Software to a
|
|
||||||
new environment.
|
|
||||||
|
|
||||||
"Author" refers to any designer, engineer, programmer, technical
|
|
||||||
writer or other person who contributed to the Font Software.
|
|
||||||
|
|
||||||
PERMISSION & CONDITIONS
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
||||||
redistribute, and sell modified and unmodified copies of the Font
|
|
||||||
Software, subject to the following conditions:
|
|
||||||
|
|
||||||
1) Neither the Font Software nor any of its individual components,
|
|
||||||
in Original or Modified Versions, may be sold by itself.
|
|
||||||
|
|
||||||
2) Original or Modified Versions of the Font Software may be bundled,
|
|
||||||
redistributed and/or sold with any software, provided that each copy
|
|
||||||
contains the above copyright notice and this license. These can be
|
|
||||||
included either as stand-alone text files, human-readable headers or
|
|
||||||
in the appropriate machine-readable metadata fields within text or
|
|
||||||
binary files as long as those fields can be easily viewed by the user.
|
|
||||||
|
|
||||||
3) No Modified Version of the Font Software may use the Reserved Font
|
|
||||||
Name(s) unless explicit written permission is granted by the corresponding
|
|
||||||
Copyright Holder. This restriction only applies to the primary font name as
|
|
||||||
presented to the users.
|
|
||||||
|
|
||||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
||||||
Software shall not be used to promote, endorse or advertise any
|
|
||||||
Modified Version, except to acknowledge the contribution(s) of the
|
|
||||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
||||||
permission.
|
|
||||||
|
|
||||||
5) The Font Software, modified or unmodified, in part or in whole,
|
|
||||||
must be distributed entirely under this license, and must not be
|
|
||||||
distributed under any other license. The requirement for fonts to
|
|
||||||
remain under this license does not apply to any document created
|
|
||||||
using the Font Software.
|
|
||||||
|
|
||||||
TERMINATION
|
|
||||||
This license becomes null and void if any of the above conditions are
|
|
||||||
not met.
|
|
||||||
|
|
||||||
DISCLAIMER
|
|
||||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
||||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
||||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
||||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
||||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
||||||
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 928 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1004 B |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 39 KiB |
@@ -1,99 +0,0 @@
|
|||||||
# 客户端文件说明
|
|
||||||
|
|
||||||
## 目录树
|
|
||||||
|
|
||||||
```text
|
|
||||||
.
|
|
||||||
├── android/ Android 工程与 Gradle 配置
|
|
||||||
├── assets/
|
|
||||||
│ ├── icon/ 启动图标
|
|
||||||
│ ├── images/ 本地图片
|
|
||||||
│ └── media/ 本地视频文件(当前未在 pubspec 声明)
|
|
||||||
├── integration_test/ 设备侧集成测试
|
|
||||||
├── ios/
|
|
||||||
│ ├── Flutter/ Flutter 生成配置与 SPM 临时包
|
|
||||||
│ ├── Runner/ iOS Runner
|
|
||||||
│ ├── Runner.xcodeproj/ Xcode 工程与共享 Scheme
|
|
||||||
│ └── RunnerTests/ iOS 原生测试
|
|
||||||
├── lib/
|
|
||||||
│ ├── core/
|
|
||||||
│ │ ├── config/ 地址、语言、运行时服务器设置
|
|
||||||
│ │ ├── data/ models、remote、repositories、services
|
|
||||||
│ │ ├── formatting/ 多语言文案、日期和错误文本
|
|
||||||
│ │ ├── platform/ 平台能力封装
|
|
||||||
│ │ ├── theme/ 颜色与 ThemeData
|
|
||||||
│ │ └── widgets/ 通用组件
|
|
||||||
│ ├── l10n/ ARB 与本地化生成代码
|
|
||||||
│ └── pages/ 业务页面
|
|
||||||
├── test/ 单元和 Widget 测试
|
|
||||||
├── docs/ 客户端任务与文件说明
|
|
||||||
├── pubspec.yaml 依赖、资源和 Flutter 配置
|
|
||||||
└── README.md 运行、接口与环境说明
|
|
||||||
```
|
|
||||||
|
|
||||||
## 关键文件
|
|
||||||
|
|
||||||
| 路径 | 职责 |
|
|
||||||
| --- | --- |
|
|
||||||
| `lib/main.dart` | 初始化 Flutter、屏幕方向、debug 服务器覆盖并启动 App |
|
|
||||||
| `lib/app.dart` | Provider、repository、controller、ApiClient 和主题装配 |
|
|
||||||
| `lib/core/config/app_config.dart` | 地址优先级、超时、媒体 URL 和平台默认地址 |
|
|
||||||
| `lib/core/config/server_endpoint.dart` | debug 运行时地址规范化、读取、保存和清除 |
|
|
||||||
| `lib/core/config/locale_controller.dart` | 语言选择与系统语言回退 |
|
|
||||||
| `lib/core/data/models/` | 账号、剧目、评论、观看、下载、公告等数据结构 |
|
|
||||||
| `lib/core/data/remote/` | `/app/**` 接口调用;`api_client.dart` 统一鉴权和错误映射 |
|
|
||||||
| `lib/core/data/repositories/` | HTTP 与内存实现的 repository 边界 |
|
|
||||||
| `lib/core/data/services/` | 页面 controller、播放会话、分享、画中画等业务服务 |
|
|
||||||
| `lib/core/formatting/` | 本地化内容、日期与错误文本 |
|
|
||||||
| `lib/core/platform/haptics.dart` | 可注入的触觉反馈封装 |
|
|
||||||
| `lib/core/theme/app_colors.dart` | 克莱因蓝和界面颜色 token |
|
|
||||||
| `lib/core/theme/app_theme.dart` | light/dark ColorScheme 与组件主题 |
|
|
||||||
| `lib/core/widgets/empty_state.dart` | 统一空列表占位 |
|
|
||||||
| `lib/core/widgets/remote_image.dart` | 后端相对图片地址与失败占位 |
|
|
||||||
| `lib/core/widgets/auth_gate.dart` | 未登录操作拦截 |
|
|
||||||
| `lib/core/widgets/download_action.dart` | 下载授权操作 |
|
|
||||||
| `lib/pages/home/` | 根页面、四个底部 tab 和抽屉 |
|
|
||||||
| `lib/pages/player/` | 竖屏播放、选集、进度和播放器交互 |
|
|
||||||
| `lib/pages/discover/` | 首页/发现信息流、轮播、分类与三列剧目网格 |
|
|
||||||
| `lib/pages/drama_detail/` | 剧目详情与剧集入口 |
|
|
||||||
| `lib/pages/auth/` | 登录/注册页面与表单逻辑 |
|
|
||||||
| `lib/pages/mine/` | 我的页、片单、设置、语言与 debug 地址入口 |
|
|
||||||
| `lib/pages/favorites/` | 收藏列表 |
|
|
||||||
| `lib/pages/watch_records/` | 独立观看记录页 |
|
|
||||||
| `lib/pages/downloads/` | 下载授权与策略页面 |
|
|
||||||
| `lib/pages/notices/` | 站内通知分页和详情 |
|
|
||||||
| `lib/pages/interaction/` | 举报弹窗 |
|
|
||||||
| `lib/l10n/*.arb` | en/zh/id 源文案,修改后执行 `flutter gen-l10n` |
|
|
||||||
| `ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme` | iOS SPM 预构建脚本 |
|
|
||||||
|
|
||||||
## 测试结构
|
|
||||||
|
|
||||||
| 目录 | 覆盖范围 |
|
|
||||||
| --- | --- |
|
|
||||||
| `test/auth/` | 登录 controller、表单、视觉与交互 |
|
|
||||||
| `test/catalog/`、`test/category/` | 首页数据、分类和模型解析 |
|
|
||||||
| `test/comment/`、`test/interaction/` | 评论、点赞、举报和弹窗 |
|
|
||||||
| `test/config/` | 三语、系统语言和运行时服务器地址 |
|
|
||||||
| `test/core/` | ApiClient、ID 解析、触觉、分享和画中画 |
|
|
||||||
| `test/discover/`、`test/home/` | 首页、轮播、网格和导航 |
|
|
||||||
| `test/download/`、`test/library/`、`test/watch/` | 下载、收藏、观看记录 |
|
|
||||||
| `test/drama_detail/`、`test/player/`、`test/playback/` | 详情、播放器与播放队列 |
|
|
||||||
| `test/notice/` | 通知分页、空态、详情与入口 |
|
|
||||||
| `test/empty_state/`、`test/theme/` | 空态组件与主题 token |
|
|
||||||
| `test/integration/` | 依赖本机后端的联调测试,服务未启动时跳过 |
|
|
||||||
| `integration_test/app_backend_test.dart` | 设备侧后端连通性测试 |
|
|
||||||
| `test/support/` | fake repository、HTTP adapter、播放器和测试数据 |
|
|
||||||
|
|
||||||
## 生成物
|
|
||||||
|
|
||||||
以下内容由工具生成,不要手工修改:
|
|
||||||
|
|
||||||
- `lib/l10n/app_localizations*.dart`
|
|
||||||
- `*.g.dart`
|
|
||||||
- `.dart_tool/`
|
|
||||||
- `build/`
|
|
||||||
- `ios/Flutter/ephemeral/`
|
|
||||||
- `ios/Runner/GeneratedPluginRegistrant.h`
|
|
||||||
- `ios/Runner/GeneratedPluginRegistrant.m`
|
|
||||||
|
|
||||||
`pubspec.lock` 由 Dart 包管理工具维护,应提交但不手工编辑。
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
# 客户端任务清单
|
|
||||||
|
|
||||||
状态按当前 `develop` 中已合入的功能整理。契约章节以
|
|
||||||
`FruitServer/docs/api-phase1.md` 为准;任务过程记录见
|
|
||||||
`FruitServer/docs/plans/PROGRESS.md`。
|
|
||||||
|
|
||||||
## K1-K13
|
|
||||||
|
|
||||||
| 批次 | 范围 | 状态 | 关键提交或契约章节 | 备注 |
|
|
||||||
| --- | --- | --- | --- | --- |
|
|
||||||
| K1 | 详情、播放票据、观看进度、续看、收藏 | 已完成并合入 | §1.3-§1.5 | 已有三态和基础页面 |
|
|
||||||
| K2 | 分类、观看记录、收藏列表、下载授权管理 | 已完成并合入 | §1.2、§1.5、§1.6 | 下载只管理授权,不落盘 |
|
|
||||||
| K3 | 评论、回复、点赞、举报、分享 | 已完成并合入 | §1.7 | 分享使用系统分享面板 |
|
|
||||||
| K4 | 账号剩余能力 | 未开始 | §1.1 | Google 登录所需凭据和契约范围需核对 |
|
|
||||||
| K5 | 克莱因蓝主题、印尼语适配 | 已完成并合入 | 无接口变更 | 三语为 en/zh/id |
|
|
||||||
| K6 | 登录页、底部导航、触觉反馈 | 已完成并合入 | 无接口变更 | 触觉通过 `Haptics` 封装 |
|
|
||||||
| K7 | 首页轮播、站内通知 | 已完成并合入 | §1.3、§1.9.2 | 通知无已读态 |
|
|
||||||
| K8 | 登录页视觉重设计 | 已完成并合入 | 无接口变更 | 保留原有登录逻辑和校验 |
|
|
||||||
| K9 | 首页全部剧目双列网格 | 已完成并合入 | 无接口变更 | 数据源为 feed 分类结果 |
|
|
||||||
| K10 | 统一空列表占位 | 已完成并合入 | 无接口变更 | 评论弹窗使用 compact 变体 |
|
|
||||||
| K11 | 首页三列、分类首位“全部” | 已完成并合入 | 无接口变更 | 网格比例保持 2:3 |
|
|
||||||
| K12 | 首页去标题、空态视觉重做 | 已完成并合入 | 无接口变更 | 双层同心圆空态 |
|
|
||||||
| K13 | debug 运行时服务器地址 | 已完成并合入 | 无接口变更 | 保存后重启生效,release 不开放 |
|
|
||||||
|
|
||||||
## 待办
|
|
||||||
|
|
||||||
| 事项 | 状态 | 依赖或说明 |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| K4 账号剩余能力 | 待办 | Google 登录需要客户端凭据、平台配置和后端契约,范围需核对 |
|
|
||||||
| B1 支付与金币 | 待办 | 客户端尚未接入 §1.8 的支付/权益闭环,商品、渠道和验证方式需核对 |
|
|
||||||
| 搜索 | 待办 | 当前没有独立搜索接口,接口契约和数据范围需核对 |
|
|
||||||
| 下载落盘 | 待办 | 需要新增文件下载、进度持久化、续传、离线播放和本地清理设计 |
|
|
||||||
|
|
||||||
## 维护规则
|
|
||||||
|
|
||||||
- 新批次完成后更新本表,不写未经确认的提交号。
|
|
||||||
- 功能状态以当前 `develop` 代码和自动化测试为准。
|
|
||||||
- “需核对”表示产品范围、接口或外部凭据尚未确认,不能按已完成处理。
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
# 开发进度
|
|
||||||
|
|
||||||
## 2026-10-21 K96 剧场页服务端分类分页
|
|
||||||
|
|
||||||
- 已完整读取 K96 `docs/plans/INBOX.md`。
|
|
||||||
- 已从最新 `origin/develop` 创建 ReelShort `feat/k96-theater-feed`。
|
|
||||||
- 计划见 FruitServer `docs/plans/2026-10-21-K96-theater-feed.md`。
|
|
||||||
- 客户端将新增 TheaterFeedController,并让剧场「全部 / 分类」改用
|
|
||||||
`/app/feed` 服务端游标分页;不改首页 K94/K95 已交付逻辑。
|
|
||||||
- 已完成 `CatalogApi.feed` / `CatalogRepository.fetchFeed` 可选 `categoryId`,
|
|
||||||
非空才拼请求参数。
|
|
||||||
- 已新增 `TheaterFeedController`:按分类独立缓存,缓存先渲染后静默刷新,
|
|
||||||
10 秒超时、游标追加去重、失败保留旧数据、失败分类切回自动重试。
|
|
||||||
- 剧场页「全部」和分类 chips 已改用 theater feed;滚动剩余不足 400px 触发
|
|
||||||
`loadMore`,下拉刷新同时执行 categories 与 theater 刷新,搜索模式保持独立。
|
|
||||||
- 已补分类参数、缓存切换、分页去重、hasMore 停止、失败保留、失败重试、
|
|
||||||
分类点击请求和滚动加载 widget 测试。
|
|
||||||
|
|
||||||
门禁原始输出:
|
|
||||||
|
|
||||||
```text
|
|
||||||
$ dart analyze lib test
|
|
||||||
Analyzing lib, test...
|
|
||||||
No issues found!
|
|
||||||
|
|
||||||
$ flutter test --no-pub
|
|
||||||
00:52 +553: All tests passed!
|
|
||||||
|
|
||||||
$ flutter build apk --debug
|
|
||||||
Running Gradle task 'assembleDebug'... 24.8s
|
|
||||||
✓ Built build/app/outputs/flutter-apk/app-debug.apk
|
|
||||||
```
|
|
||||||
|
|
||||||
解读:静态分析零告警,553 项全量测试通过,debug APK 构建成功;
|
|
||||||
`pubspec.lock` 已还原,无依赖变更,未安装或操作真机。
|
|
||||||
|
|
||||||
## 2026-10-21 K97 修复 iOS 评论输入弹窗键盘避让
|
|
||||||
|
|
||||||
- 已完整读取 K97 `docs/plans/INBOX.md`。
|
|
||||||
- 已从最新 `origin/develop` 创建 `fix/k97-ios-comment-keyboard`。
|
|
||||||
- 计划见 FruitServer `docs/plans/2026-10-21-K97-ios-comment-keyboard.md`。
|
|
||||||
- Android 保留既有 pinned/channel 策略;iOS 与其他平台改用实时
|
|
||||||
`MediaQuery.viewInsets.bottom`,并补 iOS 键盘避让测试。
|
|
||||||
- 已完成 `_isAndroid` 平台分流;Android 的 channel、80ms 防抖和 pinned 逻辑保持不变,
|
|
||||||
iOS/其他平台直接读取实时 viewInsets,超过屏幕 60% 的异常值按 0 处理。
|
|
||||||
- 面板已使用 160ms `easeOutCubic` AnimatedPadding;Android 不包裹 SafeArea,
|
|
||||||
iOS/其他平台使用 `SafeArea(top: false)` 处理 home indicator。
|
|
||||||
- 已补 iOS insets=300/0 测试,发表按钮在键盘上方;Android pinned 既有用例全部通过。
|
|
||||||
|
|
||||||
门禁原始输出:
|
|
||||||
|
|
||||||
```text
|
|
||||||
$ dart analyze lib test
|
|
||||||
Analyzing lib, test...
|
|
||||||
No issues found!
|
|
||||||
|
|
||||||
$ flutter test --no-pub
|
|
||||||
00:54 +555: All tests passed!
|
|
||||||
|
|
||||||
$ flutter build apk --debug
|
|
||||||
Running Gradle task 'assembleDebug'... 26.2s
|
|
||||||
✓ Built build/app/outputs/flutter-apk/app-debug.apk
|
|
||||||
|
|
||||||
$ flutter build ios --simulator --no-codesign
|
|
||||||
Xcode build done. 44.0s
|
|
||||||
✓ Built build/ios/iphonesimulator/Runner.app
|
|
||||||
```
|
|
||||||
|
|
||||||
解读:静态分析零告警,555 项全量测试通过,Android 与 iOS simulator 构建成功;
|
|
||||||
`pubspec.lock` 已还原,无依赖变更,未安装或操作真机。
|
|
||||||
|
|
||||||
## 2026-10-21 K97.1 剧场搜索框圆角改小
|
|
||||||
|
|
||||||
- 已从最新 `origin/develop` 创建 `fix/k97.1-theater-search-radius`。
|
|
||||||
- 仅将剧场搜索框圆角从 22 调整为 12,高度 44、颜色、聚焦态及
|
|
||||||
清空/识剧逻辑保持不变,并同步更新对应 widget 断言。
|
|
||||||
|
|
||||||
门禁原始输出:
|
|
||||||
|
|
||||||
```text
|
|
||||||
$ dart analyze lib test
|
|
||||||
Analyzing lib, test...
|
|
||||||
No issues found!
|
|
||||||
|
|
||||||
$ flutter test --no-pub
|
|
||||||
00:55 +555: All tests passed!
|
|
||||||
|
|
||||||
$ flutter build apk --debug
|
|
||||||
Running Gradle task 'assembleDebug'... 25.8s
|
|
||||||
✓ Built build/app/outputs/flutter-apk/app-debug.apk
|
|
||||||
```
|
|
||||||
|
|
||||||
解读:静态分析零告警,555 项全量测试通过,debug APK 构建成功;
|
|
||||||
`pubspec.lock` 已还原,无依赖变更,未安装或操作真机。
|
|
||||||
|
|
||||||
## 2026-10-21 K98 修复登录与手机号登录键盘遮挡
|
|
||||||
|
|
||||||
- 已完整读取 K98 `docs/plans/INBOX.md`。
|
|
||||||
- 已从最新 `origin/develop` 创建 `fix/k98-auth-keyboard`。
|
|
||||||
- 计划见 FruitServer `docs/plans/2026-10-21-K98-auth-keyboard.md`。
|
|
||||||
- 将统一 viewInsets/channel 有效键盘高度,并给登录页与手机号弹窗增加
|
|
||||||
焦点/键盘变化后的字段滚动校正。
|
|
||||||
- 已新增 `effectiveKeyboardInset`,兼容 iOS 实时 viewInsets 与 Android
|
|
||||||
`adjustNothing` 下的 MethodChannel 键盘高度。
|
|
||||||
- 登录/注册页已增加 ScrollController、notifier 监听和邮箱/密码焦点校正;
|
|
||||||
手机号弹窗已同样接入手机号/验证码框滚动校正。
|
|
||||||
- 已补 iOS 登录/注册、Android channel、手机号验证码框键盘避让测试。
|
|
||||||
|
|
||||||
门禁原始输出:
|
|
||||||
|
|
||||||
```text
|
|
||||||
$ dart analyze lib test
|
|
||||||
Analyzing lib, test...
|
|
||||||
No issues found!
|
|
||||||
|
|
||||||
$ flutter test --no-pub
|
|
||||||
00:52 +559: All tests passed!
|
|
||||||
|
|
||||||
$ flutter build apk --debug
|
|
||||||
Running Gradle task 'assembleDebug'... 26.2s
|
|
||||||
✓ Built build/app/outputs/flutter-apk/app-debug.apk
|
|
||||||
```
|
|
||||||
|
|
||||||
解读:静态分析零告警,559 项全量测试通过,debug APK 构建成功;
|
|
||||||
`pubspec.lock` 已还原,无依赖变更,未安装或操作真机。
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
// Author: fruit.com.cn
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:fruitshorts/app.dart';
|
|
||||||
import 'package:fruitshorts/core/config/app_config.dart';
|
|
||||||
import 'package:fruitshorts/core/data/remote/api_client.dart';
|
|
||||||
import 'package:fruitshorts/core/data/remote/catalog_api.dart';
|
|
||||||
import 'package:integration_test/integration_test.dart';
|
|
||||||
|
|
||||||
/// 设备侧集成测试:**在手机进程里**真实请求开发机上的 app-api。
|
|
||||||
///
|
|
||||||
/// 跑法(红线第 10 条要求的"设备侧取证"):
|
|
||||||
/// ```
|
|
||||||
/// IP=$(ipconfig getifaddr en0)
|
|
||||||
/// flutter test integration_test -d <device-id> --dart-define=API_BASE_URL=http://$IP:8081
|
|
||||||
/// ```
|
|
||||||
/// 断言失败时会把实际使用的地址一起打出来,便于区分"地址不对"还是"设备连不上"。
|
|
||||||
void main() {
|
|
||||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
|
||||||
|
|
||||||
final baseUrl = AppConfig.apiBaseUrl;
|
|
||||||
|
|
||||||
testWidgets('should_fetch_home_feed_and_drama_detail_when_device_reaches_backend', (
|
|
||||||
tester,
|
|
||||||
) async {
|
|
||||||
debugPrint('[device] API_BASE_URL=$baseUrl');
|
|
||||||
final client = ApiClient(baseUrl: baseUrl);
|
|
||||||
addTearDown(client.close);
|
|
||||||
final api = CatalogApi(client);
|
|
||||||
|
|
||||||
final feed = await api.homeFeed();
|
|
||||||
expect(
|
|
||||||
feed.dramas,
|
|
||||||
isNotEmpty,
|
|
||||||
reason: '设备侧拿不到首页列表(API_BASE_URL=$baseUrl)',
|
|
||||||
);
|
|
||||||
debugPrint('[device] 首页分区=${feed.sections.length} 剧目=${feed.dramas.length}');
|
|
||||||
|
|
||||||
final card = feed.dramas.first;
|
|
||||||
expect(card.id, isNotEmpty, reason: '设备侧首页卡片 id 为空');
|
|
||||||
|
|
||||||
final detail = await api.dramaDetail(card.id);
|
|
||||||
expect(detail.id, card.id, reason: '详情返回的 id 与卡片不一致');
|
|
||||||
expect(
|
|
||||||
detail.episodes,
|
|
||||||
isNotEmpty,
|
|
||||||
reason: '设备侧拿到的详情没有剧集(dramaId=${card.id})',
|
|
||||||
);
|
|
||||||
debugPrint('[device] 详情 id=${detail.id} 剧集=${detail.episodes.length}');
|
|
||||||
});
|
|
||||||
|
|
||||||
testWidgets('should_render_home_drama_list_when_app_boots_on_device', (
|
|
||||||
tester,
|
|
||||||
) async {
|
|
||||||
// 用真 App(真网络、真目录控制器):首页底部标签栏出现即代表"剧目列表加载成功",
|
|
||||||
// 也就是用户在真机上遇到的那个「剧目列表加载失败」已经消失。
|
|
||||||
await tester.pumpWidget(const LumaDramaApp());
|
|
||||||
|
|
||||||
var loaded = false;
|
|
||||||
for (var attempt = 0; attempt < 60 && !loaded; attempt++) {
|
|
||||||
await tester.pump(const Duration(milliseconds: 500));
|
|
||||||
loaded = find.byKey(const Key('home-tabs-bar')).evaluate().isNotEmpty;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(
|
|
||||||
loaded,
|
|
||||||
isTrue,
|
|
||||||
reason: '首页 30 秒内没渲染出剧目列表(API_BASE_URL=$baseUrl)',
|
|
||||||
);
|
|
||||||
debugPrint('[device] 首页剧目列表已渲染:API_BASE_URL=$baseUrl');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1 +1,2 @@
|
|||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||||
#include "Generated.xcconfig"
|
#include "Generated.xcconfig"
|
||||||
@@ -1 +1,2 @@
|
|||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||||
#include "Generated.xcconfig"
|
#include "Generated.xcconfig"
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# Uncomment this line to define a global platform for your project
|
||||||
|
# platform :ios, '13.0'
|
||||||
|
|
||||||
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
|
||||||
|
project 'Runner', {
|
||||||
|
'Debug' => :debug,
|
||||||
|
'Profile' => :release,
|
||||||
|
'Release' => :release,
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutter_root
|
||||||
|
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
|
||||||
|
unless File.exist?(generated_xcode_build_settings_path)
|
||||||
|
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
|
||||||
|
end
|
||||||
|
|
||||||
|
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||||
|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||||
|
return matches[1].strip if matches
|
||||||
|
end
|
||||||
|
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
|
||||||
|
end
|
||||||
|
|
||||||
|
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||||
|
|
||||||
|
flutter_ios_podfile_setup
|
||||||
|
|
||||||
|
target 'Runner' do
|
||||||
|
use_frameworks!
|
||||||
|
|
||||||
|
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||||
|
target 'RunnerTests' do
|
||||||
|
inherit! :search_paths
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
post_install do |installer|
|
||||||
|
installer.pods_project.targets.each do |target|
|
||||||
|
flutter_additional_ios_build_settings(target)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -360,7 +360,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SUPPORTED_PLATFORMS = iphoneos;
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
@@ -487,7 +487,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -538,7 +538,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SUPPORTED_PLATFORMS = iphoneos;
|
SUPPORTED_PLATFORMS = iphoneos;
|
||||||
|
|||||||
@@ -6,27 +6,11 @@
|
|||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
buildImplicitDependencies = "YES">
|
buildImplicitDependencies = "YES">
|
||||||
<PreActions>
|
<PreActions>
|
||||||
<ExecutionAction
|
|
||||||
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
|
||||||
<ActionContent
|
|
||||||
title = "Bootstrap Flutter SPM Package"
|
|
||||||
scriptText = "#!/bin/sh set -e FLUTTER="" if [ -n "${FLUTTER_ROOT:-}" ] && [ -x "$FLUTTER_ROOT/bin/flutter" ]; then FLUTTER="$FLUTTER_ROOT/bin/flutter" elif FLUTTER="$(command -v flutter)"; then : elif [ -x "$HOME/flutter/bin/flutter" ]; then FLUTTER="$HOME/flutter/bin/flutter" elif [ -x "/opt/homebrew/bin/flutter" ]; then FLUTTER="/opt/homebrew/bin/flutter" fi if [ -z "$FLUTTER" ]; then echo "error: Flutter executable not found. Set FLUTTER_ROOT or add Flutter to PATH." >&2 exit 1 fi if [ -z "${SRCROOT:-}" ]; then echo "error: SRCROOT is not set; cannot locate the Flutter project." >&2 exit 1 fi cd "$SRCROOT/.." "$FLUTTER" pub get ">
|
|
||||||
<EnvironmentBuildable>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
|
||||||
BuildableName = "Runner.app"
|
|
||||||
BlueprintName = "Runner"
|
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</EnvironmentBuildable>
|
|
||||||
</ActionContent>
|
|
||||||
</ExecutionAction>
|
|
||||||
<ExecutionAction
|
<ExecutionAction
|
||||||
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
|
||||||
<ActionContent
|
<ActionContent
|
||||||
title = "Run Prepare Flutter Framework Script"
|
title = "Run Prepare Flutter Framework Script"
|
||||||
scriptText = "#!/bin/sh set -e FLUTTER="" if [ -n "${FLUTTER_ROOT:-}" ] && [ -x "$FLUTTER_ROOT/bin/flutter" ]; then FLUTTER="$FLUTTER_ROOT/bin/flutter" elif FLUTTER="$(command -v flutter)"; then : elif [ -x "$HOME/flutter/bin/flutter" ]; then FLUTTER="$HOME/flutter/bin/flutter" elif [ -x "/opt/homebrew/bin/flutter" ]; then FLUTTER="/opt/homebrew/bin/flutter" fi if [ -z "$FLUTTER" ]; then echo "error: Flutter executable not found. Set FLUTTER_ROOT or add Flutter to PATH." >&2 exit 1 fi FLUTTER_ROOT="$(cd "$(dirname "$FLUTTER")/.." && pwd -P)" if [ -z "${SRCROOT:-}" ]; then echo "error: SRCROOT is not set; cannot locate the Flutter project." >&2 exit 1 fi cd "$SRCROOT" "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" prepare ">
|
scriptText = "/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" prepare ">
|
||||||
<EnvironmentBuildable>
|
<EnvironmentBuildable>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ final class RunnerFlutterViewController: FlutterViewController {
|
|||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
view.backgroundColor = UIColor(red: 16.0 / 255.0, green: 12.0 / 255.0, blue: 10.0 / 255.0, alpha: 1)
|
view.backgroundColor = .black
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 866 B After Width: | Height: | Size: 340 B |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 644 B |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 1001 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 499 B |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 958 B |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 644 B |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 822 B |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 889 B |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 1.3 KiB |