Files
monisuo/flutter_monisuo/lib/ui/components/glass_panel.dart
2026-04-07 01:05:05 +08:00

298 lines
7.1 KiB
Dart

import 'package:flutter/material.dart';
import '../../core/theme/app_spacing.dart';
import '../../core/theme/app_theme_extension.dart';
/// GlassPanel - 實心背景面板
///
/// Material Design 3 風格的實心背景容器
/// 用於卡片、彈窗、底部抽屜等需要清晰背景的容器
///
/// 示例:
/// ```dart
/// GlassPanel(
/// child: Text('內容'),
/// )
/// ```
class GlassPanel extends StatelessWidget {
/// 子組件
final Widget child;
/// 背景色,默認使用 surfaceContainer
final Color? backgroundColor;
/// 邊框色
final Color? borderColor;
/// 圓角,默認特大圓角
final BorderRadius? borderRadius;
/// 內邊距
final EdgeInsetsGeometry? padding;
/// 外邊距
final EdgeInsetsGeometry? margin;
/// 寬度
final double? width;
/// 高度
final double? height;
/// 是否顯示邊框
final bool showBorder;
const GlassPanel({
super.key,
required this.child,
this.backgroundColor,
this.borderColor,
this.borderRadius,
this.padding,
this.margin,
this.width,
this.height,
this.showBorder = true,
});
@override
Widget build(BuildContext context) {
final bgColor = backgroundColor ?? context.appColors.surfaceCard;
final brColor = borderColor ?? context.appColors.ghostBorder;
final br = borderRadius ?? BorderRadius.circular(AppRadius.xl);
Widget content = Container(
width: width,
height: height,
padding: padding ?? EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: bgColor,
borderRadius: br,
border: showBorder
? Border.all(
color: brColor,
width: 1,
)
: null,
),
child: child,
);
if (margin != null) {
content = Padding(
padding: margin!,
child: content,
);
}
return content;
}
}
/// GlassCard - 帶實心背景的卡片
///
/// 用於列表項、信息展示等場景
/// 預設了常用配置,簡化使用
class GlassCard extends StatelessWidget {
/// 子組件
final Widget child;
/// 點擊回調
final VoidCallback? onTap;
/// 長按回調
final VoidCallback? onLongPress;
/// 內邊距
final EdgeInsetsGeometry? padding;
/// 外邊距
final EdgeInsetsGeometry? margin;
/// 圓角
final BorderRadius? borderRadius;
/// 是否顯示霓虹光效
final bool showNeonGlow;
/// 霓虹光效顏色
final Color? neonGlowColor;
const GlassCard({
super.key,
required this.child,
this.onTap,
this.onLongPress,
this.padding,
this.margin,
this.borderRadius,
this.showNeonGlow = false,
this.neonGlowColor,
});
@override
Widget build(BuildContext context) {
final br = borderRadius ?? BorderRadius.circular(AppRadius.xl);
final glowColor = neonGlowColor ??
context.colors.primary.withValues(alpha: context.appColors.glowOpacity);
Widget card = GlassPanel(
padding: padding ?? EdgeInsets.all(AppSpacing.md),
margin: margin,
borderRadius: br,
child: child,
);
if (showNeonGlow) {
card = Container(
decoration: BoxDecoration(
borderRadius: br,
boxShadow: [
BoxShadow(
color: glowColor,
blurRadius: 15,
spreadRadius: 0,
),
],
),
child: card,
);
}
if (onTap != null || onLongPress != null) {
return GestureDetector(
onTap: onTap,
onLongPress: onLongPress,
child: card,
);
}
return card;
}
}
/// GlassBottomSheet - 實心背景底部抽屜
///
/// 用於彈出的底部面板
class GlassBottomSheet extends StatelessWidget {
/// 子組件
final Widget child;
/// 標題
final String? title;
/// 是否顯示關閉按鈕
final bool showCloseButton;
/// 內邊距
final EdgeInsetsGeometry? padding;
const GlassBottomSheet({
super.key,
required this.child,
this.title,
this.showCloseButton = true,
this.padding,
});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: context.appColors.surfaceCard,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(AppRadius.xxl),
),
border: Border.all(
color: context.appColors.ghostBorder,
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 頂部拖動條
Container(
margin: const EdgeInsets.only(top: 12, bottom: 8),
width: 40,
height: 4,
decoration: BoxDecoration(
color: context.colors.outlineVariant.withValues(alpha: 0.5),
borderRadius: BorderRadius.circular(2),
),
),
// 標題欄
if (title != null || showCloseButton)
Padding(
padding: EdgeInsets.fromLTRB(
AppSpacing.lg,
AppSpacing.sm,
AppSpacing.sm,
AppSpacing.md,
),
child: Row(
children: [
if (title != null)
Expanded(
child: Text(
title!,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: context.colors.onSurface,
),
),
),
if (showCloseButton)
GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
padding: EdgeInsets.all(AppSpacing.sm),
decoration: BoxDecoration(
color: context.colors.outlineVariant
.withOpacity(0.2),
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
size: 18,
color: context.colors.onSurfaceVariant,
),
),
),
],
),
),
// 內容
Padding(
padding: padding ??
EdgeInsets.fromLTRB(
AppSpacing.lg,
0,
AppSpacing.lg,
AppSpacing.xl,
),
child: child,
),
],
),
);
}
/// 顯示底部抽屜
static Future<T?> show<T>({
required BuildContext context,
required Widget Function(BuildContext) builder,
bool isScrollControlled = false,
bool isDismissible = true,
bool enableDrag = true,
}) {
return showModalBottomSheet<T>(
context: context,
isScrollControlled: isScrollControlled,
isDismissible: isDismissible,
enableDrag: enableDrag,
backgroundColor: Colors.transparent,
builder: builder,
);
}
}