285 lines
8.8 KiB
Dart
Executable File
285 lines
8.8 KiB
Dart
Executable File
import 'dart:ui';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||
import 'package:get/get.dart';
|
||
import 'package:kaer_with_panels/app/localization/app_translations.dart';
|
||
|
||
class HIDialog extends StatefulWidget {
|
||
final String? title;
|
||
final String? message;
|
||
final String? confirmText;
|
||
final String? cancelText;
|
||
final VoidCallback? onConfirm;
|
||
final VoidCallback? onCancel;
|
||
final Widget? customMessageWidget;
|
||
|
||
/// 是否允许点击外部关闭
|
||
final bool barrierDismissible;
|
||
|
||
/// 是否禁止物理返回键关闭
|
||
final bool preventBackDismiss;
|
||
|
||
/// 是否点击确认后自动关闭(默认 true)
|
||
final bool autoClose;
|
||
|
||
/// 是否显示按钮 loading(autoClose=false 时自动启用)
|
||
final bool? showLoading;
|
||
final Offset? targetOffset;
|
||
|
||
static double get kDialogWidth => 245.w < 280.0 ? 280.0 : 245.w;
|
||
|
||
const HIDialog({
|
||
Key? key,
|
||
this.title,
|
||
this.message,
|
||
this.confirmText,
|
||
this.cancelText,
|
||
this.onConfirm,
|
||
this.onCancel,
|
||
this.customMessageWidget,
|
||
this.barrierDismissible = true,
|
||
this.preventBackDismiss = false,
|
||
this.autoClose = true,
|
||
this.showLoading,
|
||
this.targetOffset,
|
||
}) : super(key: key);
|
||
|
||
static Future<void> show({
|
||
String? title,
|
||
String? message,
|
||
String? confirmText,
|
||
String? cancelText,
|
||
VoidCallback? onConfirm,
|
||
VoidCallback? onCancel,
|
||
Widget? customMessageWidget,
|
||
bool barrierDismissible = true,
|
||
bool preventBackDismiss = false,
|
||
bool autoClose = true,
|
||
bool? showLoading,
|
||
Offset? targetOffset,
|
||
}) {
|
||
return Get.dialog(
|
||
HIDialog(
|
||
title: title,
|
||
message: message,
|
||
confirmText: confirmText,
|
||
cancelText: cancelText,
|
||
onConfirm: onConfirm,
|
||
onCancel: onCancel,
|
||
customMessageWidget: customMessageWidget,
|
||
barrierDismissible: barrierDismissible,
|
||
preventBackDismiss: preventBackDismiss,
|
||
autoClose: autoClose,
|
||
showLoading: showLoading,
|
||
targetOffset: targetOffset,
|
||
),
|
||
barrierDismissible: barrierDismissible,
|
||
barrierColor: Colors.transparent,
|
||
useSafeArea: false,
|
||
);
|
||
}
|
||
|
||
@override
|
||
State<HIDialog> createState() => _HIDialogState();
|
||
}
|
||
|
||
class _HIDialogState extends State<HIDialog> {
|
||
bool _isLoading = false;
|
||
|
||
Future<void> _handleConfirm() async {
|
||
if (_isLoading) return;
|
||
|
||
final useLoading = widget.showLoading ?? !widget.autoClose;
|
||
if (useLoading) {
|
||
setState(() => _isLoading = true);
|
||
}
|
||
|
||
if (widget.autoClose) {
|
||
Get.back();
|
||
}
|
||
|
||
await Future.microtask(() => widget.onConfirm?.call());
|
||
|
||
if (useLoading && mounted) {
|
||
setState(() => _isLoading = false);
|
||
}
|
||
}
|
||
|
||
Widget _buildConfirmButton() {
|
||
return TextButton(
|
||
onPressed: _isLoading ? null : _handleConfirm,
|
||
style: TextButton.styleFrom(
|
||
backgroundColor: const Color(0xFFADFF5B),
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(23.r),
|
||
),
|
||
minimumSize: Size(85.w, 40),
|
||
padding: EdgeInsets.zero,
|
||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||
),
|
||
child: _isLoading
|
||
? SizedBox(
|
||
height: 20.w,
|
||
width: 20.w,
|
||
child: const CircularProgressIndicator(
|
||
color: Colors.black,
|
||
strokeWidth: 2,
|
||
),
|
||
)
|
||
: Text(
|
||
widget.confirmText ??
|
||
(widget.cancelText == null ? '好的' : AppTranslations.kr_dialog.kr_confirm),
|
||
style: TextStyle(
|
||
fontSize: 16.sp,
|
||
fontWeight: FontWeight.w600,
|
||
color: Colors.black,
|
||
fontFamily: 'AlibabaPuHuiTi-Medium',
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _buildDialogContent() {
|
||
return ClipRRect(
|
||
borderRadius: BorderRadius.circular(34.r),
|
||
child: BackdropFilter(
|
||
filter: ImageFilter.blur(sigmaX: 26, sigmaY: 26), // 毛玻璃模糊
|
||
child: Container(
|
||
width: 245.w,
|
||
padding: EdgeInsets.fromLTRB(30.w, 16.w, 30.w, 16.w),
|
||
decoration: BoxDecoration(
|
||
color: const Color(0xFFDEDEDE), // 半透明底色
|
||
borderRadius: BorderRadius.circular(34.r),
|
||
border: Border.all(
|
||
color: Colors.white.withOpacity(0.2), // 高光边框
|
||
width: 1.5,
|
||
),
|
||
),
|
||
child: Column(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
if (widget.title != null) ...[
|
||
Align(
|
||
alignment: Alignment.centerLeft,
|
||
child: Text(
|
||
widget.title!,
|
||
style: TextStyle(
|
||
fontSize: 14.sp,
|
||
fontWeight: FontWeight.w600,
|
||
color: Colors.black,
|
||
height: 1.3,
|
||
),
|
||
),
|
||
),
|
||
SizedBox(height: 4.w),
|
||
],
|
||
if (widget.message != null || widget.customMessageWidget != null) ...[
|
||
Container(
|
||
constraints: BoxConstraints(maxHeight: 200.h),
|
||
child: SingleChildScrollView(
|
||
child: widget.customMessageWidget ??
|
||
Text(
|
||
widget.message!,
|
||
textAlign: TextAlign.left,
|
||
style: TextStyle(
|
||
fontSize: 14.sp,
|
||
color: Colors.black,
|
||
height: 1.4,
|
||
fontFamily: 'AlibabaPuHuiTi-Medium',
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
if (widget.confirmText != null ||
|
||
widget.cancelText != null ||
|
||
(widget.confirmText == null && widget.cancelText == null)) ...[
|
||
SizedBox(height: 12.w),
|
||
if (widget.confirmText == null && widget.cancelText == null)
|
||
Center(
|
||
child: SizedBox(
|
||
width: 85.w,
|
||
child: _buildConfirmButton(),
|
||
),
|
||
)
|
||
else
|
||
Row(
|
||
children: [
|
||
if (widget.confirmText != null) Expanded(child: _buildConfirmButton()),
|
||
if (widget.cancelText != null && widget.confirmText != null)
|
||
SizedBox(width: 12.w),
|
||
if (widget.cancelText != null)
|
||
Expanded(
|
||
child: TextButton(
|
||
onPressed: () {
|
||
Get.back();
|
||
widget.onCancel?.call();
|
||
},
|
||
style: TextButton.styleFrom(
|
||
backgroundColor: const Color(0xFFFF00B7),
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.circular(23.r),
|
||
),
|
||
minimumSize: Size(85.w, 40.w),
|
||
padding: EdgeInsets.zero,
|
||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||
),
|
||
child: Text(
|
||
widget.cancelText!,
|
||
style: TextStyle(
|
||
fontSize: 16.sp,
|
||
fontWeight: FontWeight.w600,
|
||
color: Colors.black,
|
||
fontFamily: 'AlibabaPuHuiTi-Medium',
|
||
),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
]
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
if (widget.targetOffset != null) {
|
||
return Stack(
|
||
fit: StackFit.expand,
|
||
children: [
|
||
Positioned(
|
||
left: widget.targetOffset!.dx,
|
||
top: widget.targetOffset!.dy,
|
||
child: Material(
|
||
type: MaterialType.transparency,
|
||
child: ConstrainedBox(
|
||
constraints: const BoxConstraints(minWidth: 280.0),
|
||
child: _buildDialogContent(),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
final dialog = Dialog(
|
||
backgroundColor: Colors.transparent,
|
||
insetPadding: EdgeInsets.all(20.w),
|
||
child: Transform.translate(
|
||
offset: Offset(0, -30.w),
|
||
child: _buildDialogContent(),
|
||
),
|
||
);
|
||
|
||
if (widget.preventBackDismiss) {
|
||
return WillPopScope(onWillPop: () async => false, child: dialog);
|
||
}
|
||
|
||
return dialog;
|
||
}
|
||
}
|