flutter_integrated_whb/lib/customWidget/custom_alert_dialog.dart

299 lines
8.5 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// custom_alert_dialog.dart
import 'package:flutter/material.dart';
import 'package:qhd_prevention/main.dart'; // 导入全局 navigatorKey
/// 对话框模式
enum DialogMode { text, input }
class CustomAlertDialog extends StatefulWidget {
final String title;
final String content;
final String hintText;
final String cancelText;
final String confirmText;
final VoidCallback? onCancel;
final VoidCallback? onConfirm;
final ValueChanged<String>? onInputConfirm;
final DialogMode mode;
final bool force;
const CustomAlertDialog({
Key? key,
required this.title,
this.content = '',
this.hintText = '',
this.cancelText = '取消',
this.confirmText = '确定',
this.onCancel,
this.onConfirm,
this.onInputConfirm,
this.mode = DialogMode.text,
this.force = false,
}) : super(key: key);
// ------------------ 静态快捷方法 ------------------
static Future<bool> showConfirm(
BuildContext context, {
required String title,
String content = '',
String cancelText = '取消',
String confirmText = '确定',
bool barrierDismissible = true,
VoidCallback? onConfirm,
bool force = false,
}) async {
final result = await showDialog<bool>(
context: context,
barrierDismissible: force ? false : barrierDismissible,
builder: (_) => CustomAlertDialog(
title: title,
content: content,
cancelText: cancelText,
confirmText: confirmText,
onConfirm: onConfirm,
force: force,
),
);
return result == true;
}
static Future<void> showAlert(
BuildContext context, {
required String title,
String content = '',
String confirmText = '确定',
bool barrierDismissible = true,
VoidCallback? onConfirm,
bool force = false,
}) async {
await showDialog<void>(
context: context,
barrierDismissible: force ? false : barrierDismissible,
builder: (_) => CustomAlertDialog(
title: title,
content: content,
cancelText: '',
confirmText: confirmText,
onConfirm: onConfirm,
force: force,
),
);
}
static Future<String?> showInput(
BuildContext context, {
required String title,
String hintText = '',
String cancelText = '取消',
String confirmText = '确定',
bool barrierDismissible = true,
bool force = false,
}) async {
final result = await showDialog<String?>(
context: context,
barrierDismissible: force ? false : barrierDismissible,
builder: (_) => CustomAlertDialog(
title: title,
hintText: hintText,
cancelText: cancelText,
confirmText: confirmText,
mode: DialogMode.input,
force: force,
),
);
// 取消/点遮罩会得到 null确认会得到 String可能为空串
return result;
}
@override
_CustomAlertDialogState createState() => _CustomAlertDialogState();
}
class _CustomAlertDialogState extends State<CustomAlertDialog> {
late TextEditingController _controller;
bool _isClosing = false;
@override
void initState() {
super.initState();
_controller = TextEditingController();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
bool get hasCancel => !widget.force && widget.cancelText.isNotEmpty;
void _closeDialog([dynamic result]) {
if (_isClosing) return;
_isClosing = true;
// 优先使用当前上下文导航
if (mounted) {
Navigator.of(context).pop(result);
} else {
// 后备方案:使用全局导航键
navigatorKey.currentState?.pop(result);
}
}
@override
Widget build(BuildContext context) {
return PopScope(
canPop: !widget.force,
child: Dialog(
backgroundColor: Colors.transparent,
child: Container(
constraints: const BoxConstraints(minWidth: 280),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Text(
widget.title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
),
const SizedBox(height: 16),
if (widget.mode == DialogMode.text)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Text(
widget.content,
style: const TextStyle(
fontSize: 16,
color: Colors.black54,
),
textAlign: TextAlign.center,
),
)
else
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: TextField(
controller: _controller,
autofocus: true,
decoration: InputDecoration(
hintText: widget.hintText,
border: const OutlineInputBorder(),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue, width: 1),
borderRadius: BorderRadius.circular(4),
),
isDense: true,
contentPadding: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 10,
),
),
),
),
const SizedBox(height: 20),
const Divider(height: 1),
hasCancel
? _buildDoubleButtons(context)
: _buildSingleButton(context),
],
),
),
),
);
}
Widget _buildDoubleButtons(BuildContext context) {
return Row(
children: [
Expanded(
child: InkWell(
onTap: () {
widget.onCancel?.call();
_closeDialog(widget.mode == DialogMode.text ? false : null);
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
alignment: Alignment.center,
child: Text(
widget.cancelText,
style: const TextStyle(
fontWeight: FontWeight.w500,
color: Colors.black87,
fontSize: 18,
),
),
),
),
),
Container(width: 1, height: 48, color: Colors.grey[300]),
Expanded(
child: InkWell(
onTap: () {
if (widget.mode == DialogMode.text) {
widget.onConfirm?.call();
_closeDialog(true);
} else {
final value = _controller.text.trim();
widget.onInputConfirm?.call(value);
_closeDialog(value);
}
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12),
alignment: Alignment.center,
child: Text(
widget.confirmText,
style: const TextStyle(
color: Color(0xFF3874F6),
fontWeight: FontWeight.w500,
fontSize: 18,
),
),
),
),
),
],
);
}
Widget _buildSingleButton(BuildContext context) {
return InkWell(
onTap: () {
if (widget.mode == DialogMode.text) {
widget.onConfirm?.call();
_closeDialog(true);
} else {
final value = _controller.text.trim();
widget.onInputConfirm?.call(value);
_closeDialog(value);
}
},
child: Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 14),
alignment: Alignment.center,
child: Text(
widget.confirmText,
style: const TextStyle(
color: Color(0xFF3874F6),
fontWeight: FontWeight.w500,
fontSize: 18,
),
),
),
);
}
}