gcx/frontend/mobile/lib/shared/widgets/ai_confirm_dialog.dart

319 lines
10 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import '../../app/i18n/app_localizations.dart';
import '../../app/theme/app_colors.dart';
import '../../app/theme/app_typography.dart';
import '../../app/theme/app_spacing.dart';
import 'genex_button.dart';
/// AI操作确认弹窗组件
///
/// AI Agent 执行操作前的二次确认弹窗
/// 场景AI帮你出售、AI帮你购买、AI帮你转赠 等需要确认的代理操作
///
/// 展示内容:
/// - AI建议的操作描述
/// - 操作详情(金额/数量/对象等)
/// - 风险提示
/// - 确认/取消按钮
class AiConfirmDialog extends StatelessWidget {
final String actionTitle;
final String actionDescription;
final List<AiConfirmDetail> details;
final String? riskWarning;
final String? confirmText;
final String? cancelText;
final VoidCallback onConfirm;
final VoidCallback? onCancel;
final AiConfirmLevel level;
const AiConfirmDialog({
super.key,
required this.actionTitle,
required this.actionDescription,
required this.details,
this.riskWarning,
this.confirmText,
this.cancelText,
required this.onConfirm,
this.onCancel,
this.level = AiConfirmLevel.normal,
});
@override
Widget build(BuildContext context) {
final resolvedConfirmText = confirmText ?? context.t('aiChat.confirmAction');
final resolvedCancelText = cancelText ?? context.t('common.cancel');
return Dialog(
backgroundColor: Colors.transparent,
insetPadding: const EdgeInsets.symmetric(horizontal: 24),
child: Container(
decoration: BoxDecoration(
color: AppColors.surface,
borderRadius: AppSpacing.borderRadiusLg,
boxShadow: const [
BoxShadow(
color: Color(0x1A000000),
blurRadius: 24,
offset: Offset(0, 8),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Header with AI icon
_buildHeader(context),
// Body
Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Action Description
Text(
actionDescription,
style: AppTypography.bodyMedium.copyWith(
color: AppColors.textSecondary,
height: 1.5,
),
),
const SizedBox(height: 16),
// Detail Items
Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: AppColors.gray50,
borderRadius: AppSpacing.borderRadiusMd,
),
child: Column(
children: details.asMap().entries.map((entry) {
final isLast = entry.key == details.length - 1;
final detail = entry.value;
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
detail.label,
style: AppTypography.bodySmall.copyWith(
color: AppColors.textSecondary,
),
),
Text(
detail.value,
style: detail.isHighlight
? AppTypography.labelMedium.copyWith(
color: AppColors.primary,
)
: AppTypography.labelMedium,
),
],
),
if (!isLast) ...[
const SizedBox(height: 10),
Divider(color: AppColors.gray200, height: 1),
const SizedBox(height: 10),
],
],
);
}).toList(),
),
),
// Risk Warning
if (riskWarning != null) ...[
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: level == AiConfirmLevel.high
? AppColors.errorLight
: AppColors.warningLight,
borderRadius: AppSpacing.borderRadiusSm,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
level == AiConfirmLevel.high
? Icons.warning_amber_rounded
: Icons.info_outline_rounded,
size: 16,
color: level == AiConfirmLevel.high
? AppColors.error
: AppColors.warning,
),
const SizedBox(width: 8),
Expanded(
child: Text(
riskWarning!,
style: AppTypography.bodySmall.copyWith(
color: AppColors.gray700,
height: 1.4,
),
),
),
],
),
),
],
const SizedBox(height: 20),
// Buttons
GenexButton(
label: resolvedConfirmText,
onPressed: () {
Navigator.of(context).pop(true);
onConfirm();
},
),
const SizedBox(height: 8),
GenexButton(
label: resolvedCancelText,
variant: GenexButtonVariant.text,
onPressed: () {
Navigator.of(context).pop(false);
onCancel?.call();
},
),
],
),
),
],
),
),
);
}
Widget _buildHeader(BuildContext context) {
return Container(
padding: const EdgeInsets.fromLTRB(20, 20, 20, 16),
child: Row(
children: [
// AI Avatar
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
gradient: AppColors.primaryGradient,
borderRadius: AppSpacing.borderRadiusSm,
),
child: const Center(
child: Icon(Icons.auto_awesome_rounded, color: Colors.white, size: 20),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(context.t('aiFab.title'), style: AppTypography.labelMedium),
const SizedBox(height: 2),
Text(
actionTitle,
style: AppTypography.h3.copyWith(color: AppColors.primary),
),
],
),
),
// Level indicator
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: _levelColor.withValues(alpha: 0.1),
borderRadius: AppSpacing.borderRadiusFull,
),
child: Text(
_getLevelText(context),
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w600,
color: _levelColor,
),
),
),
],
),
);
}
Color get _levelColor {
switch (level) {
case AiConfirmLevel.low:
return AppColors.success;
case AiConfirmLevel.normal:
return AppColors.warning;
case AiConfirmLevel.high:
return AppColors.error;
}
}
String _getLevelText(BuildContext context) {
switch (level) {
case AiConfirmLevel.low:
return context.t('aiChat.riskLow');
case AiConfirmLevel.normal:
return context.t('aiChat.riskNormal');
case AiConfirmLevel.high:
return context.t('aiChat.riskHigh');
}
}
/// 显示AI确认弹窗的便捷方法
static Future<bool?> show(
BuildContext context, {
required String actionTitle,
required String actionDescription,
required List<AiConfirmDetail> details,
String? riskWarning,
String? confirmText,
String? cancelText,
AiConfirmLevel level = AiConfirmLevel.normal,
}) {
return showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (ctx) => AiConfirmDialog(
actionTitle: actionTitle,
actionDescription: actionDescription,
details: details,
riskWarning: riskWarning,
confirmText: confirmText,
cancelText: cancelText,
level: level,
onConfirm: () {},
),
);
}
}
/// AI确认弹窗的详情项
class AiConfirmDetail {
final String label;
final String value;
final bool isHighlight;
const AiConfirmDetail({
required this.label,
required this.value,
this.isHighlight = false,
});
}
/// AI操作风险等级
enum AiConfirmLevel {
/// 低风险:查看信息、获取建议等
low,
/// 需确认:购买、出售、转赠等涉及资产操作
normal,
/// 高风险:大额操作、提现到外部钱包等
high,
}