Files
pad_scanner/lib/pages/boxing/dialogs/cross_paichan_dialog.dart
Misaka_Company c2d9f5fd56 refactor: extract boxing logic into dedicated modules with tests
- boxing_models.dart: add BoxingMode, BoxingPhase, BoxingPageArguments
- boxing_box_mutations.dart: box item add/replace/remove operations
- boxing_calculations.dart: pure calculation helpers (quantities, validation)
- boxing_status_presenter.dart: status bar text/dot determination
- dialogs/cross_paichan_dialog.dart: cross-paichan confirmation dialog
- widgets/boxing_notice_banners.dart: notice banner widgets
- Add unit tests for mutations, calculations, and status presenter

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-21 09:02:56 +08:00

127 lines
4.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
Future<bool> showCrossPaichanDialog({
required BuildContext context,
required String currentPaicha,
required String newPaicha,
}) async {
final dialogFocus = FocusNode();
var selectedIndex = 0; // 0 = "否" (default), 1 = "是"
final dialogFuture = showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (ctx) => StatefulBuilder(
builder: (ctx, setDialogState) {
void dismissAsNo() => Navigator.of(ctx).pop(false);
void confirmSwitch() => Navigator.of(ctx).pop(true);
return KeyboardListener(
focusNode: dialogFocus,
onKeyEvent: (event) {
if (event is! KeyDownEvent) return;
if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
setDialogState(() => selectedIndex = 0);
} else if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
setDialogState(() => selectedIndex = 1);
} else if (event.logicalKey == LogicalKeyboardKey.enter) {
selectedIndex == 0 ? dismissAsNo() : confirmSwitch();
} else if (event.logicalKey == LogicalKeyboardKey.escape) {
dismissAsNo();
}
},
child: PopScope(
canPop: false,
onPopInvokedWithResult: (didPop, _) {
if (!didPop) dismissAsNo();
},
child: AlertDialog(
backgroundColor: Colors.orange.shade50,
title: Row(
children: [
Icon(Icons.warning_amber, color: Colors.orange.shade800),
const SizedBox(width: 8),
Text(
'跨排产号扫描',
style: TextStyle(color: Colors.orange.shade900),
),
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'当前排产号:$currentPaicha\n'
'新排产号:$newPaicha\n\n'
'是否切换到新的排产号?\n\n'
'选择「是」将放弃当前已扫描的所有数据。',
style: const TextStyle(fontSize: 14),
),
const SizedBox(height: 16),
_DialogOptionButton(
label: '',
selected: selectedIndex == 0,
onTap: dismissAsNo,
),
const SizedBox(height: 8),
_DialogOptionButton(
label: '是,切换排产号',
selected: selectedIndex == 1,
onTap: confirmSwitch,
),
],
),
),
),
);
},
),
);
WidgetsBinding.instance.addPostFrameCallback((_) {
dialogFocus.requestFocus();
});
final result = await dialogFuture;
dialogFocus.dispose();
return result ?? false;
}
class _DialogOptionButton extends StatelessWidget {
final String label;
final bool selected;
final VoidCallback onTap;
const _DialogOptionButton({
required this.label,
required this.selected,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return SizedBox(
width: double.infinity,
height: 40,
child: ElevatedButton(
onPressed: onTap,
style: ElevatedButton.styleFrom(
backgroundColor: selected
? Colors.blue.shade700
: Colors.grey.shade200,
foregroundColor: selected ? Colors.white : Colors.black87,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
child: Text(
label,
style: TextStyle(
fontWeight: selected ? FontWeight.w800 : FontWeight.w600,
),
),
),
);
}
}