626 lines
18 KiB
Markdown
626 lines
18 KiB
Markdown
|
|
# App Carwatcher 车辆监控模块
|
||
|
|
|
||
|
|
## 模块概述
|
||
|
|
|
||
|
|
`app_carwatcher` 是 OneApp 车联网生态中的车辆监控模块,负责实时监控车辆状态、位置信息、安全警报等功能。该模块为用户提供全方位的车辆安全监控服务,确保车辆的安全性和用户的使用体验。
|
||
|
|
|
||
|
|
### 基本信息
|
||
|
|
- **模块名称**: app_carwatcher
|
||
|
|
- **版本**: 0.1.2
|
||
|
|
- **描述**: 车辆监控应用模块
|
||
|
|
- **Flutter 版本**: >=2.10.5
|
||
|
|
- **Dart 版本**: >=3.0.0 <4.0.0
|
||
|
|
|
||
|
|
## 功能特性
|
||
|
|
|
||
|
|
### 核心功能
|
||
|
|
1. **实时车辆监控**
|
||
|
|
- 车辆位置实时追踪
|
||
|
|
- 车辆状态监控(锁车状态、车窗状态等)
|
||
|
|
- 车辆异常报警
|
||
|
|
|
||
|
|
2. **地理围栏管理**
|
||
|
|
- 围栏设置和编辑
|
||
|
|
- 围栏事件监控
|
||
|
|
- POI 地点搜索
|
||
|
|
|
||
|
|
3. **历史报告查看**
|
||
|
|
- 日报详情查看
|
||
|
|
- 事件详情展示
|
||
|
|
- 事件过滤功能
|
||
|
|
|
||
|
|
4. **地图集成**
|
||
|
|
- 车辆位置可视化
|
||
|
|
- 历史轨迹查看
|
||
|
|
- 地理围栏设置
|
||
|
|
|
||
|
|
## 技术架构
|
||
|
|
|
||
|
|
### 目录结构
|
||
|
|
```
|
||
|
|
lib/
|
||
|
|
├── app_carwatcher.dart # 模块入口文件
|
||
|
|
├── src/ # 源代码目录
|
||
|
|
│ ├── app_carwatcher_module.dart # 模块定义和依赖注入
|
||
|
|
│ ├── route_dp.dart # 路由配置
|
||
|
|
│ ├── route_export.dart # 路由导出
|
||
|
|
│ ├── blocs/ # 状态管理
|
||
|
|
│ ├── constants/ # 常量定义
|
||
|
|
│ ├── pages/ # 页面组件
|
||
|
|
│ │ ├── home/ # 首页
|
||
|
|
│ │ ├── geo_fence_list/ # 地理围栏列表
|
||
|
|
│ │ ├── geo_fence_detail/ # 地理围栏详情
|
||
|
|
│ │ ├── geo_fence_search_poi/ # POI搜索
|
||
|
|
│ │ ├── daily_report/ # 日报详情
|
||
|
|
│ │ ├── event_detail/ # 事件详情
|
||
|
|
│ │ └── event_filter/ # 事件过滤
|
||
|
|
│ └── utils/ # 工具类
|
||
|
|
├── generated/ # 代码生成文件
|
||
|
|
└── l10n/ # 国际化文件
|
||
|
|
```
|
||
|
|
|
||
|
|
## 核心实现
|
||
|
|
|
||
|
|
### 1. 模块入口文件
|
||
|
|
|
||
|
|
**文件**: `lib/app_carwatcher.dart`
|
||
|
|
|
||
|
|
```dart
|
||
|
|
/// Carwatcher APP
|
||
|
|
library app_carwatcher;
|
||
|
|
|
||
|
|
export 'src/app_carwatcher_module.dart';
|
||
|
|
export 'src/route_dp.dart';
|
||
|
|
export 'src/route_export.dart';
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. 模块定义与路由配置
|
||
|
|
|
||
|
|
**文件**: `src/app_carwatcher_module.dart`
|
||
|
|
|
||
|
|
```dart
|
||
|
|
import 'package:basic_modular/modular.dart';
|
||
|
|
import 'package:basic_modular_route/basic_modular_route.dart';
|
||
|
|
|
||
|
|
import 'route_export.dart';
|
||
|
|
|
||
|
|
/// Module负责的页面
|
||
|
|
class AppCarwatcherModule extends Module with RouteObjProvider {
|
||
|
|
@override
|
||
|
|
List<ModularRoute> get routes {
|
||
|
|
final moduleHome = RouteCenterAPI.routeMetaBy(AppCarwatcherRouteExport.keyModule);
|
||
|
|
final carWatcherHomePage = RouteCenterAPI.routeMetaBy(AppCarwatcherRouteExport.keyCarWatcherHomePage);
|
||
|
|
final geoFenceListPage = RouteCenterAPI.routeMetaBy(AppCarwatcherRouteExport.keyGeoFenceListPage);
|
||
|
|
final geoFenceDetailPage = RouteCenterAPI.routeMetaBy(AppCarwatcherRouteExport.keyGeoFenceDetailPage);
|
||
|
|
final searchPoiPage = RouteCenterAPI.routeMetaBy(AppCarwatcherRouteExport.keyGeoFenceSearchPOIPage);
|
||
|
|
final dailyReportDetailPage = RouteCenterAPI.routeMetaBy(AppCarwatcherRouteExport.keyDailyReportPage);
|
||
|
|
final eventFilterPage = RouteCenterAPI.routeMetaBy(AppCarwatcherRouteExport.keyEventFilterPage);
|
||
|
|
final reportDetailPage = RouteCenterAPI.routeMetaBy(AppCarwatcherRouteExport.keyReportDetailPage);
|
||
|
|
|
||
|
|
return [
|
||
|
|
ChildRoute<dynamic>(moduleHome.path, child: (_, args) => moduleHome.provider(args).as()),
|
||
|
|
ChildRoute<dynamic>(carWatcherHomePage.path, child: (_, args) => carWatcherHomePage.provider(args).as()),
|
||
|
|
ChildRoute<dynamic>(geoFenceListPage.path, child: (_, args) => geoFenceListPage.provider(args).as()),
|
||
|
|
ChildRoute<dynamic>(geoFenceDetailPage.path, child: (_, args) => geoFenceDetailPage.provider(args).as()),
|
||
|
|
ChildRoute<dynamic>(searchPoiPage.path, child: (_, args) => searchPoiPage.provider(args).as()),
|
||
|
|
ChildRoute<dynamic>(dailyReportDetailPage.path, child: (_, args) => dailyReportDetailPage.provider(args).as()),
|
||
|
|
ChildRoute<dynamic>(eventFilterPage.path, child: (_, args) => eventFilterPage.provider(args).as()),
|
||
|
|
ChildRoute<dynamic>(reportDetailPage.path, child: (_, args) => reportDetailPage.provider(args).as()),
|
||
|
|
];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. 路由导出管理
|
||
|
|
|
||
|
|
**文件**: `src/route_export.dart`
|
||
|
|
|
||
|
|
```dart
|
||
|
|
import 'package:basic_modular/modular.dart';
|
||
|
|
import 'package:clr_carwatcher/clr_carwatcher.dart';
|
||
|
|
import 'package:ui_mapview/ui_mapview.dart';
|
||
|
|
|
||
|
|
import 'app_carwatcher_module.dart';
|
||
|
|
import 'pages/daily_report/daily_report_detail_page.dart';
|
||
|
|
import 'pages/event_detail/report_event_detail_page.dart';
|
||
|
|
import 'pages/event_filter/event_filter_page.dart';
|
||
|
|
import 'pages/geo_fence_detail/geo_fence_detail_page.dart';
|
||
|
|
import 'pages/geo_fence_list/geo_fence_list_page.dart';
|
||
|
|
import 'pages/geo_fence_search_poi/geo_fence_search_poi_page.dart';
|
||
|
|
import 'pages/home/car_watcher_home_page.dart';
|
||
|
|
|
||
|
|
/// app_carwatcher的路由管理
|
||
|
|
class AppCarwatcherRouteExport implements RouteExporter {
|
||
|
|
// 路由常量定义
|
||
|
|
static const String keyModule = 'app_carWatcher';
|
||
|
|
static const String keyCarWatcherHomePage = 'app_carWatcher.carWatcher_home';
|
||
|
|
static const String keyGeoFenceListPage = 'app_carWatcher.geoFenceList';
|
||
|
|
static const String keyGeoFenceDetailPage = 'app_carWatcher.geoFenceDetail';
|
||
|
|
static const String keyGeoFenceSearchPOIPage = 'app_carWatcher.poiSearch';
|
||
|
|
static const String keyDailyReportPage = 'app_carWatcher.dailyReport';
|
||
|
|
static const String keyEventFilterPage = 'app_carWatcher.eventFilter';
|
||
|
|
static const String keyReportDetailPage = 'app_carWatcher.reportDetail';
|
||
|
|
|
||
|
|
@override
|
||
|
|
List<RouteMeta> exportRoutes() {
|
||
|
|
final r0 = RouteMeta(keyModule, '/app_carWatcher', (args) => AppCarwatcherModule(), null);
|
||
|
|
|
||
|
|
final r1 = RouteMeta(keyCarWatcherHomePage, '/carWatcher_home',
|
||
|
|
(args) => CarWatcherHomePage(args.data['vin'] as String), r0);
|
||
|
|
|
||
|
|
final r2 = RouteMeta(keyGeoFenceListPage, '/geoFenceList',
|
||
|
|
(args) => GeoFenceListPage(args.data['vin'] as String), r0);
|
||
|
|
|
||
|
|
final r3 = RouteMeta(keyGeoFenceDetailPage, '/geoFenceDetail',
|
||
|
|
(args) => GeoFenceDetailPage(
|
||
|
|
args.data['vin'] as String,
|
||
|
|
args.data['fenceId'] as String?,
|
||
|
|
args.data['fenceOnCount'] as int,
|
||
|
|
), r0);
|
||
|
|
|
||
|
|
final r4 = RouteMeta(keyGeoFenceSearchPOIPage, '/poiSearch',
|
||
|
|
(args) => GeoFenceSearchPOIPage(
|
||
|
|
args.data['province'] as String?,
|
||
|
|
args.data['city'] as String?,
|
||
|
|
args.data['cityCode'] as String?,
|
||
|
|
args.data['fenceLatLng'] as LatLng?,
|
||
|
|
), r0);
|
||
|
|
|
||
|
|
final r5 = RouteMeta(keyDailyReportPage, '/dailyReport',
|
||
|
|
(args) => DailyReportDetailPage(
|
||
|
|
args.data['vin'] as String,
|
||
|
|
args.data['reportId'] as String,
|
||
|
|
), r0);
|
||
|
|
|
||
|
|
final r6 = RouteMeta(keyEventFilterPage, '/eventFilter',
|
||
|
|
(args) => EventFilterPage(args.data['eventTypes'] as List<EEventType>?), r0);
|
||
|
|
|
||
|
|
final r7 = RouteMeta(keyReportDetailPage, '/reportDetail',
|
||
|
|
(args) => ReportEventDetailPage(
|
||
|
|
args.data['vin'] as String,
|
||
|
|
args.data['eventId'] as String,
|
||
|
|
), r0);
|
||
|
|
|
||
|
|
return [r0, r1, r2, r3, r4, r5, r6, r7];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 核心页面功能
|
||
|
|
|
||
|
|
### 1. 车辆监控首页
|
||
|
|
|
||
|
|
**文件**: `pages/home/car_watcher_home_page.dart`
|
||
|
|
|
||
|
|
```dart
|
||
|
|
import 'package:app_consent/app_consent.dart';
|
||
|
|
import 'package:basic_intl/intl.dart';
|
||
|
|
import 'package:basic_logger/basic_logger.dart';
|
||
|
|
import 'package:basic_modular/modular.dart';
|
||
|
|
import 'package:flutter/material.dart';
|
||
|
|
import 'package:ui_basic/pull_to_refresh.dart';
|
||
|
|
import 'package:ui_basic/screen_util.dart';
|
||
|
|
import 'package:ui_basic/ui_basic.dart';
|
||
|
|
|
||
|
|
/// CarWatcher首页
|
||
|
|
class CarWatcherHomePage extends StatefulWidget with RouteObjProvider {
|
||
|
|
const CarWatcherHomePage(this.vin, {Key? key}) : super(key: key);
|
||
|
|
|
||
|
|
/// 车辆vin码
|
||
|
|
final String vin;
|
||
|
|
|
||
|
|
@override
|
||
|
|
State<StatefulWidget> createState() => _CarWatcherHomeState();
|
||
|
|
}
|
||
|
|
|
||
|
|
class _CarWatcherHomeState extends State<CarWatcherHomePage> {
|
||
|
|
final CarWatcherHomeBloc _bloc = CarWatcherHomeBloc();
|
||
|
|
late List<BannerBean> images = [];
|
||
|
|
|
||
|
|
@override
|
||
|
|
void initState() {
|
||
|
|
super.initState();
|
||
|
|
|
||
|
|
// 初始化banner数据
|
||
|
|
images.add(BannerBean(
|
||
|
|
imageUrl: 'packages/app_carwatcher/assets/images/icon_banner1.png',
|
||
|
|
title: '车辆监控横幅1',
|
||
|
|
));
|
||
|
|
|
||
|
|
images.add(BannerBean(
|
||
|
|
imageUrl: 'packages/app_carwatcher/assets/images/icon_banner2.png',
|
||
|
|
title: '车辆监控横幅2',
|
||
|
|
));
|
||
|
|
}
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
return Scaffold(
|
||
|
|
appBar: AppBar(title: Text('车辆监控')),
|
||
|
|
body: RefreshIndicator(
|
||
|
|
onRefresh: () async {
|
||
|
|
_bloc.add(RefreshDataEvent());
|
||
|
|
},
|
||
|
|
child: SingleChildScrollView(
|
||
|
|
child: Column(
|
||
|
|
children: [
|
||
|
|
// Banner轮播
|
||
|
|
HomeBannerWidget(images: images),
|
||
|
|
|
||
|
|
// 功能入口区域
|
||
|
|
_buildFunctionEntries(),
|
||
|
|
|
||
|
|
// 车辆状态信息
|
||
|
|
_buildVehicleStatus(),
|
||
|
|
|
||
|
|
// 最近事件列表
|
||
|
|
_buildRecentEvents(),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
Widget _buildFunctionEntries() {
|
||
|
|
return Container(
|
||
|
|
padding: EdgeInsets.all(16),
|
||
|
|
child: GridView.count(
|
||
|
|
shrinkWrap: true,
|
||
|
|
physics: NeverScrollableScrollPhysics(),
|
||
|
|
crossAxisCount: 2,
|
||
|
|
children: [
|
||
|
|
_buildFunctionCard(
|
||
|
|
icon: Icons.location_on,
|
||
|
|
title: '地理围栏',
|
||
|
|
onTap: () => _navigateToGeoFenceList(),
|
||
|
|
),
|
||
|
|
_buildFunctionCard(
|
||
|
|
icon: Icons.event_note,
|
||
|
|
title: '历史报告',
|
||
|
|
onTap: () => _navigateToDailyReport(),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
Widget _buildFunctionCard({
|
||
|
|
required IconData icon,
|
||
|
|
required String title,
|
||
|
|
required VoidCallback onTap,
|
||
|
|
}) {
|
||
|
|
return Card(
|
||
|
|
child: InkWell(
|
||
|
|
onTap: onTap,
|
||
|
|
child: Column(
|
||
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
||
|
|
children: [
|
||
|
|
Icon(icon, size: 48, color: Colors.blue),
|
||
|
|
SizedBox(height: 8),
|
||
|
|
Text(title, style: TextStyle(fontSize: 16)),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
void _navigateToGeoFenceList() {
|
||
|
|
Modular.to.pushNamed('/app_carWatcher/geoFenceList', arguments: {'vin': widget.vin});
|
||
|
|
}
|
||
|
|
|
||
|
|
void _navigateToDailyReport() {
|
||
|
|
Modular.to.pushNamed('/app_carWatcher/dailyReport', arguments: {'vin': widget.vin});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. 地理围栏功能
|
||
|
|
|
||
|
|
#### 围栏列表页面
|
||
|
|
```dart
|
||
|
|
/// 地理围栏列表页面
|
||
|
|
class GeoFenceListPage extends StatefulWidget {
|
||
|
|
const GeoFenceListPage(this.vin, {Key? key}) : super(key: key);
|
||
|
|
|
||
|
|
final String vin;
|
||
|
|
|
||
|
|
@override
|
||
|
|
State<GeoFenceListPage> createState() => _GeoFenceListPageState();
|
||
|
|
}
|
||
|
|
|
||
|
|
class _GeoFenceListPageState extends State<GeoFenceListPage> {
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
return Scaffold(
|
||
|
|
appBar: AppBar(
|
||
|
|
title: Text('地理围栏'),
|
||
|
|
actions: [
|
||
|
|
IconButton(
|
||
|
|
icon: Icon(Icons.add),
|
||
|
|
onPressed: () => _navigateToCreateFence(),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
body: ListView.builder(
|
||
|
|
itemCount: fenceList.length,
|
||
|
|
itemBuilder: (context, index) {
|
||
|
|
final fence = fenceList[index];
|
||
|
|
return ListTile(
|
||
|
|
title: Text(fence.name),
|
||
|
|
subtitle: Text(fence.address),
|
||
|
|
trailing: Switch(
|
||
|
|
value: fence.isEnabled,
|
||
|
|
onChanged: (value) => _toggleFence(fence, value),
|
||
|
|
),
|
||
|
|
onTap: () => _navigateToFenceDetail(fence),
|
||
|
|
);
|
||
|
|
},
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
void _navigateToCreateFence() {
|
||
|
|
Modular.to.pushNamed('/app_carWatcher/geoFenceDetail', arguments: {
|
||
|
|
'vin': widget.vin,
|
||
|
|
'fenceId': null,
|
||
|
|
'fenceOnCount': 0,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### 围栏详情页面
|
||
|
|
```dart
|
||
|
|
/// 地理围栏详情页面
|
||
|
|
class GeoFenceDetailPage extends StatefulWidget {
|
||
|
|
const GeoFenceDetailPage(this.vin, this.fenceId, this.fenceOnCount, {Key? key})
|
||
|
|
: super(key: key);
|
||
|
|
|
||
|
|
final String vin;
|
||
|
|
final String? fenceId;
|
||
|
|
final int fenceOnCount;
|
||
|
|
|
||
|
|
@override
|
||
|
|
State<GeoFenceDetailPage> createState() => _GeoFenceDetailPageState();
|
||
|
|
}
|
||
|
|
|
||
|
|
class _GeoFenceDetailPageState extends State<GeoFenceDetailPage> {
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
return Scaffold(
|
||
|
|
appBar: AppBar(
|
||
|
|
title: Text(widget.fenceId == null ? '新建围栏' : '编辑围栏'),
|
||
|
|
actions: [
|
||
|
|
TextButton(
|
||
|
|
onPressed: _saveFence,
|
||
|
|
child: Text('保存'),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
body: Column(
|
||
|
|
children: [
|
||
|
|
// 地图显示区域
|
||
|
|
Expanded(
|
||
|
|
flex: 2,
|
||
|
|
child: Container(
|
||
|
|
color: Colors.grey[300],
|
||
|
|
child: Center(child: Text('地图显示区域')),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
|
||
|
|
// 设置区域
|
||
|
|
Expanded(
|
||
|
|
flex: 1,
|
||
|
|
child: Padding(
|
||
|
|
padding: EdgeInsets.all(16),
|
||
|
|
child: Column(
|
||
|
|
children: [
|
||
|
|
TextField(
|
||
|
|
decoration: InputDecoration(
|
||
|
|
labelText: '围栏名称',
|
||
|
|
border: OutlineInputBorder(),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
SizedBox(height: 16),
|
||
|
|
Row(
|
||
|
|
children: [
|
||
|
|
Text('半径(米): '),
|
||
|
|
Expanded(
|
||
|
|
child: Slider(
|
||
|
|
min: 100,
|
||
|
|
max: 5000,
|
||
|
|
value: 500,
|
||
|
|
onChanged: (value) {},
|
||
|
|
),
|
||
|
|
),
|
||
|
|
Text('500'),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
],
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
void _saveFence() {
|
||
|
|
// 保存围栏逻辑
|
||
|
|
Navigator.of(context).pop();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. POI搜索功能
|
||
|
|
|
||
|
|
```dart
|
||
|
|
/// POI搜索页面
|
||
|
|
class GeoFenceSearchPOIPage extends StatefulWidget {
|
||
|
|
const GeoFenceSearchPOIPage(
|
||
|
|
this.province,
|
||
|
|
this.city,
|
||
|
|
this.cityCode,
|
||
|
|
this.fenceLatLng, {
|
||
|
|
Key? key,
|
||
|
|
}) : super(key: key);
|
||
|
|
|
||
|
|
final String? province;
|
||
|
|
final String? city;
|
||
|
|
final String? cityCode;
|
||
|
|
final LatLng? fenceLatLng;
|
||
|
|
|
||
|
|
@override
|
||
|
|
State<GeoFenceSearchPOIPage> createState() => _GeoFenceSearchPOIPageState();
|
||
|
|
}
|
||
|
|
|
||
|
|
class _GeoFenceSearchPOIPageState extends State<GeoFenceSearchPOIPage> {
|
||
|
|
final TextEditingController _searchController = TextEditingController();
|
||
|
|
List<POIItem> _searchResults = [];
|
||
|
|
|
||
|
|
@override
|
||
|
|
Widget build(BuildContext context) {
|
||
|
|
return Scaffold(
|
||
|
|
appBar: AppBar(
|
||
|
|
title: TextField(
|
||
|
|
controller: _searchController,
|
||
|
|
decoration: InputDecoration(
|
||
|
|
hintText: '搜索地点',
|
||
|
|
border: InputBorder.none,
|
||
|
|
suffixIcon: IconButton(
|
||
|
|
icon: Icon(Icons.search),
|
||
|
|
onPressed: _performSearch,
|
||
|
|
),
|
||
|
|
),
|
||
|
|
onSubmitted: (_) => _performSearch(),
|
||
|
|
),
|
||
|
|
),
|
||
|
|
body: ListView.builder(
|
||
|
|
itemCount: _searchResults.length,
|
||
|
|
itemBuilder: (context, index) {
|
||
|
|
final poi = _searchResults[index];
|
||
|
|
return ListTile(
|
||
|
|
title: Text(poi.name),
|
||
|
|
subtitle: Text(poi.address),
|
||
|
|
onTap: () => _selectPOI(poi),
|
||
|
|
);
|
||
|
|
},
|
||
|
|
),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
void _performSearch() {
|
||
|
|
// 执行POI搜索
|
||
|
|
setState(() {
|
||
|
|
_searchResults = [
|
||
|
|
// 模拟搜索结果
|
||
|
|
POIItem(name: '示例地点1', address: '示例地址1'),
|
||
|
|
POIItem(name: '示例地点2', address: '示例地址2'),
|
||
|
|
];
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
void _selectPOI(POIItem poi) {
|
||
|
|
Navigator.of(context).pop(poi);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class POIItem {
|
||
|
|
final String name;
|
||
|
|
final String address;
|
||
|
|
|
||
|
|
POIItem({required this.name, required this.address});
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 状态管理
|
||
|
|
|
||
|
|
### BLoC状态管理模式
|
||
|
|
```dart
|
||
|
|
// CarWatcher首页状态管理
|
||
|
|
class CarWatcherHomeBloc extends Bloc<CarWatcherHomeEvent, CarWatcherHomeState> {
|
||
|
|
CarWatcherHomeBloc() : super(CarWatcherHomeInitial()) {
|
||
|
|
on<LoadDataEvent>(_onLoadData);
|
||
|
|
on<RefreshDataEvent>(_onRefreshData);
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> _onLoadData(LoadDataEvent event, Emitter<CarWatcherHomeState> emit) async {
|
||
|
|
emit(CarWatcherHomeLoading());
|
||
|
|
|
||
|
|
try {
|
||
|
|
// 加载车辆数据
|
||
|
|
final vehicleData = await loadVehicleData(event.vin);
|
||
|
|
emit(CarWatcherHomeLoaded(vehicleData));
|
||
|
|
} catch (e) {
|
||
|
|
emit(CarWatcherHomeError(e.toString()));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<void> _onRefreshData(RefreshDataEvent event, Emitter<CarWatcherHomeState> emit) async {
|
||
|
|
// 刷新数据逻辑
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 事件定义
|
||
|
|
abstract class CarWatcherHomeEvent {}
|
||
|
|
|
||
|
|
class LoadDataEvent extends CarWatcherHomeEvent {
|
||
|
|
final String vin;
|
||
|
|
LoadDataEvent(this.vin);
|
||
|
|
}
|
||
|
|
|
||
|
|
class RefreshDataEvent extends CarWatcherHomeEvent {}
|
||
|
|
|
||
|
|
// 状态定义
|
||
|
|
abstract class CarWatcherHomeState {}
|
||
|
|
|
||
|
|
class CarWatcherHomeInitial extends CarWatcherHomeState {}
|
||
|
|
|
||
|
|
class CarWatcherHomeLoading extends CarWatcherHomeState {}
|
||
|
|
|
||
|
|
class CarWatcherHomeLoaded extends CarWatcherHomeState {
|
||
|
|
final VehicleData data;
|
||
|
|
CarWatcherHomeLoaded(this.data);
|
||
|
|
}
|
||
|
|
|
||
|
|
class CarWatcherHomeError extends CarWatcherHomeState {
|
||
|
|
final String message;
|
||
|
|
CarWatcherHomeError(this.message);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 依赖管理
|
||
|
|
|
||
|
|
### 核心依赖
|
||
|
|
- **clr_carwatcher**: 车辆监控服务SDK
|
||
|
|
- **ui_mapview**: 地图视图组件
|
||
|
|
- **app_consent**: 用户授权管理
|
||
|
|
|
||
|
|
### 框架依赖
|
||
|
|
- **basic_modular**: 模块化框架
|
||
|
|
- **basic_modular_route**: 路由管理
|
||
|
|
- **basic_intl**: 国际化支持
|
||
|
|
- **basic_logger**: 日志服务
|
||
|
|
|
||
|
|
### UI 组件依赖
|
||
|
|
- **ui_basic**: 基础UI组件
|
||
|
|
- **flutter/material**: Material Design组件
|
||
|
|
|
||
|
|
## 最佳实践
|
||
|
|
|
||
|
|
### 1. 代码组织
|
||
|
|
- 按功能模块组织页面和组件
|
||
|
|
- 统一的路由管理和导航
|
||
|
|
- 清晰的状态管理模式
|
||
|
|
|
||
|
|
### 2. 用户体验
|
||
|
|
- 响应式设计适配不同屏幕
|
||
|
|
- 友好的加载和错误提示
|
||
|
|
- 流畅的页面转场
|
||
|
|
|
||
|
|
### 3. 性能优化
|
||
|
|
- 合理的状态管理避免不必要的重建
|
||
|
|
- 地图资源的合理加载和释放
|
||
|
|
- 网络请求的优化和缓存
|