import 'package:flutter/material.dart'; /// 对话框模式 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; // 对话框模式 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, }) : super(key: key); @override _CustomAlertDialogState createState() => _CustomAlertDialogState(); // ------------------ 快捷静态方法 ------------------ /// 带“取消/确定”的确认弹窗 /// 返回 true = 确定,false = 取消或关闭 static Future showConfirm( BuildContext context, { required String title, String content = '', String cancelText = '取消', String confirmText = '确定', bool barrierDismissible = true, final VoidCallback? onConfirm, }) async { final result = await showDialog( context: context, barrierDismissible: barrierDismissible, builder: (_) { return CustomAlertDialog( title: title, content: content, cancelText: cancelText, confirmText: confirmText, mode: DialogMode.text, onConfirm: onConfirm, ); }, ); return result == true; } /// 只有“确定”按钮的文字提示弹窗(适合提示信息) static Future showAlert( BuildContext context, { required String title, String content = '', String confirmText = '确定', bool barrierDismissible = true, final VoidCallback? onConfirm, }) async { await showDialog( context: context, barrierDismissible: barrierDismissible, builder: (_) { return CustomAlertDialog( title: title, content: content, cancelText: '', // 隐藏取消按钮使其成为单按钮 confirmText: confirmText, mode: DialogMode.text, onConfirm: onConfirm, ); }, ); } /// 输入对话框(带输入框),返回用户输入的字符串;取消或关闭返回 null static Future showInput( BuildContext context, { required String title, String hintText = '', String cancelText = '取消', String confirmText = '确定', bool barrierDismissible = true, }) async { final result = await showDialog( context: context, barrierDismissible: barrierDismissible, builder: (_) { return CustomAlertDialog( title: title, hintText: hintText, cancelText: cancelText, confirmText: confirmText, mode: DialogMode.input, ); }, ); return result; } } class _CustomAlertDialogState extends State { late TextEditingController _controller; @override void initState() { super.initState(); // 输入模式下初始化 TextField 控制器 _controller = TextEditingController(); } @override void dispose() { _controller.dispose(); super.dispose(); } bool get hasCancel => widget.cancelText.trim().isNotEmpty; @override Widget build(BuildContext context) { return 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), // ★ 根据 mode 决定展示文字还是输入框 ★ 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: () { // 根据模式返回不同值:文本模式返回 false,输入模式返回 null final ret = widget.mode == DialogMode.text ? false : null; // 先触发回调(如果开发者传了),再关闭并把结果返回给调用者 widget.onCancel?.call(); Navigator.of(context).pop(ret); }, 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(); Navigator.of(context).pop(true); } else { final value = _controller.text.trim(); widget.onInputConfirm?.call(value); Navigator.of(context).pop(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(); Navigator.of(context).pop(true); } else { final value = _controller.text.trim(); widget.onInputConfirm?.call(value); Navigator.of(context).pop(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, ), ), ), ); } }