This commit is contained in:
mmc
2026-02-25 20:00:33 +08:00
parent 27717cbba2
commit 07a653fca5
4 changed files with 567 additions and 223 deletions

View File

@@ -0,0 +1,169 @@
import 'package:flutter/material.dart';
class RouteDirectionPanel extends StatefulWidget { // 重命名为Panel更符合组件语义
// 接收外部传入的初始值
final bool initialOptimalHeading;
final double initialDirection;
// 新增:回调函数 - 关闭面板/返回设置结果
final ValueChanged<Map<String, dynamic>>? onConfirm;
final VoidCallback? onCancel;
const RouteDirectionPanel({
super.key,
this.initialOptimalHeading = true, // 默认开启最优航向
this.initialDirection = 0.0, // 默认方向0度
this.onConfirm,
this.onCancel,
});
@override
State<RouteDirectionPanel> createState() => _RouteDirectionPanelState();
}
class _RouteDirectionPanelState extends State<RouteDirectionPanel> {
late bool _optimalHeading;
late double _direction; // 0-360度范围
@override
void initState() {
super.initState();
_optimalHeading = widget.initialOptimalHeading;
_direction = widget.initialDirection.clamp(0, 360); // 限制在0-360度
}
@override
Widget build(BuildContext context) {
// 纯面板结构:无Scaffold,仅Container包裹核心UI
return Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 8)],
),
padding: const EdgeInsets.all(16),
constraints: const BoxConstraints(minHeight: 300), // 最小高度保证UI完整
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 顶部标题栏 + 返回按钮
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// 取消/返回按钮
GestureDetector(
onTap: () {
widget.onCancel?.call(); // 触发取消回调
},
child: const Icon(
Icons.arrow_back_ios,
color: Colors.black87,
size: 20,
),
),
const Text(
'航线方向',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
const SizedBox(width: 24), // 占位保持标题居中
],
),
const SizedBox(height: 20),
const Divider(height: 1, color: Colors.grey),
const SizedBox(height: 20),
// 最优航向开关
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'最优航向',
style: TextStyle(fontSize: 16, color: Colors.black87),
),
Switch(
value: _optimalHeading,
activeColor: const Color(0xFF00C853),
onChanged: (bool value) {
setState(() {
_optimalHeading = value;
});
},
),
],
),
const SizedBox(height: 30),
// 航线方向滑块
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'航线方向',
style: TextStyle(fontSize: 16, color: Colors.black87),
),
Text(
'${_direction.toStringAsFixed(0)}°',
style: TextStyle(
fontSize: 16,
color: _optimalHeading ? Colors.grey : Colors.black87,
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 15),
Slider(
value: _direction,
min: 0,
max: 360,
divisions: 36,
label: '${_direction.toStringAsFixed(0)}°',
activeColor: const Color(0xFF00C853),
inactiveColor: Colors.grey[300],
onChanged: _optimalHeading
? null
: (double value) {
setState(() {
_direction = value;
});
},
),
],
),
const SizedBox(height: 40),
// 确认按钮
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
// 触发确认回调,传递设置结果
widget.onConfirm?.call({
'optimalHeading': _optimalHeading,
'direction': _direction,
});
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF00C853),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text('确认'),
),
),
],
),
),
);
}
}

View File

