895 lines
22 KiB
Markdown
895 lines
22 KiB
Markdown
# 换皮应用开发完整流程指南
|
||
|
||
本文档说明如何使用 `client_proxy_framework` 从零创建并完成一个换皮应用。
|
||
|
||
***
|
||
|
||
> **重要说明**:本指南**仅完成数据框架的对接**,包括:
|
||
>
|
||
> - API 请求封装与字段映射
|
||
> - 第三方 SDK(Adjust、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` | |
|
||
| 应用 ID(appId) | `填写 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
|
||
android_id: ^0.5.1
|
||
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 'dart:math';
|
||
|
||
import 'package:android_id/android_id.dart';
|
||
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 'package:shared_preferences/shared_preferences.dart';
|
||
|
||
import '../user/user_state.dart';
|
||
|
||
const _prefsKeyFallbackDeviceId = 'persisted_device_id';
|
||
|
||
Future<String> _persistedFallbackDeviceId() async {
|
||
final prefs = await SharedPreferences.getInstance();
|
||
var id = prefs.getString(_prefsKeyFallbackDeviceId);
|
||
if (id != null && id.isNotEmpty) return id;
|
||
final random = Random.secure();
|
||
final bytes = List<int>.generate(16, (_) => random.nextInt(256));
|
||
id = base64UrlEncode(bytes).replaceAll('=', '');
|
||
await prefs.setString(_prefsKeyFallbackDeviceId, id);
|
||
return id;
|
||
}
|
||
|
||
/// 归因回调实现
|
||
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 {
|
||
/// 与 app_client 一致:Android 用 Settings.Secure.ANDROID_ID(`android_id` 包);
|
||
/// `device_info_plus` 的 `AndroidDeviceInfo.id` 是 ROM 构建号,不能作为设备 ID。
|
||
/// iOS 用 `identifierForVendor`;读失败或其它平台用 SharedPreferences 持久化随机 id。
|
||
@override
|
||
Future<String> getDeviceId() async {
|
||
switch (defaultTargetPlatform) {
|
||
case TargetPlatform.android:
|
||
final androidId = await const AndroidId().getId();
|
||
if (androidId != null && androidId.isNotEmpty) {
|
||
return androidId;
|
||
}
|
||
return _persistedFallbackDeviceId();
|
||
case TargetPlatform.iOS:
|
||
final ios = await DeviceInfoPlugin().iosInfo;
|
||
final idfv = ios.identifierForVendor;
|
||
if (idfv != null && idfv.isNotEmpty) return idfv;
|
||
return _persistedFallbackDeviceId();
|
||
default:
|
||
return _persistedFallbackDeviceId();
|
||
}
|
||
}
|
||
|
||
@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/` - 主题配置
|
||
|