client_framework/docs/skin_app_development_guide.md
2026-03-26 10:39:39 +08:00

870 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 换皮应用开发完整流程指南
本文档说明如何使用 `client_proxy_framework` 从零创建并完成一个换皮应用。
***
> **重要说明**:本指南**仅完成数据框架的对接**,包括:
>
> - API 请求封装与字段映射
> - 第三方 SDKAdjust、Facebook、Google Play集成
> - 用户认证与归因追踪
>
> **UI 组件需要另行对接**,包括:
>
> - 首页、生成页、个人中心等业务页面
> - 主题配色、字体、图标等 UI 样式
> - 业务组件和交互逻辑
>
> 详见本文档最后一章 [后续开发指引](#后续开发指引)。
***
## 目录
1. [准备阶段:创建项目目录](#1-准备阶段创建项目目录)
2. [填写配置模板](#2-填写配置模板)
3. [创建 Flutter 项目](#3-创建-flutter-项目)
4. [集成框架](#4-集成框架)
5. [配置应用信息](#5-配置应用信息)
6. [配置第三方-sdk](#6-配置第三方-sdk)
7. [实现业务代码](#7-实现业务代码)
8. [调试与测试](#8-调试与测试)
9. [上线准备](#9-上线准备)
10. [后续开发指引](#10-后续开发指引)
***
## 1. 准备阶段:创建项目目录
### 1.1 创建项目目录
在合适的位置创建项目目录:
```bash
# 请将 your_app_name 替换为实际的应用名称
mkdir your_app_name
cd your_app_name
```
### 1.2 复制配置模板
从框架目录复制配置模板到项目的 `docs` 目录下:
```bash
mkdir docs
cp ../client_proxy_framework/docs/new_app_config_template.md docs/
cp ../client_proxy_framework/docs/sdk_integration_guide.md docs/
```
复制后的目录结构:
```
your_app_name/
├── docs/
│ ├── new_app_config_template.md # 应用配置模板
│ └── sdk_integration_guide.md # SDK 集成指南
└── ...
```
### 1.3 打开配置模板
使用编辑器打开配置模板文件:
```bash
# 使用 VSCode
code docs/new_app_config_template.md
# 或使用其他编辑器
open docs/new_app_config_template.md
```
### 1.4 下一步
> **请先完成配置模板的填写**,再继续执行后续步骤。
>
> 配置模板包含以下信息:
> - 应用基本信息appId、packageName、AES 密钥等)
> - Adjust 配置App Token、Event Tokens
> - Facebook 配置App ID、Client Token
> - Google Play 配置(商品 ID
填写完成后,继续执行第 3 步。
***
## 2. 填写配置模板
请参考 `docs/new_app_config_template.md` 文件,填写以下配置信息:
### 2.1 应用基本信息
| 配置项 | 占位符 | 填写值 |
|-------|-------|-------|
| 应用名称 | `填写应用名称` | |
| Android 包名 | `填写 Android 包名` | |
| iOS Bundle ID | `填写 iOS Bundle ID` | |
| 应用 IDappId | `填写 appId` | |
### 2.2 服务器配置
| 配置项 | 占位符 | 填写值 |
|-------|-------|-------|
| 预发布环境 URL | `填写预发布环境 URL` | |
| 生产环境 URL | `填写生产环境 URL` | |
| 代理接口路径 | `填写代理接口路径` | |
### 2.3 Adjust SDK 配置
| 配置项 | 占位符 | 填写值 |
|-------|-------|-------|
| App Token | `填写 Adjust App Token` | |
| Environment | `sandbox` / `production` | |
### 2.4 Facebook SDK 配置
| 配置项 | 占位符 | 填写值 |
|-------|-------|-------|
| App ID | `填写 Facebook App ID` | |
| Client Token | `填写 Client Token` | |
### 2.5 AES 密钥
| 配置项 | 占位符 | 填写值 |
|-------|-------|-------|
| AES 密钥 | `填写 16 字符 AES-128 密钥` | |
> 框架使用 AES-128-ECB 模式,密钥固定为 16 字符。
### 2.6 下一步
> **配置填写完成后,继续执行第 3 步。**
>
> 如果配置复杂或有任何疑问,请将填写好的配置发送给我进行确认。
***
## 3. 创建 Flutter 项目
### 3.1 在项目目录下执行
确保你在之前创建的项目目录下执行:
```bash
# 如果不在项目目录下,先进入
cd your_app_name
```
### 3.2 创建 Flutter 项目
```bash
# 请将 com.yourcompany 替换为你的组织包名
flutter create --org com.yourcompany your_app_name
cd your_app_name
```
### 3.3 复制配置模板到项目
```bash
mkdir docs
cp ../client_proxy_framework/docs/new_app_config_template.md docs/
cp ../client_proxy_framework/docs/sdk_integration_guide.md docs/
```
### 3.4 下一步
> 继续执行 [第 4 步:集成框架](#4-集成框架)
***
## 4. 集成框架
### 4.1 添加依赖
编辑 `pubspec.yaml`
```yaml
dependencies:
flutter:
sdk: flutter
# 框架依赖
client_proxy_framework:
path: ../client_proxy_framework # 或使用 pub 发布的版本
# 框架需要的额外依赖
http: ^1.2.2
encrypt: ^5.0.3
crypto: ^3.0.3
logger: ^2.0.2
shared_preferences: ^2.2.2
device_info_plus: ^11.1.0
```
### 4.2 目录结构
建议按以下结构组织代码:
```
lib/
├── main.dart # 入口文件
├── app.dart # MaterialApp
├── core/
│ ├── config/
│ │ └── app_config.dart # 应用配置类
│ ├── auth/
│ │ └── auth_service.dart # 认证服务实现
│ ├── user/
│ │ └── user_state.dart # 用户状态管理
│ └── theme/
│ ├── app_colors.dart # 主题配色
│ ├── app_typography.dart # 字体样式
│ └── app_spacing.dart # 间距常量
├── features/
│ ├── home/ # 首页模块
│ ├── create/ # 生成页模块
│ ├── profile/ # 个人中心模块
│ └── recharge/ # 充值页模块
└── shared/
├── tab_selector_scope.dart # Tab 导航作用域
└── widgets/ # 共享组件
```
***
## 5. 配置应用信息
### 5.1 创建应用配置类
创建 `lib/core/config/app_config.dart`
```dart
import 'package:client_proxy_framework/client_proxy_framework.dart';
class YourAppConfig implements AppConfig {
@override
String get appId => 'your_app_id';
@override
String get packageName => 'com.yourcompany.yourapp';
@override
String get aesKey => 'your_16_char_key'; // AES-128 需要 16 字符
@override
String get preBaseUrl => 'https://pre-api.example.com';
@override
String get prodBaseUrl => 'https://api.example.com';
@override
String get proxyPath => '/v1/proxy';
@override
FieldMapping? get fieldMapping => DefaultFieldMapping.instance;
}
```
### 5.2 创建用户状态管理
创建 `lib/core/user/user_state.dart`
```dart
import 'package:flutter/foundation.dart';
class UserState {
static String? _userId;
static int _credits = 0;
static String? _avatar;
static String? _userName;
static String? _countryCode;
static String? get userId => _userId;
static int get credits => _credits;
static String? get avatar => _avatar;
static String? get userName => _userName;
static String? get countryCode => _countryCode;
static void setUserId(String id) => _userId = id;
static void setCredits(int credits) => _credits = credits;
static void setAvatar(String avatar) => _avatar = avatar;
static void setUserName(String name) => _userName = name;
static void setCountryCode(String code) => _countryCode = code;
}
```
***
## 6. 配置第三方 SDK
### 6.1 填写配置信息
参考 [SDK 集成配置指南](sdk_integration_guide.md),获取并填写以下信息:
| SDK | 必需信息 |
| ----------- | ---------------------- |
| Adjust | App Token, Environment |
| Facebook | App ID, Client Token |
| Google Play | 无(商品 ID 从后端获取) |
### 6.2 Android 配置
#### 6.2.1 创建 strings.xml
创建 `android/app/src/main/res/values/strings.xml`
```xml
<resources>
<string name="facebook_app_id">your_facebook_app_id</string>
<string name="facebook_client_token">your_facebook_client_token</string>
</resources>
```
#### 6.2.2 更新 AndroidManifest.xml
`<application>` 标签之前添加:
```xml
<!-- Adjust -->
<meta-data
android:name="com.adjust.sdk.appToken"
android:value="your_adjust_app_token" />
<meta-data
android:name="com.adjust.sdk.environment"
android:value="sandbox" />
<!-- Facebook -->
<meta-data
android:name="com.facebook.sdk.ApplicationId"
android:value="@string/facebook_app_id" />
<meta-data
android:name="com.facebook.sdk.ClientToken"
android:value="@string/facebook_client_token" />
```
#### 6.2.3 创建 Application 类
创建 `android/app/src/main/kotlin/com/yourcompany/yourapp/App.kt`
```kotlin
package com.yourcompany.yourapp
import android.app.Application
import android.os.Handler
import android.os.Looper
import com.facebook.FacebookSdk
import com.facebook.LoggingBehavior
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.FlutterEngineCache
import io.flutter.plugin.common.MethodChannel
class App : Application() {
companion object {
const val CHANNEL = "com.yourcompany.yourapp/facebook_sdk"
const val ENGINE_ID = "main"
}
override fun onCreate() {
super.onCreate()
FacebookSdk.sdkInitialize(this)
FacebookSdk.setIsDebugEnabled(true)
FacebookSdk.addLoggingBehavior(LoggingBehavior.APP_EVENTS)
Handler(Looper.getMainLooper()).postDelayed({
val engine = FlutterEngineCache.getInstance().get(ENGINE_ID)
if (engine != null) {
MethodChannel(engine.dartExecutor.binaryMessenger, CHANNEL)
.invokeMethod("onFacebookSdkInitialized", null)
}
}, 100)
}
}
```
#### 6.2.4 创建 MainActivity
创建 `android/app/src/main/kotlin/com/yourcompany/yourapp/MainActivity.kt`
```kotlin
package com.yourcompany.yourapp
import android.os.Handler
import android.os.Looper
import com.facebook.appevents.AppEventsLogger
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.FlutterEngineCache
import io.flutter.plugin.common.MethodChannel
class MainActivity : FlutterActivity() {
companion object {
const val CHANNEL = "com.yourcompany.yourapp/facebook_sdk"
const val ENGINE_ID = "main"
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
AppEventsLogger.activateApp(application)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
.setMethodCallHandler { call, result ->
when (call.method) {
"waitForFacebookSdkInit" -> {
AppEventsLogger.activateApp(application)
result.success(true) // 必须调用 result.success() 否则 Dart 端会卡住
}
"onFacebookSdkInitialized" -> {
result.success(true)
}
else -> {
result.notImplemented()
}
}
}
Handler(Looper.getMainLooper()).postDelayed({
FlutterEngineCache.getInstance().put(ENGINE_ID, flutterEngine)
}, 100)
}
}
```
> **注意**`result.success()` 必须被调用,否则 Dart 端的 `await` 会一直等待响应,导致应用卡住。
#### 6.2.5 更新 AndroidManifest.xml 使用 Application
```xml
<application
android:name=".App"
android:label="Your App Name"
android:icon="@mipmap/ic_launcher">
```
### 6.3 iOS 配置
#### 6.3.1 更新 Info.plist
`ios/Runner/Info.plist` 中添加:
```xml
<key>AdjustAppToken</key>
<string>your_adjust_app_token</string>
<key>AdjustEnvironment</key>
<string>sandbox</string>
<key>FacebookAppID</key>
<string>your_facebook_app_id</string>
<key>FacebookClientToken</key>
<string>your_facebook_client_token</string>
<key>FacebookDisplayName</key>
<string>Your App Name</string>
```
***
## 7. 实现业务代码
### 7.1 创建认证服务实现
创建 `lib/core/auth/auth_service.dart`
```dart
import 'dart:convert';
import 'package:client_proxy_framework/client_proxy_framework.dart';
import 'package:crypto/crypto.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import '../user/user_state.dart';
/// 归因回调实现
class AppAttributionCallbacks implements AttributionCallbacks {
@override
Future<String?> getReferrer() async {
final attribution = AnalyticsService.getAttribution();
if (attribution != null) {
return _attributionToJson(attribution);
}
return '';
}
String _attributionToJson(AttributionData data) {
final map = <String, dynamic>{
'trackerToken': data.trackerToken,
'trackerName': data.trackerName,
'network': data.network,
'campaign': data.campaign,
'adgroup': data.adgroup,
'creative': data.creative,
'clickLabel': data.clickLabel,
'costType': data.costType,
'costAmount': _jsonEncodableCostAmount(data.costAmount),
'costCurrency': data.costCurrency,
'jsonResponse': data.jsonResponse,
'fbInstallReferrer': data.fbInstallReferrer,
};
return base64Encode(utf8.encode(jsonEncode(map)));
}
Object? _jsonEncodableCostAmount(double? v) {
if (v == null) return null;
if (v.isNaN || !v.isFinite) return v.toString();
return v;
}
@override
Future<String?> getAdjustReferrer() async => getReferrer();
@override
Future<String?> getPlatformReferrer() async => null;
}
/// 认证回调实现
class AppAuthCallbacks implements AuthServiceCallbacks {
@override
Future<String> getDeviceId() async {
final deviceInfo = DeviceInfoPlugin();
switch (defaultTargetPlatform) {
case TargetPlatform.android:
final android = await deviceInfo.androidInfo;
return android.id;
case TargetPlatform.iOS:
final ios = await deviceInfo.iosInfo;
return ios.identifierForVendor ?? 'ios-unknown';
default:
return 'device-${DateTime.now().millisecondsSinceEpoch}';
}
}
@override
String computeSign(String deviceId) {
return md5.convert(utf8.encode(deviceId)).toString().toUpperCase();
}
@override
void onLoginSuccess(FastLoginResponse data) {
if (data.userId != null) UserState.setUserId(data.userId!);
if (data.credits != null) UserState.setCredits(data.credits!);
if (data.avatar != null) UserState.setAvatar(data.avatar!);
if (data.userName != null) UserState.setUserName(data.userName!);
}
@override
void onCommonInfoLoaded(CommonInfoResponse data) {
if (data.credits != null) UserState.setCredits(data.credits!);
if (data.avatar != null) UserState.setAvatar(data.avatar!);
if (data.userName != null) UserState.setUserName(data.userName!);
}
@override
void onLoginFailed(String msg) {
debugPrint('[AuthService] Login failed: $msg');
}
}
/// 认证服务
class AuthService {
static final _authCallbacks = AppAuthCallbacks();
static final _attributionCallbacks = AppAttributionCallbacks();
static Future<void> init() async {
AttributionService.init(_attributionCallbacks);
FrameworkAuthService.init(_authCallbacks);
await FrameworkAuthService.start();
}
static Future<void> get loginComplete => FrameworkAuthService.loginComplete;
}
```
### 7.2 创建 main.dart
创建 `lib/main.dart`
```dart
import 'package:flutter/material.dart';
import 'package:client_proxy_framework/client_proxy_framework.dart';
import 'app.dart';
import 'core/auth/auth_service.dart';
import 'core/config/app_config.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 1. 初始化 API 客户端
ApiClient.init(YourAppConfig());
// 2. 初始化分析服务
await AnalyticsService.init(
AnalyticsConfig(
packageName: 'com.yourcompany.yourapp', // 包名MethodChannel 使用
adjustConfig: AdjustConfig(
appToken: 'your_adjust_app_token',
environment: AdjustEnv.sandbox, // 上线改为 production
),
facebookConfig: FacebookConfig(
appId: 'your_facebook_app_id',
clientToken: 'your_facebook_client_token',
),
),
);
// 3. 提前获取归因
await AnalyticsService.initAttribution();
// 4. 启动应用
runApp(const App());
// 5. 初始化认证
AuthService.init();
}
```
### 7.3 创建 App 组件
创建 `lib/app.dart`
```dart
import 'package:flutter/material.dart';
import 'home_screen.dart';
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Your App Name',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const HomeScreen(),
);
}
}
```
***
## 8. 调试与测试
### 8.1 运行调试版本
```bash
flutter run
```
### 8.2 检查日志
运行时应看到以下日志:
```
D/Adjust (xxxxx): Adjust SDK started
I/flutter (xxxxx): [AuthService] start: deviceId=xxx
I/flutter (xxxxx): Facebook App Events initialized (from native callback)
```
### 8.3 常见问题排查
| 问题 | 解决方案 |
| --------------------------------- | ---------------------------------------- |
| Adjust 日志显示 "SANDBOX" | 正常,测试环境会显示 |
| Facebook 报错 "must be initialized" | 检查 AndroidManifest.xml 和 Application 类配置 |
| 归因未上报 | 检查 initAttribution() 是否在 main() 中调用 |
***
## 9. 上线准备
### 9.1 修改环境配置
#### main.dart
```dart
// Adjust 改为 production
adjustConfig: AdjustConfig(
appToken: 'your_production_app_token',
environment: AdjustEnv.production, // 修改这里
),
// Facebook 关闭调试日志
facebookConfig: FacebookConfig(
appId: 'your_facebook_app_id',
clientToken: 'your_facebook_client_token',
debugLogs: false, // 关闭调试日志
),
```
#### Android
```xml
<!-- AndroidManifest.xml -->
<meta-data
android:name="com.adjust.sdk.environment"
android:value="production" />
```
#### iOS
```xml
<!-- Info.plist -->
<key>AdjustEnvironment</key>
<string>production</string>
```
### 9.2 构建发布版本
```bash
# Android
flutter build apk --release
flutter build appbundle --release
# iOS
flutter build ios --release
```
### 9.3 检查清单
- [ ] Adjust App Token 已替换为生产环境
- [ ] Adjust Environment 已改为 production
- [ ] Facebook 调试日志已关闭
- [ ] Facebook Client Token 已更新(生产环境)
- [ ] 应用名称和包名正确
- [ ] 所有第三方 SDK 配置完成
***
## 附录:框架 API 快速参考
### 分析服务
```dart
// 初始化
await AnalyticsService.init(config);
// 埋点
AnalyticsService.trackEvent('event_token');
AnalyticsService.trackPurchase(amount: 9.99, currency: 'USD');
AnalyticsService.trackRegister();
AnalyticsService.trackSubscribe('monthly_vip');
// 获取归因
final attribution = AnalyticsService.getAttribution();
```
### 认证服务
```dart
// 获取登录完成状态
await AuthService.loginComplete;
// 发起登录
await AuthService.init();
```
### API 请求
```dart
// 获取代理客户端
final proxy = ApiClient.instance.proxy;
// GET 请求
final res = await proxy.request(
path: '/v1/endpoint',
method: 'GET',
);
// POST 请求
final res = await proxy.request(
path: '/v1/endpoint',
method: 'POST',
body: {'key': 'value'},
);
// 请求带字段映射的实体
final res = await proxy.requestEntity(
path: '/v1/entity',
method: 'GET',
entityFactory: YourEntity.fromJson,
);
```
***
## 10. 后续开发指引
完成本指南后,数据框架已就绪。接下来需要开发 **UI 业务层**
### 10.1 框架提供的 API 服务
| 服务类 | 用途 |
| ------------- | ------------- |
| `ImageApi` | 图片/视频生成相关 API |
| `PaymentApi` | 支付相关 API |
| `FeedbackApi` | 举报/反馈相关 API |
| `UserApi` | 用户账户相关 API |
### 10.2 框架提供的实体类
参考 `client_proxy_framework/lib/src/entities/` 目录:
| 实体类 | 用途 |
| ------------------ | ------------ |
| `UserEntities` | 用户、登录、通用信息响应 |
| `ImageEntities` | 分类、任务、生成结果 |
| `PaymentEntities` | 商品、订单、支付结果 |
| `FeedbackEntities` | 举报反馈相关 |
### 10.3 典型的 UI 开发流程
```dart
// 1. 在业务页面中使用框架 API
class HomeScreen extends StatefulWidget {
@override
void initState() {
super.initState();
_loadData();
}
Future<void> _loadData() async {
// 等待登录完成(如果需要)
await AuthService.loginComplete;
// 调用框架 API 获取数据
final res = await ImageApi.getCategoryList();
if (res.isSuccess) {
// 处理数据...
}
}
}
// 2. 使用 UserState 管理状态
UserState.setCredits(credits);
UserState.setUserId(userId);
// 3. 调用分析服务埋点
AnalyticsService.trackPurchase(amount: 9.99, currency: 'USD');
AnalyticsService.trackEvent('your_event_token');
```
### 10.4 建议的 UI 开发顺序
1. **首页HomeScreen** - 展示分类和任务卡片
2. **生成页CreateScreen** - 创建生成任务
3. **个人中心ProfileScreen** - 用户信息和设置
4. **充值页RechargeScreen** - 内购支付流程
### 10.5 参考实现
参考 `app_client_1` 项目中的 UI 实现:
- `lib/features/home/home_screen.dart` - 首页实现
- `lib/shared/widgets/` - 共享组件(按钮、卡片等)
- `lib/core/theme/` - 主题配置