- 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>
127 lines
4.0 KiB
Dart
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,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|