// 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? 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 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( 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 showAlert( BuildContext context, { required String title, String content = '', String confirmText = '确定', bool barrierDismissible = true, VoidCallback? onConfirm, bool force = false, }) async { await showDialog( context: context, barrierDismissible: force ? false : barrierDismissible, builder: (_) => CustomAlertDialog( title: title, content: content, cancelText: '', confirmText: confirmText, onConfirm: onConfirm, force: force, ), ); } static Future showInput( BuildContext context, { required String title, String hintText = '', String cancelText = '取消', String confirmText = '确定', bool barrierDismissible = true, bool force = false, }) async { final result = await showDialog( context: context, barrierDismissible: force ? false : barrierDismissible, builder: (_) => CustomAlertDialog( title: title, hintText: hintText, cancelText: cancelText, confirmText: confirmText, mode: DialogMode.input, force: force, ), ); return result ?? ''; } @override _CustomAlertDialogState createState() => _CustomAlertDialogState(); } class _CustomAlertDialogState extends State { 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, ), ), ), ); } }