@@ -64,6 +64,8 @@ const String kSavedWorkStatus = 'saved_work_status'; // 作业状态( idle/work
const String kSavedIsStartWork = ' saved_is_start_work ' ; // 是否开始作业
const String kSavedIsStopWork = ' saved_is_stop_work ' ; // 是否点击过停止
const String kSavedTPMode = " saved_tp_mode " ;
const String kSavedCurrentRobotMode = " kSavedCurrentRobotMode " ;
const String kSavedIsPanelOpen = " kSavedIsPanelOpen " ;
// 保持 PlotData 类不变
class PlotData {
@@ -97,6 +99,8 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
bool isreceiveFirstCompletePoint = false ;
Offset _videoPopupPos = const Offset ( 0 , 100 ) ; // 弹窗初始位置
final GlobalKey _mapRepaintKey = GlobalKey ( ) ;
// 用于存储当前页面的 Toast OverlayEntry
List < OverlayEntry > _toastEntries = [ ] ;
final MapController _mapController = MapController ( ) ;
// 初始化轨迹管理器( 泛型指定为LatLng)
@@ -237,7 +241,23 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
await LocationUtils . clearCache ( LocationUtils . kSavedMarkedPoints ) ;
await LocationUtils . clearCache ( LocationUtils . kSavedObstacleHoles ) ;
await LocationUtils . clearCache ( LocationUtils . kSavedIsWorkAreaCompleted ) ;
debugPrint ( ' 本地存储的绘制数据已清空 ' ) ;
final prefs = await SharedPreferences . getInstance ( ) ;
await prefs . remove ( kSavedWorkStatus ) ;
await prefs . remove ( kSavedTPMode ) ;
await prefs . remove ( kSavedCurrentRobotMode ) ; // 机器人模式
await prefs . remove ( " kSavedTypedPathList " ) ; // 生成的路径
await prefs . remove ( kSavedIsStartWork ) ;
await prefs . remove ( kSavedIsStopWork ) ;
await prefs . remove ( kSavedGcjPathPoints ) ; // 路径点
await prefs . remove ( kSavedGcjOuterPoints ) ; // 外边框
await prefs . remove ( kSavedMarkedPoints ) ; // 打点
await prefs . remove ( kSavedObstacleHoles ) ; // 障碍物
await prefs . remove ( kSavedIsWorkAreaCompleted ) ;
await prefs . remove ( kSavedIsWorkPanelOpen ) ;
await prefs . remove ( kSavedWorkMode ) ;
await prefs . remove ( kSavedStartWorkList ) ;
await prefs . remove ( kSavedSelectedPlot ) ;
await prefs . remove ( kSavedIsPanelOpen ) ;
} catch ( e ) {
debugPrint ( ' 清空本地数据失败: $ e ' ) ;
}
@@ -256,12 +276,30 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
_traceManager . setMode ( TPMode . values . firstWhere ( ( e ) = > e . toString ( ) = = modeStr ) ) ;
}
final robotModeStr = prefs . getString ( kSavedCurrentRobotMode ) ;
if ( robotModeStr ! = null ) {
_currentRobotMode = RobotMode . values . firstWhere ( ( e ) = > e . toString ( ) = = robotModeStr , orElse: ( ) = > RobotMode . point ) ;
}
// 读取 生成完成的路径 typedPathList
final typedPathString = prefs . getString ( " kSavedTypedPathList " ) ;
if ( typedPathString ! = null ) {
final List < dynamic > typedPathJson = jsonDecode ( typedPathString ) ;
setState ( ( ) {
typedPathList = typedPathJson . map ( ( p ) {
return work_area_model . DeviceAddPathPointModel ( latitude: p [ ' lat ' ] , longitude: p [ ' lng ' ] ) ;
} ) . toList ( ) ;
} ) ;
}
// 2. 恢复是否开始作业
isStartWork = prefs . getBool ( kSavedIsStartWork ) ? ? false ;
// 3. 恢复是否点击过停止
isStopWork = prefs . getBool ( kSavedIsStopWork ) ? ? false ;
_isPanelOpen = prefs . getBool ( kSavedIsPanelOpen ) ? ? false ;
// 加载路径点
final pathJson = prefs . getString ( kSavedGcjPathPoints ) ;
if ( pathJson ! = null ) {
@@ -280,7 +318,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
final markedJson = prefs . getString ( kSavedMarkedPoints ) ;
if ( markedJson ! = null ) {
final List < dynamic > markedList = jsonDecode ( markedJson ) ;
_markedPoints = markedList . map ( ( item ) = > LatLng ( double . parse ( item [ ' lat ' ] . toString ( ) ) , double . parse ( item [ ' lng ' ] . toString ( ) ) ) ) . toList ( ) . toList ( ) ;
_markedPoints = markedList . map ( ( item ) = > LatLng ( double . parse ( item [ ' lat ' ] . toString ( ) ) , double . parse ( item [ ' lng ' ] . toString ( ) ) ) ) . toList ( ) ;
}
// 加载障碍物数据
@@ -298,11 +336,6 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// 1. 恢复作业面板状态
_isWorkPanelOpen = prefs . getBool ( kSavedIsWorkPanelOpen ) ? ? false ;
// if (isStartWork && !isStopWork) {
// _isWorkPanelOpen = true;
// } else {
// _isWorkPanelOpen = false;
// }
// 2. 恢复作业模式
workMode = prefs . getString ( kSavedWorkMode ) ? ? " 弓字模式 " ;
@@ -325,6 +358,19 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
) ;
}
// ==================== ✅ 核心修复 ====================
// 1. 从缓存恢复后,强制让 _markedPoints 同步给外边框(解决不显示)
if ( _markedPoints . isNotEmpty & & gcjOuterPoints . isEmpty ) {
gcjOuterPoints = List . from ( _markedPoints ) ;
}
// 2. 关键:同步机器人模式坐标(保存时边框不丢失)
if ( _currentRobotMode = = RobotMode . point ) {
_robotModeWgsPoints = _markedPoints . map ( ( p ) {
return gcj02ToWgs84 ( p . latitude , p . longitude ) ;
} ) . toList ( ) ;
}
if ( mounted ) {
setState ( ( ) { } ) ;
// 移动地图到绘制内容中心
@@ -363,9 +409,14 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
} ) . toList ( ) ;
prefs . setString ( kSavedObstacleHoles , jsonEncode ( obstacleList ) ) ;
final typedPath = typedPathList . map ( ( point ) = > { ' lat ' : point . latitude , ' lng ' : point . longitude } ) . toList ( ) ;
prefs . setString ( " kSavedTypedPathList " , jsonEncode ( typedPath ) ) ;
// 保存作业区域完成状态
prefs . setBool ( kSavedIsWorkAreaCompleted , _isWorkAreaCompleted ) ;
prefs . setBool ( kSavedIsPanelOpen , _isPanelOpen ) ;
// 1. 保存作业面板显示状态
prefs . setBool ( kSavedIsWorkPanelOpen , _isWorkPanelOpen ) ;
@@ -384,6 +435,8 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// 保存当前模式到本地缓存
prefs . setString ( kSavedTPMode , _traceManager . getMode ( ) . toString ( ) ) ;
prefs . setString ( kSavedCurrentRobotMode , _currentRobotMode . toString ( ) ) ;
// 4. 保存选中地块
if ( _selectedPlot ! = null ) {
final plotMap = {
@@ -469,12 +522,14 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
if ( isStartWork & & ! isStopWork ) {
if ( headingStatus = = 0 ) {
if ( ! _showHeadingWarn ) {
setState ( ( ) {
_showHeadingWarn = true ;
} ) ;
if ( mounted ) {
setState ( ( ) {
_showHeadingWarn = true ;
} ) ;
}
}
} else {
if ( _showHeadingWarn ) {
if ( _showHeadingWarn & & mounted ) {
setState ( ( ) {
_showHeadingWarn = false ;
} ) ;
@@ -486,13 +541,13 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// ==============================================
String mode = controlMode ? . toString ( ) ? ? " " ;
if ( mode ! = " 3 " ) {
if ( ! _showControlModeWarn ) {
if ( ! _showControlModeWarn & & mounted ) {
setState ( ( ) {
_showControlModeWarn = true ;
} ) ;
}
} else {
if ( _showControlModeWarn ) {
if ( _showControlModeWarn & & mounted ) {
setState ( ( ) {
_showControlModeWarn = false ;
} ) ;
@@ -500,7 +555,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
}
} else {
// 未作业 → 自动隐藏所有警告
if ( _showHeadingWarn | | _showControlModeWarn ) {
if ( ( _showHeadingWarn | | _showControlModeWarn ) & & mounted ) {
setState ( ( ) {
_showHeadingWarn = false ;
_showControlModeWarn = false ;
@@ -560,7 +615,9 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
} else {
// 截图失败时,允许空图片保存(可根据业务调整为强制失败)
_savePlotData ( plotName , null ) ;
ToastUtils . showWarn ( context , ' 地块保存成功,但地图截图生成失败! ' ) ;
_showPageToast ( message: " 地块保存成功,但地图截图生成失败! " , type: ToastType . warn ) ;
//ToastUtils.showWarn(context, '地块保存成功,但地图截图生成失败!');
//ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('地块保存成功,但地图截图生成失败!'), backgroundColor: Colors.amber));
}
}
@@ -621,7 +678,9 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// 1. 获取用户ID
final userId = context . read < AppUserCubit > ( ) . state . user ? . userId ? ? " " ;
if ( userId . isEmpty ) {
ToastUtils . showError ( context , ' 用户ID为空, 无法保存! ' ) ;
_showPageToast ( message: " 用户ID为空, 无法保存! " , type: ToastType . error ) ;
//ToastUtils.showError(context, '用户ID为空, 无法保存! ');
return ;
}
debugPrint ( " ===打印生成的路径开始 " ) ;
@@ -633,6 +692,15 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
final Map < String , dynamic > savePath = {
//'img': imgBase64 ?? '', // 截图的 Base64 字符串
' name ' : plotName , // 地块名称
' outer ' :
// 核心修改:根据机器人模式选择不同的坐标源
( _currentRobotMode = = RobotMode . robot ? _robotModeWgsPoints : gcjOuterPoints ) . map ( ( point ) {
final wgs84 = _currentRobotMode = = RobotMode . robot
? point // Robot模式直接使用原始WGS84坐标
: gcj02ToWgs84 ( point . latitude , point . longitude ) ; // Point模式转换
return { ' lng ' : wgs84 . longitude , ' lat ' : wgs84 . latitude } ;
} ) . toList ( ) ,
' path ' : _currentWorkMode = = WorkMode . custom
? [ ] // 自定义模式 path 为空数组
: typedPathList
@@ -645,18 +713,9 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
)
. toList ( ) ,
' outer ' :
// 核心修改:根据机器人模式选择不同的坐标源
( _currentRobotMode = = RobotMode . robot ? _robotModeWgsPoints : gcjOuterPoints ) . map ( ( point ) {
final wgs84 = _currentRobotMode = = RobotMode . robot
? point // Robot模式直接使用原始WGS84坐标
: gcj02ToWgs84 ( point . latitude , point . longitude ) ; // Point模式转换
return { ' lng ' : wgs84 . longitude , ' lat ' : wgs84 . latitude } ;
} ) . toList ( ) ,
' planModel ' : _currentWorkMode = = WorkMode . bow ? 0 : 2 , // 作业模式值
} ;
debugPrint ( " $ savePath savePath " ) ;
// 3. 构造 workRecord (包裹一层)
final Map < String , dynamic > workRecord = {
@@ -690,11 +749,11 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
) ,
) ;
//_debugPrintMultipartRequest(request);
// 7. 发送请求
try {
ToastUtils . showLoading ( context , ' 正在保存地块「$ plotName 」...' ) ;
_showPageToast ( message: " 正在保存地块「$ plotName 」..." , type: ToastType . loading ) ;
//ToastUtils.showLoading(context, '正在保存地块「$plotName」...');
//ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('正在保存地块...'), backgroundColor: Colors.blue));
final http . StreamedResponse response = await request . send ( ) ;
@@ -702,7 +761,9 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
debugPrint ( ' 保存地块响应体: $ responseBody ' ) ;
if ( response . statusCode = = 200 ) {
ToastUtils . showSuccess ( context , ' 地块「$ plotName 」保存成功!' ) ;
_showPageToast ( message: " 地块「$ plotName 」保存成功!" , type: ToastType . success ) ;
//ToastUtils.showSuccess(context, '地块「$plotName」保存成功! ');
_clearLocalData ( ) ;
//ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('地块「$plotName」保存成功! '), backgroundColor: Colors.green));
// 保存成功
@@ -711,6 +772,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
gcjPathPoints . clear ( ) ; // 清空路径
gcjOuterPoints . clear ( ) ; // 清空外边界
_robotModeWgsPoints . clear ( ) ;
_obstacleHoles . clear ( ) ;
_isPanelOpen = false ; // 关闭操作面板
_isWorkAreaCompleted = false ; // 重置作业区域完成状态
@@ -720,7 +782,9 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
}
} catch ( e ) {
debugPrint ( ' 保存失败: $ e ' ) ;
ToastUtils . showError ( context , ' 保存失败: ${ e . toString ( ) } ' ) ;
_showPageToast ( message: " 保存失败: ${ e . toString ( ) } " , type: ToastType . error ) ;
//ToastUtils.showError(context, '保存失败: ${e.toString()}');
//ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('保存失败: ${e.toString()}'), backgroundColor: Colors.red));
}
}
@@ -739,17 +803,21 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// 1. 作业区域模式
if ( _currentAreaMode = = AreaMode . work ) {
if ( _markedPoints . isNotEmpty ) {
_robotModeWgsPoints . removeLast ( ) ;
_markedPoints . removeLast ( ) ;
// 单独判断,单独删除
if ( _robotModeWgsPoints . isNotEmpty ) {
_robotModeWgsPoints . removeLast ( ) ;
}
if ( _markedPoints . isNotEmpty ) {
_markedPoints . removeLast ( ) ;
}
// 作业点撤回后,若路径已生成,重新生成路径(保持同步)
if ( _isWorkAreaCompleted & & _markedPoints . length > = 3 ) {
_generatePath ( showTips: false ) ;
}
ToastUtils . showInfo ( context , ' 已撤回作业区域最后一个点,剩余${ _markedPoints . length } 个点' ) ;
//ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('已撤回作业区域最后一个点,剩余${_markedPoints.length}个点')));
_showPageToast ( message: " 已撤回作业区域最后一个点,剩余${ _markedPoints . length } 个点" , type: ToastType . info ) ;
} else {
ToastUtils . showInfo ( context , ' 暂无作业区域点可撤回 ' ) ;
//ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('暂无作业区域点可撤回')));
_showPageToast ( message: " 暂无作业区域点可撤回 " , type: ToastType . info ) ;
}
return ;
}
@@ -757,15 +825,19 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// 2. 空洞模式(完成后优先撤回已完成的空洞)
if ( _currentAreaMode = = AreaMode . obstacle ) {
if ( _currentObstaclePoints . isNotEmpty ) {
_currentObstaclePoints . removeLast ( ) ;
_robotModeObsWgsPoints . removeLast ( ) ;
// 单独判断,单独删除
if ( _currentObstaclePoints . isNotEmpty ) {
_currentObstaclePoints . removeLast ( ) ;
}
if ( _robotModeObsWgsPoints . isNotEmpty ) {
_robotModeObsWgsPoints . removeLast ( ) ;
}
if ( _currentObstaclePoints . isEmpty ) {
_isObstacleEditing = false ;
}
ToastUtils . showInfo ( context , ' 已撤回当前空洞最后一个点,剩余${ _currentObstaclePoints . length } 个点' ) ;
//ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('已撤回当前空洞最后1个点, 剩余${_currentObstaclePoints.length}个点')));
return ; // 撤完点就结束,不执行后面的逻辑
_showPageToast ( message: " 已撤回当前空洞最后一个点,剩余${ _currentObstaclePoints . length } 个点" , type: ToastType . info ) ;
return ;
}
// 第二步:当前空洞无点可撤 → 再撤回「上一个完整空洞」
@@ -775,15 +847,12 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
if ( _isWorkAreaCompleted ) {
_generatePath ( showTips: false ) ;
}
ToastUtils . showInfo ( context , ' 已撤回上一个完整空洞,剩余${ _obstacleHoles . length } 个空洞' ) ;
//ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('已撤回上一个完整空洞,剩余${_obstacleHoles.length}个空洞')));
_showPageToast ( message: " 已撤回上一个完整空洞,剩余${ _obstacleHoles . length } 个空洞" , type: ToastType . info ) ;
return ;
}
// 第三步:无任何可撤内容
ToastUtils . showInfo ( context , ' 暂无空洞可撤回 ' ) ;
//ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('暂无空洞可撤回')));
_showPageToast ( message: " 暂无空洞可撤回 " , type: ToastType . info ) ;
}
} ) ;
_saveDataToLocal ( ) ;
@@ -795,7 +864,9 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// 1. 作业区域模式:清空作业点 + 重置路径
if ( _currentAreaMode = = AreaMode . work ) {
if ( _markedPoints . isEmpty ) {
ToastUtils . showInfo ( context , ' 暂无作业区域点可删除 ' ) ;
_showPageToast ( message: " 暂无作业区域点可删除 " , type: ToastType . info ) ;
//ToastUtils.showInfo(context, '暂无作业区域点可删除');
//ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('暂无作业区域点可删除')));
return ;
@@ -804,17 +875,21 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
gcjPathPoints . clear ( ) ;
gcjOuterPoints . clear ( ) ;
_robotModeWgsPoints . clear ( ) ;
_obstacleHoles . clear ( ) ;
_isWorkAreaCompleted = false ;
_saveBoxOpen = false ;
typedPathList . clear ( ) ;
ToastUtils . showSuccess ( context , ' 已清空所有作业区域点和路径 ' ) ;
_showPageToast ( message: " 已清空所有作业区域点和路径 " , type: ToastType . success ) ;
//ToastUtils.showSuccess(context, '已清空所有作业区域点和路径');
//ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('已清空所有作业区域点和路径')));
}
// 2. 空洞模式:清空所有空洞 + 恢复UI + 重新生成路径
else if ( _currentAreaMode = = AreaMode . obstacle ) {
if ( _obstacleHoles . isEmpty & & _currentObstaclePoints . isEmpty ) {
ToastUtils . showInfo ( context , ' 暂无障碍物可删除 ' ) ;
_showPageToast ( message: " 暂无障碍物可删除 " , type: ToastType . info ) ;
//ToastUtils.showInfo(context, '暂无障碍物可删除');
return ;
}
_obstacleHoles . clear ( ) ;
@@ -825,7 +900,8 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
if ( _isWorkAreaCompleted ) {
_generatePath ( showTips: false ) ;
}
ToastUtils . showSuccess ( context , ' 已清空所有障碍物 ' ) ;
_showPageToast ( message: " 已清空所有障碍物 " , type: ToastType . success ) ;
//ToastUtils.showSuccess(context, '已清空所有障碍物');
}
} ) ;
_saveDataToLocal ( ) ;
@@ -844,100 +920,82 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// ========== 新增:页面刷新初始化方法 ==========
Future < void > _handleRefresh ( ) async {
debugPrint ( " 点击刷新 " ) ;
// 防止重复刷新
_cancelAllToast ( ) ; // 👇 加这行
if ( _isRefreshing ) return ;
// 🔥 修复1: 保存当前有效的地图中心和缩放级别( 而非定位坐标)
final savedZoom = _mapController . camera ? . zoom ? ? 17.0 ;
setState ( ( ) {
_isRefreshing = true ; // 标记开始刷新
_isRefreshing = true ;
} ) ;
debugPrint ( " 点击刷新111 " ) ;
try {
if ( _currentLatLng ! = null & & _isValidLatLng ( _currentLatLng ! . latitude , _currentLatLng ! . longitude ) ) {
_mapController . move ( _currentLatLng ! , 18 ) ;
debugPrint ( " 点击刷新333 " ) ;
}
debugPrint ( " 点击刷新2222 " ) ;
// 2. 重置所有页面状态(恢复初始值)
setState ( ( ) {
_traceManager . setMode ( TPMode . LOCATION ) ;
isreceiveFirstCompletePoint = false ;
// 2. 清空打点和轨迹 数据
// 清空所有绘制 数据
_markedPoints . clear ( ) ;
gcjPathPoints . clear ( ) ;
_robotModeObsWgsPoints . clear ( ) ;
gcjOuterPoints . clear ( ) ;
_robotModeWgsPoints . clear ( ) ;
_obstacleHoles . clear ( ) ;
// 3. 重置作业区域状态
// 重置作业区域状态
_isWorkAreaCompleted = false ;
_saveBoxOpen = false ;
_showControlModeWarn = false ;
_showHeadingWarn = false ;
// 4. 重置面板状态
_isPanelOpen = false ;
_directionBoxOpen = false ;
_isListBoxOpe n = false ;
_isWorkPanelOpe n = false ;
// ==============================================
// 🔥 核心修复:强制关闭所有弹窗/面板/警告
// ==============================================
_showControlModeWar n = false ; // 关闭远程模式警告
_showHeadingWar n = false ; // 关闭航向警告
_isPanelOpen = false ; // 关闭底部操作面板
_directionBoxOpen = false ; // 关闭航线方向面板
_isListBoxOpen = false ; // 关闭地块列表面板
_isWorkPanelOpen = false ; // 关闭作业面板
_isVideoDialogOpen = false ; // 关闭视频弹窗
_isObstacleEditing = false ; // 退出障碍物编辑
_selectedPlot = null ; // 清空选中地块
// 5. 重置障碍物数据
_obstacleHoles . clear ( ) ;
_currentObstaclePoints . clear ( ) ;
_isObstacleEditing = false ;
// 6. 重置选中状态
_selectedPlot = null ;
// 7. 重置作业模式
// 重置模式
_currentRobotMode = RobotMode . robot ;
_currentAreaMode = AreaMode . work ;
_currentWorkMode = WorkMode . bow ;
// 8. 重置作业 参数
// 重置 参数
_workDistance = 0.0 ;
_angle = 0.0 ;
// 9. 重置地图与定位状态(关键修复)
// 重置定位
_currentLatLng = null ;
_currentWgsLatLng = null ;
_mapCenter = _getDefaultValidCenter ( ) ; // 仅用兜底坐标初始化地图中心
_mapCenter = _getDefaultValidCenter ( ) ;
// 10. 重置作业状态
// 重置作业状态
isDjMapShow = false ;
_workStatus = WorkStatus . idle ;
isStartWork = false ;
isStopWork = false ;
} ) ;
_clearLocalData ( ) ;
// 4. 重置轨迹管理器
_clearLocalData ( ) ;
_traceManager . reset ( ) ;
// 5. 重新加载作业记录(保持数据最新)
// final userId = context.read<AppUserCubit>().state.user?.userId ?? "";
// if (userId.isNotEmpty) {
// await context.read<DevicesCubit>().loadWorkRecords(userId);
// }
// 刷新成功提示
if ( mounted ) {
ToastUtils . showSuccess ( context , ' 页面已刷新完成 ' ) ;
//ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('页面已刷新完成'), backgroundColor: Colors.green, duration: Duration(seconds: 2)) );
_showPageToast ( message: " 页面已刷新完成 " , type: ToastType . success ) ;
//ToastUtils.showSuccess(context, '页面已刷新完成' );
}
} catch ( e ) {
// 错误处理
if ( mounted ) {
ToastUtils . showError ( context , ' 刷新失败:${ e . toString ( ) . substring ( 0 , 50 ) } ' ) ;
//ScaffoldMessenger.of(
// context,
//).showSnackBar(SnackBar(content: Text('刷新失败:${e.toString().substring(0, 50)}'), backgroundColor: Colors.red, duration: Duration(seconds: 3)));
_showPageToast ( message: " 刷新失败:${ e . toString ( ) . substring ( 0 , 50 ) } " , type: ToastType . error ) ;
//ToastUtils.showError(context, '刷新失败:${e.toString().substring(0, 50)}');
}
} finally {
// 标记刷新结束
if ( mounted ) {
setState ( ( ) {
_isRefreshing = false ;
@@ -955,7 +1013,8 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
// ========== 障碍物模式:完成当前障碍物绘制 ==========
void _completeObstacle ( ) {
if ( _currentObstaclePoints . length < 3 ) {
ToastUtils . showError ( context , ' 障碍物区域至少需要3个打点! ' ) ;
_showPageToast ( message: " 障碍物区域至少需要3个打点! " , type: ToastType . error ) ;
//ToastUtils.showError(context, '障碍物区域至少需要3个打点! ');
return ;
}
@@ -972,15 +1031,16 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
_isObstacleEditing = false ; // 退出编辑状态
} ) ;
_saveDataToLocal ( ) ;
ToastUtils. showSuccess( context, ' 已添加第 ${ _obstacleHoles. length} 个障碍物区域' ) ;
_showPageToast ( message: " 已添加第 ${ _obstacleHoles . length } 个障碍物区域 " , type: ToastType . success ) ;
// ToastUtils. showSuccess( context, '已添加第${ _obstacleHoles. length} 个障碍物区域');
}
// ========== 核心方法:打点逻辑 ==========
void _addMarkedPoint ( ) {
_traceManager . reset ( ) ;
if ( _currentAreaMode = = AreaMode . work & & _isWorkAreaCompleted ) {
ToastUtils . showError ( context , ' 作业区域已完成,无法继续添加打点! ' ) ;
_showPageToast ( message: " 作业区域已完成,无法继续添加打点! " , type: ToastType . error ) ;
//ToastUtils.showError(context, '作业区域已完成,无法继续添加打点!');
return ;
}
setState ( ( ) {
@@ -989,6 +1049,10 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
_markedPoints . add ( _mapCenter ) ;
debugPrint ( ' 新增作业区域打点:第 ${ _markedPoints . length } 个点,经纬度: ${ _mapCenter . latitude } , ${ _mapCenter . longitude } ' ) ;
} else {
if ( _markedPoints . isEmpty ) {
_showPageToast ( message: " 请先完成作业区域打点! " , type: ToastType . info ) ;
return ;
}
// 障碍物模式:添加到当前障碍物打点
_currentObstaclePoints . add ( _mapCenter ) ;
_isObstacleEditing = true ; // 标记进入障碍物编辑状态
@@ -996,7 +1060,8 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
}
} else {
if ( _currentLatLng = = null | | ! _isValidLatLng ( _currentLatLng ! . latitude , _currentLatLng ! . longitude ) ) {
ToastUtils . showError ( context , ' 设备坐标无效,无法添加打点! ' ) ;
_showPageToast ( message: " 设备坐标无效,无法添加打点! " , type: ToastType . error ) ;
//ToastUtils.showError(context, '设备坐标无效,无法添加打点!');
return ;
}
@@ -1006,6 +1071,10 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
_robotModeWgsPoints . add ( _currentWgsLatLng ! ) ;
debugPrint ( ' Robot模式新增作业打点: 第 ${ _markedPoints . length } 个点,经纬度: ${ _currentLatLng ! . latitude } , ${ _currentLatLng ! . longitude } ' ) ;
} else {
if ( _markedPoints . isEmpty | | _robotModeWgsPoints . isEmpty ) {
_showPageToast ( message: " 请先完成作业区域打点! " , type: ToastType . info ) ;
return ;
}
// 3. Robot模式-障碍物打点
_currentObstaclePoints . add ( _currentLatLng ! ) ;
_robotModeObsWgsPoints . add ( _currentWgsLatLng ! ) ;
@@ -1030,6 +1099,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
_mapController . dispose ( ) ;
_pathPlanningSub ? . cancel ( ) ; // 🔥 取消订阅
_saveDataToLocal ( ) ; // 🔥 页面销毁时保存最终状态
_cancelAllToast ( ) ; // 退出页面 → 关闭所有本页面的Toast
super . dispose ( ) ;
}
@@ -1140,6 +1210,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
List < dynamic > outerList = [ ] ;
final rawOuter = parsedJson [ ' outer ' ] ;
debugPrint ( " $ rawOuter rawOuterrawOuterrawOuter " ) ;
if ( rawOuter is String ) {
try {
outerList = jsonDecode ( rawOuter ) as List ;
@@ -1275,8 +1346,6 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
tracePoint = _traceManager . getTracePoint ( ) ;
gctracePoint = batchWgs84ToGcj02 ( tracePoint ! ) ;
debugPrint ( " $ gctracePoint gctracePoint " ) ;
//_currentLatLng = gcjPoint; // ✅ 状态变量在setState内更新
if ( _isValidLatLng ( gcjPoint . latitude , gcjPoint . longitude ) ) {
_currentLatLng = gcjPoint ;
@@ -1504,6 +1573,28 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
) ,
] ,
) ,
if ( gctracePoint ! . isNotEmpty )
PolylineLayer (
polylines: [ Polyline ( points: gctracePoint ! , color: Color . fromARGB ( 255 , 6 , 187 , 232 ) , strokeWidth: 3 , isDotted: false ) ] ,
) ,
/// 当前定位 Marker
if ( _currentLatLng ! = null & & __isValidLatLng ( _currentLatLng ) )
MarkerLayer (
markers: [
Marker (
point: _currentLatLng ! ,
width: 40 ,
height: 40 ,
child: CustomPaint (
size: const Size ( 40 , 40 ) ,
painter: HeadingMarkerPainter ( headingAngle: _headingAngle ) ,
) ,
) ,
] ,
) ,
if ( ! _isWorkAreaCompleted )
/// 历史打点的绿色标记
MarkerLayer (
@@ -1524,7 +1615,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
width: 16 ,
height: 16 ,
decoration: const BoxDecoration (
color: Color ( 0xFF00C853 ) , // 绿色主题色
color: Color . fromARGB ( 255 , 10 , 218 , 55 ) , // 绿色主题色
shape: BoxShape . circle ,
boxShadow: [ BoxShadow ( color: Colors . black12 , blurRadius: 2 ) ] ,
) ,
@@ -1540,26 +1631,6 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
) ;
} ) . toList ( ) ,
) ,
if ( gctracePoint ! . isNotEmpty )
PolylineLayer (
polylines: [ Polyline ( points: gctracePoint ! , color: Color ( 0xFF00C853 ) , strokeWidth: 3 , isDotted: false ) ] ,
) ,
/// 当前定位 Marker
if ( _currentLatLng ! = null & & __isValidLatLng ( _currentLatLng ) )
MarkerLayer (
markers: [
Marker (
point: _currentLatLng ! ,
width: 40 ,
height: 40 ,
child: CustomPaint (
size: const Size ( 40 , 40 ) ,
painter: HeadingMarkerPainter ( headingAngle: _headingAngle ) ,
) ,
) ,
] ,
) ,
] ,
) ;
}
@@ -1569,7 +1640,8 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
_traceManager . reset ( ) ;
if ( startWorkList . isEmpty ) {
ToastUtils . showInfo ( context , ' 作业列表为空,请重新选择路径 ' ) ;
_showPageToast ( message: " 作业列表为空,请重新选择路径 " , type: ToastType . info ) ;
//ToastUtils.showInfo(context, '作业列表为空,请重新选择路径');
return ;
}
_traceManager . setMode ( TPMode . NAVIGATION ) ;
@@ -1601,7 +1673,8 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
///context.read<DevicesCubit>().updateAppState(AppState.routePlanning);
// 可选:显示作业提示
ToastUtils . showSuccess ( context , ' 作业已开始 ' ) ;
_showPageToast ( message: " 作业已开始 " , type: ToastType . success ) ;
//ToastUtils.showSuccess(context, '作业已开始');
_saveDataToLocal ( ) ;
//ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('作业已开始'), backgroundColor: Colors.green));
}
@@ -1618,21 +1691,23 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
}
void _stopWork ( ) async {
setState ( ( ) {
isStartWork = false ; // 🔥 关键:停止作业标志
_workStatus = WorkStatus . idle ;
_showHeadingWarn = fals e ;
_showControlMode Warn = false ;
isStopWork = tru e; //已经点击过停止
isreceiveFirstCompletePoint = fals e;
} ) ;
if ( mounted ) {
setState ( ( ) {
isStartWork = false ; // 🔥 关键:停止作业标志
_workStatus = WorkStatus . idl e ;
_showHeading Warn = false ;
_showControlModeWarn = fals e;
isStopWork = tru e; //已经点击过停止
isreceiveFirstCompletePoint = false ;
} ) ;
}
//context.read<DevicesCubit>().updateAppState(AppState.none);
await context . read < DevicesCubit > ( ) . stopRoutePlanning ( ) ;
_traceManager . reset ( ) ;
_saveDataToLocal ( ) ; // 🔥 保存暂停状态
ToastUtils . showError ( context , ' 作业已停止 ' ) ;
_showPageToast ( message: " 作业已停止 " , type: ToastType . error ) ;
_traceManager . setMode ( TPMode . LOCATION ) ;
}
/// 继续作业
@@ -1975,68 +2050,57 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
if ( _currentWorkMode = = WorkMode . custom ) {
setState ( ( ) {
_saveBoxOpen = true ; // 显示保存按钮
_isWorkAreaCompleted = true ; // 标记为完成,允许保存
gcjPathPoints = List . from ( _markedPoints ) ; // outer直接使用打点坐标
_saveBoxOpen = true ;
_isWorkAreaCompleted = true ;
gcjPathPoints = List . from ( _markedPoints ) ;
gcjOuterPoints = List . from ( _markedPoints ) ;
typedPathList = [ ] ; // 路径列表置空
typedPathList = [ ] ;
} ) ;
return ;
}
// 1. 校验必要条件
if ( _currentAreaMode = = AreaMode . work & & _markedPoints . isEmpty ) {
if ( showTips ) {
ToastUtils . showError ( context , ' 请先添加作业区域打点! ' ) ;
_showPageToast ( message: ' 请先添加作业区域打点! ' , type: ToastType . error ) ;
}
return ;
}
if ( _currentAreaMode = = AreaMode . work & & _markedPoints . length < 3 ) {
if ( showTips ) {
ToastUtils . showError ( context , ' 作业区域至少需要3个打点才能生成路径! ' ) ;
_showPageToast ( message: ' 作业区域至少需要3个打点才能生成路径! ' , type: ToastType . error ) ;
}
return ;
}
if ( _currentWorkMode = = null ) {
if ( showTips ) {
ToastUtils . showError ( context , ' 请先选择作业模式! ' ) ;
_showPageToast ( message: ' 请先选择作业模式! ' , type: ToastType . error ) ;
}
return ;
}
try {
// 2. 转换页面数据为 generatePath 所需的参数格式
// 2.1 参考点( 区分Robot/Point模式)
LatLng firstPointWgs ; // 最终传给接口的WGS84格式参考点
LatLng firstPointWgs ;
if ( _currentRobotMode = = RobotMode . robot & & _robotModeWgsPoints . isNotEmpty ) {
// Robot模式: 直接使用原始WGS84坐标( 无需转换)
firstPointWgs = _robotModeWgsPoints . first ;
} else if ( _currentRobotMode = = RobotMode . point & & _markedPoints . isNotEmpty ) {
// Point模式: 先取十字准星的GCJ02坐标 → 转换为WGS84
LatLng firstPointGcj = _markedPoints . first ;
firstPointWgs = gcj02ToWgs84 ( firstPointGcj . latitude , firstPointGcj . longitude ) ;
} else {
if ( showTips ) {
ToastUtils . showError ( context , ' 参考点坐标为空,请先添加作业区域打点! ' ) ;
_showPageToast ( message: ' 参考点坐标为空,请先添加作业区域打点! ' , type: ToastType . error ) ;
}
return ; // 终止方法执行,避免后续错误
return ;
}
// 参考点使用转换后的WGS84坐标
final referencePoint = work_area_model . ReferencePoint ( lat: firstPointWgs . latitude , lon: firstPointWgs . longitude ) ;
// 2.2 航向角
final heading = _angle = = - 1 ? - 1 : _angle . toInt ( ) ;
// 2.3 外边界(作业区域)- 核心区分Robot/Point模式
List < work_area_model . Position > outerPositions = [ ] ;
if ( _currentRobotMode = = RobotMode . robot & & _robotModeWgsPoints . isNotEmpty ) {
// Robot模式: 直接使用原始WGS84坐标, 跳过GCJ02→WGS84转换
outerPositions = _robotModeWgsPoints . map ( ( latLng ) {
return work_area_model . Position ( lat: latLng . latitude , lon: latLng . longitude ) ;
} ) . toList ( ) ;
} else if ( _currentRobotMode = = RobotMode . point & & _markedPoints . isNotEmpty ) {
// Point模式: 十字准星的GCJ02坐标 → 转换为WGS84
outerPositions = _markedPoints . map ( ( latLng ) {
final wgs84Point = gcj02ToWgs84 ( latLng . latitude , latLng . longitude ) ;
return work_area_model . Position ( lat: wgs84Point . latitude , lon: wgs84Point . longitude ) ;
@@ -2050,17 +2114,14 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
} ) . toList ( ) ;
final outerBoundary = work_area_model . OuterBoundary ( position: outerPositions , sideWidth: _workDistance ) ;
work_area_model . HoleBoundary ? holeBoundary ;
if ( _obstacleHoles . isNotEmpty ) {
// 1. 构造二维 Position 数组
List < List < work_area_model . Position > > positionList = [ ] ;
if ( _currentRobotMode = = RobotMode . robot & & _robotModeObsWgsPoints . isNotEmpty ) {
for ( var holePoints in _obstacleWgsHoles ) {
List < work_area_model . Position > innerPositionList = [ ] ;
innerPositionList = holePoints . map ( ( latLng ) {
return work_area_model . Position ( lat: latLng . latitude , lon: latLng . longitude ) ;
} ) . toList ( ) ;
@@ -2069,8 +2130,6 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
} else if ( _currentRobotMode = = RobotMode . point & & _obstacleHoles . isNotEmpty ) {
for ( var holePoints in _obstacleHoles ) {
List < work_area_model . Position > innerPositionList = [ ] ;
// Point模式: GCJ02→WGS84转换
innerPositionList = holePoints . map ( ( latLng ) {
final wgs84Point = gcj02ToWgs84 ( latLng . latitude , latLng . longitude ) ;
return work_area_model . Position ( lat: wgs84Point . latitude , lon: wgs84Point . longitude ) ;
@@ -2079,70 +2138,39 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
}
}
//for (var holePoints in _obstacleHoles) {
// List<work_area_model.Position> innerPositionList = [];
// if (_currentRobotMode == RobotMode.robot && _robotModeObsWgsPoints.isNotEmpty) {
// // Robot模式: 使用障碍物原始WGS84坐标
// innerPositionList = holePoints.map((latLng) {
// return work_area_model.Position(lat: latLng.latitude, lon: latLng.longitude);
// }).toList();
// } else {
// // Point模式: GCJ02→WGS84转换
// innerPositionList = holePoints.map((latLng) {
// final wgs84Point = gcj02ToWgs84(latLng.latitude, latLng.longitude);
// return work_area_model.Position(lat: wgs84Point.latitude, lon: wgs84Point.longitude);
// }).toList();
// }
// positionList.add(innerPositionList);
//}
// 2. 创建 HoleBoundary 对象(无外层 Map)
holeBoundary = work_area_model . HoleBoundary ( position: positionList , sideWidth: 0.0 ) ;
}
// 🔥 第二步:构造符合方法参数要求的 Map<String, HoleBoundary>
// 用一个临时 Map 来满足类型要求,后续在 Cubit 中取值即可
Map < String , work_area_model . HoleBoundary > holes = { } ;
if ( holeBoundary ! = null ) {
holes = { ' tempKey ' : holeBoundary } ; // 键名任意,仅为满足类型要求
holes = { ' tempKey ' : holeBoundary } ;
}
final workType = _currentWorkMode = = WorkMode . bow ? 0 : 2 ;
print ( ' ================================================== ' ) ;
print ( ' ✅ generatePath 完整参数结构: ' ) ;
print ( ' reference: ${ formatPrettyJson ( referencePoint ) } ' ) ;
print ( ' heading: $ heading ' ) ;
print ( ' outer: ${ formatPrettyJson ( outerBoundary ) } ' ) ;
print ( ' holes: ${ formatPrettyJson ( holes ) } ' ) ;
print ( ' workType: $ workType ' ) ;
print ( ' ================================================== ' ) ;
// 3. 调用 Cubit 的 generatePath 方法( 传入holes参数)
await context . read < DevicesCubit > ( ) . generatePath (
reference: referencePoint ,
heading: heading ,
outer: outerBoundary ,
holes: holes . isNotEmpty ? holes : { } , // 无障碍物时传空对象
holes: holes . isNotEmpty ? holes : { } ,
workType: workType ,
) ;
// 4. 处理生成结果(原有逻辑不变)
final cubitState = context . read < DevicesCubit > ( ) . state ;
if ( cubitState . errorMessage ! = null ) {
if ( showTips ) {
ToastUtils . showError ( context , ' 路径生成失败: ${ cubitState . errorMessage } ' ) ;
//ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('路径生成失败:${cubitState.errorMessage}'), backgroundColor: Colors.red));
_showPageToast ( message: ' 路径生成失败: ${ cubitState . errorMessage } ' , type: ToastType . error );
}
} else if ( cubitState . generatedPath ! = null ) {
if ( showTips ) {
ToastUtils . showSuccess ( context , ' 路径生成成功! ' ) ;
_showPageToast ( message: ' 路径生成成功! ' , type: ToastType . success ) ;
}
if ( cubitState . generatedPath is List ) {
setState ( ( ) {
_isWorkAreaCompleted = true ;
_saveBoxOpen = true ;
_currentObstaclePoints . clear ( ) ; // 清空当前绘制的障碍物点
_robotModeObsWgsPoints . clear ( ) ; // 清空Robot模式下的障碍物WGS84坐标
_currentObstaclePoints . clear ( ) ;
_robotModeObsWgsPoints . clear ( ) ;
final pathList = cubitState . generatedPath as List ;
typedPathList = pathList . whereType < work_area_model . DeviceAddPathPointModel > ( ) . toList ( ) ;
gcjPathPoints = typedPathList . map ( ( point ) {
@@ -2151,8 +2179,6 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
final gcjPoint = wgs84ToGcj02 ( wgs84Lat , wgs84Lon ) ;
return LatLng ( gcjPoint . latitude , gcjPoint . longitude ) ;
} ) . toList ( ) ;
// 外边界保持原有逻辑
//gcjOuterPoints = List.from(_markedPoints);
} ) ;
_saveDataToLocal ( ) ;
moveMapToPointsCenter ( gcjPathPoints ) ;
@@ -2161,11 +2187,67 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
} catch ( e ) {
debugPrint ( ' 路径生成异常: $ e ' ) ;
if ( showTips ) {
ToastUtils . showError ( context , ' 路径生成异常: ${ e . toString ( ) . substring ( 0 , 50 ) } ' ) ;
_showPageToast ( message: ' 路径生成异常: ${ e . toString ( ) . substring ( 0 , 50 ) } ' , type: ToastType . error );
}
}
}
void _cancelAllToast ( ) {
for ( final entry in _toastEntries ) {
if ( entry . mounted ) {
entry . remove ( ) ;
}
}
_toastEntries . clear ( ) ;
}
// 只在当前页面显示安全 Toast, 退出自动消失
void _showPageToast ( { required String message , required ToastType type } ) {
_cancelAllToast ( ) ;
// 🔥 延迟执行 Overlay 操作,避开构建阶段
WidgetsBinding . instance . addPostFrameCallback ( ( _ ) {
if ( ! mounted ) return ;
OverlayEntry entry = OverlayEntry (
builder: ( context ) = > SafeArea (
child: Align (
alignment: Alignment . topCenter ,
child: Container (
margin: const EdgeInsets . symmetric ( horizontal: 20 , vertical: 30 ) ,
padding: const EdgeInsets . symmetric ( horizontal: 16 , vertical: 10 ) ,
decoration: BoxDecoration ( color: _getToastColor ( type ) , borderRadius: BorderRadius . circular ( 8 ) ) ,
child: Text ( message , style: const TextStyle ( color: Colors . white , fontSize: 14 ) ) ,
) ,
) ,
) ,
) ;
_toastEntries . add ( entry ) ;
Overlay . of ( context ) ? . insert ( entry ) ;
// 2秒后自动关闭
Future . delayed ( const Duration ( seconds: 2 ) , ( ) {
if ( entry . mounted ) entry . remove ( ) ;
_toastEntries . remove ( entry ) ;
} ) ;
} ) ;
}
Color _getToastColor ( ToastType type ) {
switch ( type ) {
case ToastType . success:
return Colors . green ;
case ToastType . error:
return Colors . red ;
case ToastType . warn:
return Colors . orange ;
case ToastType . info:
return Colors . blue ;
case ToastType . loading:
return Colors . black87 ;
}
}
@ override
Widget build ( BuildContext context ) {
final screenHeight = MediaQuery . of ( context ) . size . height ;
@@ -2207,16 +2289,25 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
DeviceStatusUpdated ? updatedState ;
//有停止信号
if ( isFinishWork ) {
_workStatus = WorkStatus . idle ;
ToastUtils . showError ( context , ' 作业已停止 ' ) ;
// 👇 延迟 1 秒钟再执行 reset( 不会阻塞UI, 安全)
Future . delayed ( const Duration ( seconds: 1 ) , ( ) {
// 🔥 关键:用微任务延迟执行状态更新,避开构建阶段
WidgetsBinding . instance . addPostFrameCallback ( ( _ ) {
if ( mounted ) {
_traceManager . reset ( ) ;
setState ( ( ) {
_workStatus = WorkStatus . idle ;
isStopWork = true ;
isreceiveFirstCompletePoint = false ;
} ) ;
_showPageToast ( message: " 作业已停止 " , type: ToastType . error ) ;
// 延迟重置轨迹管理器
Future . delayed ( const Duration ( seconds: 1 ) , ( ) {
if ( mounted ) {
_traceManager . reset ( ) ;
_traceManager . setMode ( TPMode . LOCATION ) ;
}
} ) ;
}
} ) ;
isStopWork = true ;
isreceiveFirstCompletePoint = false ;
}
if ( state is DeviceStatusUpdated ) {