first commit

This commit is contained in:
2026-03-02 21:26:32 +08:00
commit e68bb3ac42
8 changed files with 3076 additions and 0 deletions

View File

@@ -0,0 +1,836 @@
# AISee Flutter 项目初始化指南
## 1. 创建项目
```bash
cd C:\Users\xdedmi\Desktop\aisee
# 创建 Flutter 项目
flutter create --org com.aisee --project-name aisee_app --platforms android,ios aisee_app
cd aisee_app
```
## 2. 项目目录结构
```
aisee_app/
├── lib/
│ ├── main.dart # 入口文件
│ ├── app.dart # App 根组件
│ │
│ ├── config/ # 配置
│ │ ├── app_config.dart # 应用配置
│ │ ├── routes.dart # 路由配置
│ │ └── theme.dart # 主题配置
│ │
│ ├── models/ # 数据模型
│ │ ├── detection.dart # 检测结果
│ │ ├── text_region.dart # 文字区域
│ │ ├── scene_description.dart # 场景描述
│ │ └── analysis_result.dart # 分析结果
│ │
│ ├── services/ # 服务层
│ │ ├── api/
│ │ │ ├── api_client.dart # HTTP 客户端
│ │ │ ├── image_api.dart # 图像 API
│ │ │ └── analysis_api.dart # 分析 API
│ │ ├── bluetooth/
│ │ │ ├── bluetooth_service.dart # 蓝牙服务
│ │ │ └── glasses_protocol.dart # 眼镜通信协议
│ │ ├── camera/
│ │ │ └── camera_service.dart # 相机服务
│ │ ├── websocket/
│ │ │ └── ws_service.dart # WebSocket 服务
│ │ └── tts/
│ │ └── tts_service.dart # 语音合成服务
│ │
│ ├── providers/ # 状态管理
│ │ ├── camera_provider.dart
│ │ ├── analysis_provider.dart
│ │ ├── bluetooth_provider.dart
│ │ └── settings_provider.dart
│ │
│ ├── screens/ # 页面
│ │ ├── home/
│ │ │ └── home_screen.dart
│ │ ├── camera/
│ │ │ └── camera_screen.dart
│ │ ├── analysis/
│ │ │ └── analysis_screen.dart
│ │ ├── history/
│ │ │ └── history_screen.dart
│ │ ├── settings/
│ │ │ └── settings_screen.dart
│ │ └── bluetooth/
│ │ └── bluetooth_screen.dart
│ │
│ ├── widgets/ # 通用组件
│ │ ├── detection_overlay.dart # 检测结果叠加层
│ │ ├── loading_indicator.dart
│ │ └── error_widget.dart
│ │
│ └── utils/ # 工具类
│ ├── constants.dart
│ ├── image_utils.dart
│ └── permission_utils.dart
├── assets/ # 静态资源
│ ├── images/
│ ├── icons/
│ └── fonts/
├── test/ # 测试
│ ├── services/
│ ├── providers/
│ └── widgets/
├── android/ # Android 原生代码
├── ios/ # iOS 原生代码
├── pubspec.yaml # 依赖配置
└── analysis_options.yaml # 代码分析配置
```
## 3. 依赖配置 pubspec.yaml
```yaml
name: aisee_app
description: AISee - AI 视觉辅助眼镜系统
publish_to: 'none'
version: 0.1.0+1
environment:
sdk: '>=3.2.0 <4.0.0'
dependencies:
flutter:
sdk: flutter
# UI
cupertino_icons: ^1.0.6
google_fonts: ^6.1.0
flutter_svg: ^2.0.9
cached_network_image: ^3.3.0
shimmer: ^3.0.0
# 状态管理
riverpod: ^2.4.9
flutter_riverpod: ^2.4.9
# 路由
go_router: ^13.0.0
# 网络
dio: ^5.4.0
web_socket_channel: ^2.4.0
# 相机
camera: ^0.10.5+7
image: ^4.1.4
# 蓝牙
flutter_blue_plus: ^1.28.5
# 本地存储
sqflite: ^2.3.0
shared_preferences: ^2.2.2
path_provider: ^2.1.1
hive: ^2.2.3
hive_flutter: ^1.1.0
# 权限
permission_handler: ^11.1.0
# 语音
flutter_tts: ^3.8.5
# 图像处理
image_picker: ^1.0.5
# JSON 序列化
json_annotation: ^4.8.1
freezed_annotation: ^2.4.1
# 工具
logger: ^2.0.2+1
intl: ^0.19.0
uuid: ^4.2.1
connectivity_plus: ^5.0.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.1
# 代码生成
build_runner: ^2.4.7
json_serializable: ^6.7.1
freezed: ^2.4.5
hive_generator: ^2.0.1
# 测试
mockito: ^5.4.3
mocktail: ^1.0.1
flutter:
uses-material-design: true
assets:
- assets/images/
- assets/icons/
fonts:
- family: AISee
fonts:
- asset: assets/fonts/AISee-Regular.ttf
- asset: assets/fonts/AISee-Bold.ttf
weight: 700
```
## 4. 核心代码
### main.dart
```dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'app.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 强制竖屏
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
// 初始化 Hive 本地存储
await Hive.initFlutter();
runApp(
const ProviderScope(
child: AISeeApp(),
),
);
}
```
### app.dart
```dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'config/routes.dart';
import 'config/theme.dart';
class AISeeApp extends ConsumerWidget {
const AISeeApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final router = ref.watch(routerProvider);
return MaterialApp.router(
title: 'AISee',
theme: AISeeTheme.lightTheme,
darkTheme: AISeeTheme.darkTheme,
themeMode: ThemeMode.system,
routerConfig: router,
debugShowCheckedModeBanner: false,
);
}
}
```
### config/theme.dart
```dart
import 'package:flutter/material.dart';
class AISeeTheme {
static const _primaryColor = Color(0xFF2196F3);
static const _secondaryColor = Color(0xFF00BCD4);
static ThemeData get lightTheme {
return ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: _primaryColor,
secondary: _secondaryColor,
brightness: Brightness.light,
),
appBarTheme: const AppBarTheme(
centerTitle: true,
elevation: 0,
),
);
}
static ThemeData get darkTheme {
return ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: _primaryColor,
secondary: _secondaryColor,
brightness: Brightness.dark,
),
appBarTheme: const AppBarTheme(
centerTitle: true,
elevation: 0,
),
);
}
}
```
### config/routes.dart
```dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../screens/home/home_screen.dart';
import '../screens/camera/camera_screen.dart';
import '../screens/analysis/analysis_screen.dart';
import '../screens/history/history_screen.dart';
import '../screens/settings/settings_screen.dart';
import '../screens/bluetooth/bluetooth_screen.dart';
final routerProvider = Provider<GoRouter>((ref) {
return GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomeScreen(),
),
GoRoute(
path: '/camera',
builder: (context, state) => const CameraScreen(),
),
GoRoute(
path: '/analysis',
builder: (context, state) => const AnalysisScreen(),
),
GoRoute(
path: '/history',
builder: (context, state) => const HistoryScreen(),
),
GoRoute(
path: '/settings',
builder: (context, state) => const SettingsScreen(),
),
GoRoute(
path: '/bluetooth',
builder: (context, state) => const BluetoothScreen(),
),
],
);
});
```
### config/app_config.dart
```dart
class AppConfig {
// API 配置
static const String apiBaseUrl = 'https://api.aisee.com';
static const String wsBaseUrl = 'wss://api.aisee.com/ws';
// 开发环境
static const String devApiBaseUrl = 'http://10.0.2.2:8000';
static const String devWsBaseUrl = 'ws://10.0.2.2:8000/ws';
// 图像配置
static const int imageMaxWidth = 1280;
static const int imageMaxHeight = 720;
static const int imageQuality = 85;
// 蓝牙配置
static const String glassesServiceUuid = '0000ffe0-0000-1000-8000-00805f9b34fb';
static const String glassesCharUuid = '0000ffe1-0000-1000-8000-00805f9b34fb';
// 超时配置
static const Duration apiTimeout = Duration(seconds: 30);
static const Duration wsReconnectDelay = Duration(seconds: 5);
}
```
### services/api/api_client.dart
```dart
import 'package:dio/dio.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../config/app_config.dart';
final apiClientProvider = Provider<ApiClient>((ref) {
return ApiClient();
});
class ApiClient {
late final Dio _dio;
ApiClient() {
_dio = Dio(BaseOptions(
baseUrl: AppConfig.devApiBaseUrl,
connectTimeout: AppConfig.apiTimeout,
receiveTimeout: AppConfig.apiTimeout,
headers: {
'Content-Type': 'application/json',
},
));
_dio.interceptors.add(LogInterceptor(
requestBody: true,
responseBody: true,
));
}
// 上传图像
Future<Map<String, dynamic>> uploadImage(List<int> imageBytes, String filename) async {
final formData = FormData.fromMap({
'file': MultipartFile.fromBytes(imageBytes, filename: filename),
});
final response = await _dio.post('/api/v1/images/upload', data: formData);
return response.data;
}
// 请求 AI 分析
Future<Map<String, dynamic>> analyzeImage(String imageUrl) async {
final response = await _dio.post('/api/v1/analysis/analyze', data: {
'image_url': imageUrl,
});
return response.data;
}
}
```
### services/camera/camera_service.dart
```dart
import 'package:camera/camera.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:logger/logger.dart';
final cameraServiceProvider = Provider<CameraService>((ref) {
return CameraService();
});
class CameraService {
final _logger = Logger();
CameraController? _controller;
List<CameraDescription> _cameras = [];
CameraController? get controller => _controller;
bool get isInitialized => _controller?.value.isInitialized ?? false;
Future<void> initialize() async {
_cameras = await availableCameras();
if (_cameras.isEmpty) {
_logger.e('No cameras available');
return;
}
_controller = CameraController(
_cameras.first,
ResolutionPreset.medium,
enableAudio: false,
imageFormatGroup: ImageFormatGroup.jpeg,
);
await _controller!.initialize();
_logger.i('Camera initialized');
}
Future<XFile?> takePicture() async {
if (!isInitialized) return null;
return await _controller!.takePicture();
}
void dispose() {
_controller?.dispose();
}
}
```
### services/bluetooth/bluetooth_service.dart
```dart
import 'dart:async';
import 'dart:convert';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:logger/logger.dart';
import '../../config/app_config.dart';
final bluetoothServiceProvider = Provider<BluetoothService>((ref) {
return BluetoothService();
});
class BluetoothService {
final _logger = Logger();
BluetoothDevice? _connectedDevice;
BluetoothCharacteristic? _writeCharacteristic;
final _dataController = StreamController<List<int>>.broadcast();
Stream<List<int>> get dataStream => _dataController.stream;
bool get isConnected => _connectedDevice != null;
// 扫描设备
Future<List<ScanResult>> scanDevices({Duration timeout = const Duration(seconds: 5)}) async {
final results = <ScanResult>[];
await FlutterBluePlus.startScan(timeout: timeout);
FlutterBluePlus.scanResults.listen((scanResults) {
results.addAll(scanResults);
});
await Future.delayed(timeout);
await FlutterBluePlus.stopScan();
return results;
}
// 连接设备
Future<bool> connect(BluetoothDevice device) async {
try {
await device.connect(timeout: const Duration(seconds: 10));
_connectedDevice = device;
// 发现服务
final services = await device.discoverServices();
for (var service in services) {
if (service.uuid.toString() == AppConfig.glassesServiceUuid) {
for (var char in service.characteristics) {
if (char.uuid.toString() == AppConfig.glassesCharUuid) {
_writeCharacteristic = char;
// 监听数据
await char.setNotifyValue(true);
char.onValueReceived.listen((data) {
_dataController.add(data);
});
}
}
}
}
_logger.i('Connected to ${device.platformName}');
return true;
} catch (e) {
_logger.e('Connection failed: $e');
return false;
}
}
// 发送数据到眼镜
Future<void> sendToGlasses(Map<String, dynamic> data) async {
if (_writeCharacteristic == null) return;
final jsonStr = jsonEncode(data);
final bytes = utf8.encode(jsonStr);
await _writeCharacteristic!.write(bytes);
}
// 断开连接
Future<void> disconnect() async {
await _connectedDevice?.disconnect();
_connectedDevice = null;
_writeCharacteristic = null;
}
void dispose() {
_dataController.close();
disconnect();
}
}
```
### screens/home/home_screen.dart
```dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
class HomeScreen extends ConsumerWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
title: const Text('AISee'),
actions: [
IconButton(
icon: const Icon(Icons.bluetooth),
onPressed: () => context.push('/bluetooth'),
),
IconButton(
icon: const Icon(Icons.settings),
onPressed: () => context.push('/settings'),
),
],
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// 状态卡片
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
const Icon(Icons.visibility, size: 48, color: Colors.blue),
const SizedBox(height: 8),
Text(
'AISee 视觉辅助',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 4),
const Text('眼镜未连接', style: TextStyle(color: Colors.grey)),
],
),
),
),
const SizedBox(height: 24),
// 功能按钮
Expanded(
child: GridView.count(
crossAxisCount: 2,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
children: [
_FeatureCard(
icon: Icons.camera_alt,
label: '拍照识别',
color: Colors.blue,
onTap: () => context.push('/camera'),
),
_FeatureCard(
icon: Icons.text_fields,
label: '文字识别',
color: Colors.green,
onTap: () => context.push('/camera'),
),
_FeatureCard(
icon: Icons.landscape,
label: '场景理解',
color: Colors.orange,
onTap: () => context.push('/camera'),
),
_FeatureCard(
icon: Icons.history,
label: '历史记录',
color: Colors.purple,
onTap: () => context.push('/history'),
),
],
),
),
],
),
),
);
}
}
class _FeatureCard extends StatelessWidget {
final IconData icon;
final String label;
final Color color;
final VoidCallback onTap;
const _FeatureCard({
required this.icon,
required this.label,
required this.color,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, size: 48, color: color),
const SizedBox(height: 8),
Text(label, style: Theme.of(context).textTheme.titleMedium),
],
),
),
);
}
}
```
### 其他页面占位
```dart
// screens/camera/camera_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class CameraScreen extends ConsumerWidget {
const CameraScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('拍照识别')),
body: const Center(child: Text('相机页面 - 待开发')),
);
}
}
// screens/analysis/analysis_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class AnalysisScreen extends ConsumerWidget {
const AnalysisScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('分析结果')),
body: const Center(child: Text('分析结果页面 - 待开发')),
);
}
}
// screens/history/history_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class HistoryScreen extends ConsumerWidget {
const HistoryScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('历史记录')),
body: const Center(child: Text('历史记录页面 - 待开发')),
);
}
}
// screens/settings/settings_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class SettingsScreen extends ConsumerWidget {
const SettingsScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('设置')),
body: const Center(child: Text('设置页面 - 待开发')),
);
}
}
// screens/bluetooth/bluetooth_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class BluetoothScreen extends ConsumerWidget {
const BluetoothScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('蓝牙连接')),
body: const Center(child: Text('蓝牙连接页面 - 待开发')),
);
}
}
```
## 5. Android 权限配置
### android/app/src/main/AndroidManifest.xml 添加权限
```xml
<!-- 在 <manifest> 标签内添加 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
```
### android/app/build.gradle 修改最低 SDK
```groovy
android {
defaultConfig {
minSdkVersion 21 // 改为 21
}
}
```
## 6. 初始化项目命令
```bash
# 1. 创建项目
cd C:\Users\xdedmi\Desktop\aisee
flutter create --org com.aisee --project-name aisee_app --platforms android,ios aisee_app
cd aisee_app
# 2. 获取依赖
flutter pub get
# 3. 运行代码生成freezed、json_serializable
dart run build_runner build --delete-conflicting-outputs
# 4. 运行项目
flutter run
# 5. 运行测试
flutter test
```
## 7. Git 初始化
```bash
cd C:\Users\xdedmi\Desktop\aisee\aisee_app
git init
git add .
git commit -m "feat: 初始化 AISee Flutter 项目"
```
## 8. 开发顺序建议
```
第 1 周:项目搭建 + 首页 UI
第 2 周:相机模块 + 拍照功能
第 3 周API 对接 + 图像上传
第 4 周AI 分析结果展示
第 5 周:蓝牙通信模块
第 6 周:语音播报 + 历史记录
第 7 周:设置页面 + 优化
第 8 周:测试 + Bug 修复
```
---
**提示**:先运行 `flutter pub get` 安装依赖,然后按照目录结构创建文件,逐步填充代码。