@@ -4,12 +4,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:geolocator/geolocator.dart';
import 'package:latlong2/latlong.dart';
import 'package:maibu_satabot_v2/features/home/presentation/widgets/BottomDirectionLine.dart';
import 'package:maibu_satabot_v2/features/home/presentation/widgets/common/enum.dart';
import 'package:maibu_satabot_v2/features/home/presentation/widgets/startpoint_area.dart';
import '../path_list_pages.dart';
class MapPageEnterprise extends StatefulWidget {
const MapPageEnterprise({Key? key}) : super(key: key);
@@ -24,8 +24,32 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
StreamSubscription<Position>? _positionSub;
bool _hasMovedOnce = false;
bool _isPanelOpen = false; // 控制面板显示/隐藏
RobotMode _currentRobotMode = RobotMode.point;
AreaMode _currentAreaMode = AreaMode.work;
bool _directionBoxOpen = false; //航线面板显示/隐藏
RobotMode _currentRobotMode = RobotMode.point; //打点模式
AreaMode _currentAreaMode = AreaMode.work; //作业区域还是障碍物区域
WorkMode? _currentWorkMode = WorkMode.bow; // 当前作业模式
// ========== 新增:打点相关状态 ==========
List<LatLng> _markedPoints = []; // 存储所有打点的坐标
int _pointIndex = 1; // 打点序号(用于标记显示)
void _addMapMarker() {
// 1. 获取当前地图中心的坐标
LatLng centerLatLng = _mapController.center;
// 2. 记录打点坐标(带序号)
setState(() {
_markedPoints.add(centerLatLng);
});
// 3. 打印打点信息(调试用)
debugPrint(
'第$_pointIndex个点:纬度=${centerLatLng.latitude.toStringAsFixed(6)},经度=${centerLatLng.longitude.toStringAsFixed(6)}',
);
// 4. 序号自增
_pointIndex++;
}
@override
void initState() {
@@ -58,15 +82,16 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
_updateLocation(gcj, moveMap: true);
// 2️⃣ 实时监听(不再强制移动地图)
_positionSub = Geolocator.getPositionStream(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.bestForNavigation,
distanceFilter: 2,
),
).listen((pos) {
final gcj = wgs84ToGcj02(pos.latitude, pos.longitude);
_updateLocation(gcj, moveMap: false);
});
_positionSub =
Geolocator.getPositionStream(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.bestForNavigation,
distanceFilter: 2,
),
).listen((pos) {
final gcj = wgs84ToGcj02(pos.latitude, pos.longitude);
_updateLocation(gcj, moveMap: false);
});
}
/// ===============================
@@ -101,102 +126,137 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
final menuHeight = 16 * 6; // 假设 VerticalFloatMenu 有 6 个选项,每个高度为 56
final maxTop = screenHeight - menuHeight;
return Scaffold(
body:
SafeArea(
body: SafeArea(
child: Stack(
children: [
FlutterMap(
mapController: _mapController,
options: MapOptions(
initialCenter:
_currentLatLng ?? const LatLng(39.9042, 116.4074),
initialZoom: 15,
maxZoom: 18,
),
children: [
/// 高德瓦片(GCJ-02)
TileLayer(
urlTemplate:
'https://webrd02.is.autonavi.com/appmaptile'
'?style=8&x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1'
'&key=bbb1f0f20eed6bf679eddf2625630aba',
children: [
FlutterMap(
mapController: _mapController,
options: MapOptions(
initialCenter:
_currentLatLng ?? const LatLng(39.9042, 116.4074),
initialZoom: 15,
maxZoom: 18,
),
/// 当前定位 Marker
if (_currentLatLng != null)
MarkerLayer(
markers: [
Marker(
point: _currentLatLng!,
width: 40,
height: 40,
child: const Icon(
Icons.my_location,
color: Colors.blue,
size: 30,
),
),
],
children: [
/// 高德瓦片(GCJ-02)
TileLayer(
urlTemplate:
'https://webrd02.is.autonavi.com/appmaptile'
'?style=8&x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1'
'&key=bbb1f0f20eed6bf679eddf2625630aba',
),
],
),
Positioned(
height: 336,
right: 16,
top: maxTop > 10 ? 11 : maxTop, // 确保不超出屏幕范围
child: FloatingActionButton(
onPressed: _moveToCurrentLocation,
child: new VerticalFloatMenu(
onEditTap: (bool isOpen) {
// 关键:接收子组件的回调,修改外部的_isPanelOpen
setState(() {
_isPanelOpen = isOpen;
});
},),
/// 当前定位 Marker
if (_currentLatLng != null)
MarkerLayer(
markers: [
Marker(
point: _currentLatLng!,
width: 40,
height: 40,
child: const Icon(
Icons.my_location,
color: Colors.blue,
size: 30,
),
),
],
),
],
),
),
if (_isPanelOpen)
Positioned(
left: 0,
right: 0,
bottom: 0,
child: BottomOperationPanel(
// 传入初始值
initialRobotMode:RobotMode.point ,
initialAreaMode: AreaMode.work,
// 接收面板的事件回调
onComplete: () {
// 完成按钮点击:关闭面板
setState(() => _isPanelOpen = false);
},
onRobotModeChanged: (mode) {
// 接收机器人模式变更
setState(() => _currentRobotMode = mode);
debugPrint('外部收到机器人模式:$mode');
},
onAreaModeChanged: (mode) {
// 接收作业区域模式变更
setState(() => _currentAreaMode = mode);
debugPrint('外部收到区域模式:$mode');
},
onAddTap: () {
// 加号按钮自定义逻辑
debugPrint('外部处理加号按钮点击');
},
onLandTap: () {
// 地块标签自定义逻辑
debugPrint('外部处理地块标签点击');
},
onRouteTap: () {
// 航线标签自定义逻辑
debugPrint('外部处理航线标签点击');
},
height: 336,
right: 16,
top: maxTop > 10 ? 11 : maxTop, // 确保不超出屏幕范围
child: FloatingActionButton(
onPressed: _moveToCurrentLocation,
child: new VerticalFloatMenu(
onEditTap: (bool isOpen) {
// 关键:接收子组件的回调,修改外部的_isPanelOpen
setState(() {
_isPanelOpen = isOpen;
});
},
onWorkModeSelected: (mode) {
// 接收作业模式选择结果
_currentWorkMode = mode;
debugPrint('外部收到作业模式:$mode');
},
),
),
),
],
if (_isPanelOpen)
Positioned(
left: 0,
right: 0,
bottom: 0,
child: BottomOperationPanel(
// 传入初始值
initialRobotMode: RobotMode.point,
initialAreaMode: AreaMode.work,
// 接收面板的事件回调
onComplete: () {
// 完成按钮点击:关闭面板
setState(() => _isPanelOpen = false);
},
onRobotModeChanged: (mode) {
// 接收机器人模式变更
setState(() => _currentRobotMode = mode);
debugPrint('外部收到机器人模式:$mode');
},
onAreaModeChanged: (mode) {
// 接收作业区域模式变更
setState(() => _currentAreaMode = mode);
debugPrint('外部收到区域模式:$mode');
},
onAddTap: () {
// 加号按钮自定义逻辑
_addMapMarker();
debugPrint('外部处理加号按钮点击');
},
onSettingTap: () {
// 设置按钮自定义逻辑
debugPrint('外部处理设置按钮点击');
setState(() {
_directionBoxOpen = true;
});
},
onLandTap: () {
// 地块标签自定义逻辑
debugPrint('外部处理地块标签点击');
},
onRouteTap: () {
// 航线标签自定义逻辑
debugPrint('外部处理航线标签点击');
},
),
),
if (_directionBoxOpen)
Positioned(
left: 0,
right: 0,
bottom: 0,
child: RouteDirectionPanel(
initialOptimalHeading: true,
initialDirection: 0.0,
onConfirm: (result) {
// 处理确认结果
debugPrint(
'最优航向:${result['optimalHeading']},角度:${result['direction']}',
);
setState(() => _directionBoxOpen = false);
},
onCancel: () {
// 关闭面板
setState(() => _directionBoxOpen = false);
},
),
),
],
),
),
));
);
}
}
@@ -218,8 +278,7 @@ LatLng wgs84ToGcj02(double lat, double lon) {
double magic = sin(radLat);
magic = 1 - _ee * magic * magic;
double sqrtMagic = sqrt(magic);
dLat = (dLat * 180.0) /
((_a * (1 - _ee)) / (magic * sqrtMagic) * _pi);
dLat = (dLat * 180.0) / ((_a * (1 - _ee)) / (magic * sqrtMagic) * _pi);
dLon = (dLon * 180.0) / (_a / sqrtMagic * cos(radLat) * _pi);
double mgLat = lat + dLat;
double mgLon = lon + dLon;
@@ -227,52 +286,29 @@ LatLng wgs84ToGcj02(double lat, double lon) {
}
bool _outOfChina(double lat, double lon) {
return lon < 72.004 ||
lon > 137.8347 ||
lat < 0.8293 ||
lat > 55.8271;
return lon < 72.004 || lon > 137.8347 || lat < 0.8293 || lat > 55.8271;
}
double _transformLat(double x, double y) {
double ret = -100.0 +
double ret =
-100.0 +
2.0 * x +
3.0 * y +
0.2 * y * y +
0.1 * x * y +
0.2 * sqrt(x.abs());
ret += (20.0 * sin(6.0 * x * _pi) +
20.0 * sin(2.0 * x * _pi)) *
2.0 /
3.0;
ret += (20.0 * sin(y * _pi) +
40.0 * sin(y / 3.0 * _pi)) *
2.0 /
3.0;
ret += (160.0 * sin(y / 12.0 * _pi) +
320 * sin(y * _pi / 30.0)) *
2.0 /
3.0;
ret += (20.0 * sin(6.0 * x * _pi) + 20.0 * sin(2.0 * x * _pi)) * 2.0 / 3.0;
ret += (20.0 * sin(y * _pi) + 40.0 * sin(y / 3.0 * _pi)) * 2.0 / 3.0;
ret += (160.0 * sin(y / 12.0 * _pi) + 320 * sin(y * _pi / 30.0)) * 2.0 / 3.0;
return ret;
}
double _transformLon(double x, double y) {
double ret = 300.0 +
x +
2.0 * y +
0.1 * x * x +
0.1 * x * y +
0.1 * sqrt(x.abs());
ret += (20.0 * sin(6.0 * x * _pi) +
20.0 * sin(2.0 * x * _pi)) *
2.0 /
3.0;
ret += (20.0 * sin(x * _pi) +
40.0 * sin(x / 3.0 * _pi)) *
2.0 /
3.0;
ret += (150.0 * sin(x / 12.0 * _pi) +
300.0 * sin(x / 30.0 * _pi)) *
2.0 /
3.0;
double ret =
300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(x.abs());
ret += (20.0 * sin(6.0 * x * _pi) + 20.0 * sin(2.0 * x * _pi)) * 2.0 / 3.0;
ret += (20.0 * sin(x * _pi) + 40.0 * sin(x / 3.0 * _pi)) * 2.0 / 3.0;
ret +=
(150.0 * sin(x / 12.0 * _pi) + 300.0 * sin(x / 30.0 * _pi)) * 2.0 / 3.0;
return ret;
}

View File

@@ -4,7 +4,13 @@ import 'package:maibu_satabot_v2/features/home/presentation/widgets/common/enum.
class VerticalFloatMenu extends StatefulWidget {
final Function(int index, String name)? onItemTap;
final ValueChanged<bool>? onEditTap;
const VerticalFloatMenu({super.key, this.onItemTap, this.onEditTap});
final ValueChanged<WorkMode>? onWorkModeSelected; // 新增:作业模式选择回调
const VerticalFloatMenu({
super.key,
this.onItemTap,
this.onEditTap,
this.onWorkModeSelected,
});
@override
State<VerticalFloatMenu> createState() => _VerticalFloatMenuState();
@@ -63,6 +69,8 @@ class _VerticalFloatMenuState extends State<VerticalFloatMenu> {
if (result != null) {
_selectedWorkMode = result;
widget.onEditTap?.call(true);
// ========== 核心修改:传递result到外部 ==========
widget.onWorkModeSelected?.call(result);
//_showBottomPanel();
}
}
@@ -210,10 +218,6 @@ class _VerticalFloatMenuState extends State<VerticalFloatMenu> {
);
}
void _handleSubmit() {
debugPrint("作业模式: $_selectedWorkMode");
debugPrint("机器人模式: $_robotMode");

View File

@@ -7,6 +7,8 @@ class BottomOperationPanel extends StatefulWidget {
// 外部传入的初始值
final RobotMode initialRobotMode;
final AreaMode initialAreaMode;
// 新增:外部控制初始选中的标签(默认地块)
final String initialSelectedTab;
// 回调函数:向外部传递事件
final VoidCallback onComplete; // 完成按钮点击回调
final ValueChanged<RobotMode>? onRobotModeChanged; // 机器人模式变更回调
@@ -14,17 +16,20 @@ class BottomOperationPanel extends StatefulWidget {
final VoidCallback? onAddTap; // 加号按钮点击回调
final VoidCallback? onLandTap; // 地块标签点击回调
final VoidCallback? onRouteTap; // 航线标签点击回调
final VoidCallback? onSettingTap; // 设置标签点击回调
const BottomOperationPanel({
super.key,
this.initialRobotMode = RobotMode.point,
this.initialAreaMode = AreaMode.work,
this.initialSelectedTab = '地块', // 默认选中地块
required this.onComplete, // 完成按钮必须传
this.onRobotModeChanged,
this.onAreaModeChanged,
this.onAddTap,
this.onLandTap,
this.onRouteTap,
this.onSettingTap,
});
@override
@@ -34,6 +39,10 @@ class BottomOperationPanel extends StatefulWidget {
class _BottomOperationPanelState extends State<BottomOperationPanel> {
late RobotMode _robotMode;
late AreaMode _areaMode;
// 新增:当前选中的标签(地块/航线)- 核心状态
late String _currentTab;
// 新增:航线模式下的作业行距(默认0.5米)
double _operationDistance = 0.5;
@override
void initState() {
@@ -41,6 +50,8 @@ class _BottomOperationPanelState extends State<BottomOperationPanel> {
// 初始化外部传入的默认值
_robotMode = widget.initialRobotMode;
_areaMode = widget.initialAreaMode;
// 初始化选中的标签
_currentTab = widget.initialSelectedTab;
}
@override
@@ -52,87 +63,13 @@ class _BottomOperationPanelState extends State<BottomOperationPanel> {
boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 8)],
),
padding: const EdgeInsets.all(16),
constraints: const BoxConstraints(minHeight: 260),
//constraints: const BoxConstraints(minHeight: 260),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 面板核心内容
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
DropdownButton<RobotMode>(
value: _robotMode,
items: const [
DropdownMenuItem(
value: RobotMode.point,
child: Text("地图打点"),
),
DropdownMenuItem(
value: RobotMode.robot,
child: Text("机器人模式"),
),
],
onChanged: (v) {
if (v != null) {
setState(() => _robotMode = v);
// 触发回调,把选中的模式传给外部
widget.onRobotModeChanged?.call(v);
}
},
),
GestureDetector(
onTap: () {
debugPrint('点击了添加按钮');
// 触发加号按钮回调
widget.onAddTap?.call();
},
child: Container(
width: 60,
height: 60,
decoration: const BoxDecoration(
color: Color(0xFF00C853),
shape: BoxShape.circle,
),
child: const Icon(Icons.add, color: Colors.white, size: 32),
),
),
DropdownButton<AreaMode>(
value: _areaMode,
items: const [
DropdownMenuItem(value: AreaMode.work, child: Text("作业区域")),
DropdownMenuItem(
value: AreaMode.obstacle,
child: Text("障碍区域"),
),
],
onChanged: (v) {
if (v != null) {
setState(() => _areaMode = v);
// 触发回调,把选中的区域模式传给外部
widget.onAreaModeChanged?.call(v);
}
},
),
],
),
const SizedBox(height: 32),
// 操作按钮行
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildActionButton(Icons.delete_outline, '删除', true, () {}),
_buildActionButton(Icons.undo_outlined, '撤回', true, () {}),
_buildActionButton(Icons.check_outlined, '完成', true, () {
// 触发完成按钮回调,通知外部关闭面板
widget.onComplete.call();
}),
],
),
const SizedBox(height: 32),
// 底部标签页切换
_buildTabContent(),
// 底部标签页切换(修复背景色切换+状态更新)
Container(
decoration: BoxDecoration(
color: Colors.grey[200],
@@ -143,13 +80,19 @@ class _BottomOperationPanelState extends State<BottomOperationPanel> {
Expanded(
child: GestureDetector(
onTap: () {
setState(() {
_currentTab = '地块'; // 更新选中状态
});
debugPrint('切换到地块标签');
widget.onLandTap?.call();
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10),
// 选中地块时背景为白色,否则透明
decoration: BoxDecoration(
color: Colors.white,
color: _currentTab == '地块'
? Colors.white
: Colors.transparent,
borderRadius: BorderRadius.circular(8),
),
child: const Center(
@@ -167,13 +110,29 @@ class _BottomOperationPanelState extends State<BottomOperationPanel> {
Expanded(
child: GestureDetector(
onTap: () {
setState(() {
_currentTab = '航线'; // 更新选中状态
});
debugPrint('切换到航线标签');
widget.onRouteTap?.call();
},
child: const Center(
child: Text(
'航线',
style: TextStyle(color: Colors.grey, fontSize: 16),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10),
// 选中航线时背景为白色,否则透明
decoration: BoxDecoration(
color: _currentTab == '航线'
? Colors.white
: Colors.transparent,
borderRadius: BorderRadius.circular(8),
),
child: const Center(
child: Text(
'航线',
style: TextStyle(
color: Colors.black87,
fontSize: 16,
),
),
),
),
),
@@ -188,6 +147,182 @@ class _BottomOperationPanelState extends State<BottomOperationPanel> {
);
}
// 地块模式内容构建方法
Widget _buildLandContent() {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
// 面板核心内容
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
DropdownButton<RobotMode>(
value: _robotMode,
items: const [
DropdownMenuItem(value: RobotMode.point, child: Text("地图打点")),
DropdownMenuItem(value: RobotMode.robot, child: Text("机器人模式")),
],
onChanged: (v) {
if (v != null) {
setState(() => _robotMode = v);
widget.onRobotModeChanged?.call(v);
}
},
),
GestureDetector(
onTap: () {
debugPrint('点击了添加按钮');
widget.onAddTap?.call();
},
child: Container(
width: 60,
height: 60,
decoration: const BoxDecoration(
color: Color(0xFF00C853),
shape: BoxShape.circle,
),
child: const Icon(Icons.add, color: Colors.white, size: 32),
),
),
DropdownButton<AreaMode>(
value: _areaMode,
items: const [
DropdownMenuItem(value: AreaMode.work, child: Text("作业区域")),
DropdownMenuItem(value: AreaMode.obstacle, child: Text("障碍区域")),
],
onChanged: (v) {
if (v != null) {
setState(() => _areaMode = v);
widget.onAreaModeChanged?.call(v);
}
},
),
],
),
const SizedBox(height: 32),
// 操作按钮行
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildActionButton(Icons.delete_outline, '删除', true, () {}),
_buildActionButton(Icons.undo_outlined, '撤回', true, () {}),
_buildActionButton(Icons.check_outlined, '完成', true, () {
widget.onComplete.call();
}),
],
),
const SizedBox(height: 32),
],
);
}
// 标签内容切换方法(地块/航线)
Widget _buildTabContent() {
if (_currentTab == '地块') {
// 地块模式:显示机器人/区域选择+操作按钮
return _buildLandContent();
} else {
// 航线模式:显示作业行距+航线方向设置
return Column(
children: [
// 作业行距配置行
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'作业行距(米)',
style: TextStyle(fontSize: 16, color: Colors.black87),
),
Row(
children: [
// 减号按钮
GestureDetector(
onTap: () {
setState(() {
if (_operationDistance > 0.1) {
_operationDistance -= 0.1;
}
});
},
child: Container(
width: 30,
height: 30,
decoration: BoxDecoration(
color: Colors.grey[200],
shape: BoxShape.circle,
),
child: const Icon(
Icons.remove,
size: 16,
color: Colors.black87,
),
),
),
const SizedBox(width: 10),
// 行距数值
Text(
'${_operationDistance.toStringAsFixed(1)}',
style: const TextStyle(fontSize: 16, color: Colors.black87),
),
const SizedBox(width: 10),
// 加号按钮
GestureDetector(
onTap: () {
setState(() {
_operationDistance += 0.1;
});
},
child: Container(
width: 30,
height: 30,
decoration: BoxDecoration(
color: Colors.grey[200],
shape: BoxShape.circle,
),
child: const Icon(
Icons.add,
size: 16,
color: Colors.black87,
),
),
),
],
),
],
),
const SizedBox(height: 16),
// 航线方向配置行
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'航线方向',
style: TextStyle(fontSize: 16, color: Colors.black87),
),
// 设置按钮
ElevatedButton(
onPressed: () {
widget.onSettingTap?.call();
debugPrint('点击了航线方向设置按钮');
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF00C853),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
),
child: const Text('设置'),
),
],
),
],
);
}
}
// 构建操作按钮
Widget _buildActionButton(
IconData icon,