Files
aisee/技术文档/Flutter项目初始化.md
2026-03-02 21:26:32 +08:00

22 KiB
Raw Blame History

AISee Flutter 项目初始化指南

1. 创建项目

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

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

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

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

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

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

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

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

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

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

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),
          ],
        ),
      ),
    );
  }
}

其他页面占位

// 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 添加权限

<!-- 在 <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

android {
    defaultConfig {
        minSdkVersion 21  // 改为 21
    }
}

6. 初始化项目命令

# 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 初始化

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 安装依赖,然后按照目录结构创建文件,逐步填充代码。