Automated null-safety fixes driven by dart analyze (no corruption): - unchecked_use_of_nullable_value: insert '!' on receiver (property/method/[]/op) - not_initialized field/var: mark 'late' - missing_default_value_for_parameter: nullable param Errors: 2374(peak) -> 907
207 lines
7.1 KiB
Dart
207 lines
7.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
|
|
import '../../constants.dart';
|
|
import '../../generated/l10n.dart';
|
|
import '../../models/order.dart';
|
|
import '../../routes.dart';
|
|
import '../../store/actions.dart';
|
|
import '../../store/store.dart';
|
|
|
|
/// e-Transfer 支付页(替代原 Stripe 收款)。
|
|
/// 向顾客展示 e-Transfer 邮箱,并提醒在备注里写 R#<receiptId> 以便自动处理订单。
|
|
class ETransferPay extends StatelessWidget {
|
|
final Order order;
|
|
|
|
const ETransferPay({required this.order});
|
|
|
|
String get _remark =>
|
|
'${Constants.ETRANSFER_REMARK_PREFIX}${order.id}';
|
|
|
|
Future<void> _copy(BuildContext context, String text, String label) async {
|
|
await Clipboard.setData(ClipboardData(text: text));
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('$label copied')),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
store.dispatch(UpdateContext(context));
|
|
final isWide = MediaQuery.of(context).size.width > 700;
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
leading: IconButton(
|
|
icon: const Icon(Icons.arrow_back_ios),
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
),
|
|
title: const Text('e-Transfer Payment'),
|
|
),
|
|
body: Center(
|
|
child: ConstrainedBox(
|
|
constraints: BoxConstraints(
|
|
maxWidth: isWide ? 640 : double.infinity,
|
|
),
|
|
child: SingleChildScrollView(
|
|
padding: const EdgeInsets.all(24),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
// 金额
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
vertical: 24, horizontal: 16),
|
|
decoration: BoxDecoration(
|
|
color: Colors.lightBlueAccent.withOpacity(0.12),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
S.of(context).payment_amount,
|
|
style: const TextStyle(
|
|
fontSize: 15, color: Colors.black54),
|
|
),
|
|
Text(
|
|
'\$${order.totalPrice!.toStringAsFixed(2)}',
|
|
style: const TextStyle(
|
|
fontSize: 28,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
const Text(
|
|
'Please complete your payment via Interac e-Transfer:',
|
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
|
|
),
|
|
const SizedBox(height: 16),
|
|
// 邮箱
|
|
_CopyRow(
|
|
icon: Icons.alternate_email,
|
|
title: 'Send e-Transfer to',
|
|
value: Constants.ETRANSFER_EMAIL,
|
|
onCopy: () => _copy(
|
|
context, Constants.ETRANSFER_EMAIL, 'Email'),
|
|
),
|
|
const SizedBox(height: 16),
|
|
// 备注
|
|
Container(
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFFFF8E1),
|
|
border: Border.all(color: Colors.orangeAccent),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const Row(
|
|
children: [
|
|
Icon(Icons.priority_high, color: Colors.orange),
|
|
SizedBox(width: 8),
|
|
Text(
|
|
'Important — message / remark',
|
|
style: TextStyle(fontWeight: FontWeight.bold),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 8),
|
|
const Text(
|
|
'In the e-Transfer message (remark) field, please include '
|
|
'the following code so we can match and process your order '
|
|
'automatically:',
|
|
),
|
|
const SizedBox(height: 12),
|
|
_CopyRow(
|
|
icon: Icons.receipt_long,
|
|
title: 'Order code',
|
|
value: _remark,
|
|
emphasize: true,
|
|
onCopy: () =>
|
|
_copy(context, _remark, 'Order code'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
FilledButton.icon(
|
|
icon: const Icon(Icons.check_circle_outline),
|
|
label: const Text("I've sent the payment"),
|
|
style: FilledButton.styleFrom(
|
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
onPressed: () {
|
|
Routes.router.navigateTo(context, '/orderdetail/${order.id}',
|
|
replace: true);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _CopyRow extends StatelessWidget {
|
|
final IconData icon;
|
|
final String title;
|
|
final String value;
|
|
final VoidCallback onCopy;
|
|
final bool emphasize;
|
|
|
|
const _CopyRow({
|
|
required this.icon,
|
|
required this.title,
|
|
required this.value,
|
|
required this.onCopy,
|
|
this.emphasize = false,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
|
decoration: BoxDecoration(
|
|
border: Border.all(color: Colors.black12),
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(icon, color: Colors.lightBlueAccent),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(title,
|
|
style: const TextStyle(
|
|
fontSize: 12, color: Colors.black54)),
|
|
Text(
|
|
value,
|
|
style: TextStyle(
|
|
fontSize: emphasize ? 20 : 16,
|
|
fontWeight:
|
|
emphasize ? FontWeight.bold : FontWeight.w600,
|
|
letterSpacing: emphasize ? 1.0 : 0,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
IconButton(
|
|
icon: const Icon(Icons.copy, color: Colors.black54),
|
|
onPressed: onCopy,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|