抽离 保存弹窗

This commit is contained in:
mmc
2026-02-28 15:50:31 +08:00
parent cd2f94347a
commit bb7748a9f2
2 changed files with 107 additions and 73 deletions

View File

@@ -0,0 +1,78 @@
import 'package:flutter/material.dart';
/// 通用带输入框的确认弹窗组件
/// [title]:弹窗标题
/// [hintText]:输入框提示文字
/// [labelText]:输入框标签文字
/// [confirmText]:确认按钮文字(默认"保存")
/// [cancelText]:取消按钮文字(默认"取消")
/// [onConfirm]:确认按钮回调,参数为输入的文本,返回bool表示是否关闭弹窗
/// [inputValidator]:输入内容校验器,返回null表示校验通过,返回字符串表示错误提示
Future<void> showInputConfirmDialog({
required BuildContext context,
required String title,
required String hintText,
String labelText = '',
String confirmText = '保存',
String cancelText = '取消',
required Function(String inputText) onConfirm,
String? Function(String inputText)? inputValidator,
}) async {
// 输入框控制器
final TextEditingController _controller = TextEditingController();
await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(title),
content: TextField(
controller: _controller,
decoration: InputDecoration(
hintText: hintText,
border: const OutlineInputBorder(),
labelText: labelText,
),
autofocus: true, // 自动聚焦输入框
),
actions: [
// 取消按钮
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(cancelText),
),
// 确认按钮
TextButton(
onPressed: () {
// 获取输入文本并去空格
final String inputText = _controller.text.trim();
// 1. 执行自定义校验
if (inputValidator != null) {
final String? errorMsg = inputValidator(inputText);
if (errorMsg != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(errorMsg),
backgroundColor: Colors.red,
),
);
return;
}
}
// 2. 执行确认回调
onConfirm(inputText);
// 3. 关闭弹窗
Navigator.of(context).pop();
},
child: Text(confirmText),
),
],
);
},
);
}

View File

@@ -20,6 +20,7 @@ import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_cubi
import 'package:maibu_satabot_v2/features/devices/presentation/bloc/devices_state.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/common/input_confirm_dialog.dart';
import 'package:maibu_satabot_v2/features/home/presentation/widgets/common/tracepoint.dart';
import 'package:maibu_satabot_v2/features/home/presentation/widgets/map/CenterLocation.dart';
import 'package:maibu_satabot_v2/features/home/presentation/widgets/map/HeadingPointer.dart';
@@ -64,9 +65,9 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
bool _isRefreshing = false; // 新增:页面刷新状态标志
final MapController _mapController = MapController();
final TextEditingController _plotNameController = TextEditingController();
// 初始化轨迹管理器(泛型指定为LatLng)
late final TracePoint<PlotPoint> _traceManager;
late String workMode = "弓字模式"; // 作业模式:默认值为"弓字模式"
LatLng? _currentLatLng;
StreamSubscription<Position>? _positionSub;
@@ -215,65 +216,31 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
}
void _showSavePlotDialog() {
// 清空上次的输入内容
_plotNameController.clear();
// 显示弹窗
showDialog(
showInputConfirmDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('保存地块'),
content: TextField(
controller: _plotNameController,
decoration: const InputDecoration(
hintText: '请输入地块名称(如:北地块、一号田)',
border: OutlineInputBorder(),
labelText: '地块名称',
),
title: '保存地块', // 自定义标题
hintText: '请输入地块名称(如:北地块、一号田)', // 自定义输入提示
labelText: '地块名称', // 自定义输入框标签
confirmText: '保存', // 确认按钮文字
cancelText: '取消', // 取消按钮文字
// 输入校验器(可选)
inputValidator: (inputText) {
if (inputText.isEmpty) {
return '地块名称不能为空!';
}
if (inputText.length > 20) {
return '地块名称不能超过20个字符!';
}
return null; // 校验通过
},
onConfirm: (plotName) {
_savePlotData(plotName);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('地块「$plotName」保存成功!'),
backgroundColor: Colors.green,
),
actions: [
// 取消按钮
TextButton(
onPressed: () {
Navigator.of(context).pop(); // 关闭弹窗
},
child: const Text('取消'),
),
// 确认保存按钮
TextButton(
onPressed: () {
// 获取输入的地块名称并去除首尾空格
String plotName = _plotNameController.text.trim();
// 验证输入是否为空
if (plotName.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('地块名称不能为空!'),
backgroundColor: Colors.red,
),
);
return;
}
// 执行保存逻辑
_savePlotData(plotName);
// 关闭弹窗
Navigator.of(context).pop();
// 保存成功提示
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('地块「$plotName」保存成功!'),
backgroundColor: Colors.green,
),
);
},
child: const Text('保存'),
),
],
);
},
);
@@ -501,9 +468,7 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
_positionSub?.cancel();
_traceManager.reset();
_mapController.dispose();
// 修复:_sub 是 non-null 类型,直接 cancel 无需 ?
_sub.cancel();
_plotNameController.dispose(); // 新增:释放文本控制器资源
super.dispose();
}
@@ -555,9 +520,13 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
try {
final dynamic nestedJsonRaw =
selectedPlotPath.jsonData['jsonData'];
if (nestedJsonRaw != null && nestedJsonRaw is String) {
final String nestedJsonStr = nestedJsonRaw;
final dynamic parsedJson = jsonDecode(nestedJsonStr);
workMode = parsedJson['planModel'] == WorkMode.bow.value
? "弓字模式"
: "自定义模式";
if (parsedJson is Map<String, dynamic>) {
final Map<String, dynamic> nestedJson = parsedJson;
final dynamic rawPath = nestedJson['path'];
@@ -716,19 +685,6 @@ class _MapPageEnterpriseState extends State<MapPageEnterprise> {
return const SizedBox.shrink();
}
String workMode = "未知模式";
if (_selectedPlotPath != null) {
try {
final String nestedJsonStr =
_selectedPlotPath!.jsonData['jsonData'] as String;
final Map<String, dynamic> nestedJson = jsonDecode(nestedJsonStr);
workMode = nestedJson['planModel'] == WorkMode.bow.value
? "弓字模式"
: "自定义模式";
} catch (e) {
workMode = "未知模式";
}
}
return Positioned(
left: 0,
right: 0,