/// ============================================================ /// 【ExerciseConfirmBox】通用确认弹窗 /// (1:1 迁移 Vue classExercises/ExerciseConfirmBox.vue) /// /// 交互点: /// - 点遮罩 / 取消:onCancel(父级负责把 show 置 false) /// - 点确定:onConfirm(不主动关闭,与 Vue 一致由业务决定) /// - showCancel=false 时只显示单满宽确定按钮 /// ============================================================ library; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:new_learn/core/utils/img_url.dart'; import 'exercise_styles.dart'; class ExerciseConfirmBox extends StatelessWidget { const ExerciseConfirmBox({ super.key, required this.show, this.title = '提示', this.content = '', this.cancelText = '取消', this.confirmText = '确定', this.showCancel = true, this.onCancel, this.onConfirm, }); final bool show; final String title; final String content; final String cancelText; final String confirmText; final bool showCancel; final VoidCallback? onCancel; final VoidCallback? onConfirm; @override Widget build(BuildContext context) { if (!show) return const SizedBox.shrink(); return Stack( children: [ // mask rgba(0,0,0,.45),点击=cancel Positioned.fill( child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: onCancel, child: const ColoredBox(color: Color(0x73000000)), ), ), Center( child: Container( width: 315.w, // 630rpx, max calc(100vw - 80rpx) constraints: BoxConstraints( maxWidth: MediaQuery.sizeOf(context).width - 40.w, ), padding: EdgeInsets.symmetric(vertical: 30.h, horizontal: 15.w), decoration: BoxDecoration( color: const Color(0xFFF5F8FA), borderRadius: BorderRadius.circular(20.r), image: DecorationImage( image: CachedNetworkImageProvider( ImgUrl.build('/StaticResource/2026-03-31/header-bac.png'), ), fit: BoxFit.fitWidth, alignment: Alignment.topCenter, ), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ if (title.isNotEmpty) Text( title, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, color: ExerciseColors.title, ), ), if (content.isNotEmpty) Padding( padding: EdgeInsets.only(top: 18.h), child: ConstrainedBox( constraints: BoxConstraints(maxWidth: 263.w), child: Text( content, textAlign: TextAlign.center, style: TextStyle( fontSize: 14.sp, color: ExerciseColors.title, height: 1.5, ), ), ), ), Padding( padding: EdgeInsets.only(top: 23.h), child: showCancel ? Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ExerciseButton( text: cancelText, style: ExerciseButtonStyle.secondary, width: 135.w, onTap: onCancel, ), SizedBox(width: 10.w), ExerciseButton( text: confirmText, style: ExerciseButtonStyle.primary, width: 135.w, onTap: onConfirm, ), ], ) : ExerciseButton( text: confirmText, style: ExerciseButtonStyle.primary, width: double.infinity, onTap: onConfirm, ), ), ], ), ), ), ], ); } }