import 'package:flutter/material.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import '../../core/theme/app_spacing.dart'; import '../../core/theme/app_theme.dart'; /// 現代底部抽屜模板 - 基於 modernization-v2.md 規範 /// /// 使用方法: /// ```dart /// ModernBottomSheet.show( /// context: context, /// title: '選擇幣種', /// child: YourContentWidget(), /// ); /// ``` class ModernBottomSheet extends StatelessWidget { final String? title; final Widget? titleWidget; final Widget child; final double? height; final bool showDragHandle; final bool showCloseButton; final VoidCallback? onClose; const ModernBottomSheet({ super.key, this.title, this.titleWidget, required this.child, this.height, this.showDragHandle = true, this.showCloseButton = false, this.onClose, }); /// 顯示底部抽屜 static Future show({ required BuildContext context, String? title, Widget? titleWidget, required Widget child, double? height, bool showDragHandle = true, bool showCloseButton = false, bool isScrollControlled = true, bool isDismissible = true, bool enableDrag = true, }) { return showModalBottomSheet( context: context, isScrollControlled: isScrollControlled, isDismissible: isDismissible, enableDrag: enableDrag, backgroundColor: Colors.transparent, builder: (context) => ModernBottomSheet( title: title, titleWidget: titleWidget, child: child, height: height, showDragHandle: showDragHandle, showCloseButton: showCloseButton, ), ); } /// 顯示操作列表 static Future showActions({ required BuildContext context, String? title, required List actions, }) { return show( context: context, title: title, child: _ActionList(actions: actions), ); } /// 顯示確認操作 static Future confirmAction({ required BuildContext context, required String title, String? description, String confirmText = '確認', String cancelText = '取消', bool isDestructive = false, }) async { final result = await show( context: context, title: title, child: Column( mainAxisSize: MainAxisSize.min, children: [ if (description != null) ...[ Text( description, style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(height: AppSpacing.lg), ], Row( children: [ Expanded( child: OutlinedButton( onPressed: () => Navigator.of(context).pop(0), child: Text(cancelText), ), ), const SizedBox(width: AppSpacing.md), Expanded( child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: isDestructive ? Theme.of(context).colorScheme.error : null, ), onPressed: () => Navigator.of(context).pop(1), child: Text(confirmText), ), ), ], ), ], ), ); return result == 1; } @override Widget build(BuildContext context) { final theme = Theme.of(context); final bottomPadding = MediaQuery.of(context).padding.bottom; return Container( decoration: BoxDecoration( color: theme.colorScheme.surfaceContainer, borderRadius: const BorderRadius.vertical( top: Radius.circular(AppRadius.xxl), ), ), constraints: height != null ? BoxConstraints(maxHeight: height!) : null, child: SingleChildScrollView( child: Padding( padding: EdgeInsets.only( left: AppSpacing.lg, right: AppSpacing.lg, top: AppSpacing.md, bottom: bottomPadding + AppSpacing.lg, ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ // 拖動指示器 if (showDragHandle) _buildDragHandle(theme), // 標題行 if (title != null || titleWidget != null || showCloseButton) _buildHeader(context, theme), // 內容 child, ], ), ), ), ); } Widget _buildDragHandle(ThemeData theme) { return Center( child: Container( width: 40, height: 4, margin: const EdgeInsets.only(bottom: AppSpacing.md), decoration: BoxDecoration( color: theme.colorScheme.outline, borderRadius: BorderRadius.circular(2), ), ), ); } Widget _buildHeader(BuildContext context, ThemeData theme) { return Padding( padding: const EdgeInsets.only(bottom: AppSpacing.md), child: Row( children: [ // 標題 Expanded( child: titleWidget ?? Text( title!, style: AppTextStyles.headlineSmall(context).copyWith( fontWeight: FontWeight.w600, ), ), ), // 關閉按鈕 if (showCloseButton) GestureDetector( onTap: () { Navigator.of(context).pop(); onClose?.call(); }, child: Icon( LucideIcons.x, size: 20, color: theme.colorScheme.onSurfaceVariant, ), ), ], ), ); } } /// 操作列表 class _ActionList extends StatelessWidget { final List actions; const _ActionList({required this.actions}); @override Widget build(BuildContext context) { final theme = Theme.of(context); return Column( children: actions.asMap().entries.map((entry) { final index = entry.key; final action = entry.value; return Column( children: [ if (index > 0 && actions[index - 1].isDivider) Divider(color: theme.colorScheme.outline, height: 1), _ActionTile(action: action), ], ); }).toList(), ); } } /// 操作項 class _ActionTile extends StatelessWidget { final ModernSheetAction action; const _ActionTile({required this.action}); @override Widget build(BuildContext context) { final theme = Theme.of(context); return InkWell( onTap: () { Navigator.of(context).pop(action.returnValue); action.onPressed?.call(); }, child: Container( width: double.infinity, padding: const EdgeInsets.symmetric( horizontal: AppSpacing.md, vertical: AppSpacing.md, ), child: Row( children: [ if (action.icon != null) ...[ Icon( action.icon, size: 20, color: action.isDestructive ? theme.colorScheme.error : theme.colorScheme.onSurface, ), const SizedBox(width: AppSpacing.md), ], Expanded( child: Text( action.label, style: TextStyle( fontSize: 16, color: action.isDestructive ? theme.colorScheme.error : theme.colorScheme.onSurface, ), ), ), ], ), ), ); } } /// 底部抽屜操作配置 class ModernSheetAction { final String label; final IconData? icon; final dynamic returnValue; final bool isDestructive; final bool isDivider; final VoidCallback? onPressed; const ModernSheetAction({ required this.label, this.icon, this.returnValue, this.isDestructive = false, this.isDivider = false, this.onPressed, }); }