import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import '../utils/img_url.dart'; /// ============================================================ /// 【ConfirmBox】全局二次确认弹窗 —— 对标小程序 components/ConfirmBox /// /// 一、与 Vue3 版的架构差异: /// 小程序不支持动态插入 DOM,所以 Vue 版需要每个页面手动放置 /// ``,并在 service.js 里按路由注册 controller。 /// Flutter 的 showDialog 可以从任意 context 直接弹出(等价于 Web /// 动态挂载),因此这里无需注册机制,单文件即可全局使用。 /// /// 二、用法(对标 Vue 的 `await proxy.$confirm({...})`): /// final ok = await showConfirmBox( /// context, /// title: '提示', /// content: '确定要删除吗?', /// ); /// 点"确定"返回 true;点"取消"、点遮罩、按返回键返回 false。 /// ============================================================ Future showConfirmBox( BuildContext context, { String title = '提示', String content = '', String cancelText = '取消', String confirmText = '确定', bool showCancel = true, double? minWidth, // 弹窗最小宽度(逻辑像素,调用方自行 .w 换算) }) { // 默认弹窗固定宽度 310.w(按钮 135.w × 2 + 间距 10.w + 左右 15.w×2 = 310.w) minWidth ??= 310.w; return showDialog( context: context, barrierDismissible: true, // 点遮罩关闭 → false(对标 onPopupClose) barrierColor: Colors.black54, builder: (_) => ConfirmBox( title: title, content: content, cancelText: cancelText, confirmText: confirmText, showCancel: showCancel, minWidth: minWidth, ), ).then((result) => result ?? false); } class ConfirmBox extends StatelessWidget { const ConfirmBox({ super.key, required this.title, required this.content, required this.cancelText, required this.confirmText, required this.showCancel, this.minWidth, }); final String title; final String content; final String cancelText; final String confirmText; final bool showCancel; final double? minWidth; @override Widget build(BuildContext context) { return Dialog( backgroundColor: Colors.transparent, // Dialog 默认左右 insetPadding: 40 太宽,310.w 在手机上放不下 // 这里减到 20,给 310.w 固定宽度留足空间(375-40=335 > 310) insetPadding: EdgeInsets.symmetric(horizontal: 20.w), child: Container( width: minWidth, // 固定宽度(minWidth 语义变了:这里就是固定宽) padding: EdgeInsets.symmetric(vertical: 30.w, horizontal: 15.w), decoration: BoxDecoration( borderRadius: BorderRadius.circular(20.w), // 40rpx image: DecorationImage( image: NetworkImage( ImgUrl.build('/StaticResource/2026-03-31/header-bac.png'), ), fit: BoxFit.fill, // background-size: 100% 100% ), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ if (title.isNotEmpty) Text( title, style: TextStyle( fontSize: 16.sp, // 32rpx fontWeight: FontWeight.w600, color: const Color(0xFF1D2B47), ), ), if (content.isNotEmpty) Padding( padding: EdgeInsets.only(top: 18.w), // 36rpx child: ConstrainedBox( constraints: BoxConstraints( maxWidth: 263.w, ), // max-width 526rpx child: Text( content, style: TextStyle( fontSize: 14.sp, // 28rpx color: const Color(0xFF1D2B47), ), ), ), ), Padding( padding: EdgeInsets.only(top: 23.w), // 46rpx child: Row( mainAxisSize: MainAxisSize.min, children: [ if (showCancel) ...[ _buildButton( text: cancelText, backgroundColor: const Color(0xFFFDFFFF), textColor: const Color(0xFF6195F6), onTap: () => Navigator.of(context).pop(false), isConfirm: false, ), SizedBox(width: 10.w), // 20rpx ], _buildButton( text: confirmText, gradient: const LinearGradient( // CSS: linear-gradient(360deg, #8AD1FD -11.22%, #3C84FF 60.2%, #3D7DF5 100%) begin: Alignment.bottomCenter, end: Alignment.topCenter, colors: [ Color(0xFF8AD1FD), Color(0xFF3C84FF), Color(0xFF3D7DF5), ], stops: [0, 0.602, 1], ), textColor: Colors.white, onTap: () => Navigator.of(context).pop(true), isConfirm: true, ), ], ), ), ], ), ), ); } /// 胶囊按钮:取消键(白底+蓝字+边框+阴影)/ 确定键(渐变+边框+阴影) Widget _buildButton({ required String text, required Color textColor, required VoidCallback onTap, required bool isConfirm, Color? backgroundColor, Gradient? gradient, }) { return GestureDetector( onTap: onTap, child: Container( width: 135.w, height: 49.w, alignment: Alignment.center, decoration: BoxDecoration( color: backgroundColor, gradient: gradient, borderRadius: BorderRadius.circular(50.w), boxShadow: [ if (isConfirm) BoxShadow( color: Colors.black.withValues(alpha: 0.25), offset: Offset(0, 1.w), blurRadius: 9.2.w, spreadRadius: 3.w, ) else BoxShadow( color: Colors.black.withValues(alpha: 0.25), offset: Offset(0, 2.w), blurRadius: 12.2.w, spreadRadius: 0.5.w, ), ], ), child: Text( text, style: TextStyle(fontSize: 16.sp, color: textColor), ), ), ); } }