167 lines
4.6 KiB
Dart
167 lines
4.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../../../common/painter/wave_painter.dart';
|
|
import '../../../common/theme/theme.dart';
|
|
|
|
class ParticleCard extends StatelessWidget {
|
|
/// Content yang ditampilkan di atas particle background
|
|
final Widget child;
|
|
|
|
/// Gradient background card. Default pakai primaryGradient
|
|
final List<Color>? gradientColors;
|
|
|
|
/// Arah gradient. Default topLeft → bottomRight
|
|
final AlignmentGeometry gradientBegin;
|
|
final AlignmentGeometry gradientEnd;
|
|
|
|
/// Border radius card. Default 16
|
|
final double borderRadius;
|
|
|
|
/// Padding konten. Default 16 semua sisi
|
|
final EdgeInsetsGeometry? padding;
|
|
|
|
/// Height card. Null = wrap content
|
|
final double? height;
|
|
|
|
/// Opacity particle & wave. Default 1.0
|
|
final double decorationOpacity;
|
|
|
|
const ParticleCard({
|
|
super.key,
|
|
required this.child,
|
|
this.gradientColors,
|
|
this.gradientBegin = Alignment.topLeft,
|
|
this.gradientEnd = Alignment.bottomRight,
|
|
this.borderRadius = 16,
|
|
this.padding,
|
|
this.height,
|
|
this.decorationOpacity = 1.0,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final colors = gradientColors ?? AppColor.primaryGradient;
|
|
|
|
return ClipRRect(
|
|
borderRadius: BorderRadius.circular(borderRadius),
|
|
child: Container(
|
|
height: height,
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(borderRadius),
|
|
gradient: LinearGradient(
|
|
colors: colors,
|
|
begin: gradientBegin,
|
|
end: gradientEnd,
|
|
),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: colors.first.withOpacity(0.35),
|
|
blurRadius: 16,
|
|
offset: const Offset(0, 6),
|
|
),
|
|
],
|
|
),
|
|
child: Stack(
|
|
children: [
|
|
// --- Decorative background ---
|
|
Opacity(
|
|
opacity: decorationOpacity,
|
|
child: Stack(
|
|
children: [
|
|
// Circles kanan atas
|
|
Positioned(
|
|
top: -24,
|
|
right: -24,
|
|
child: _circle(120, AppColor.white, 0.10),
|
|
),
|
|
Positioned(
|
|
top: 28,
|
|
right: 16,
|
|
child: _circle(60, AppColor.white, 0.06),
|
|
),
|
|
Positioned(
|
|
top: 70,
|
|
right: -10,
|
|
child: _circle(36, AppColor.white, 0.08),
|
|
),
|
|
|
|
// Circles kiri bawah
|
|
Positioned(
|
|
bottom: -20,
|
|
left: -20,
|
|
child: _circle(90, AppColor.white, 0.06),
|
|
),
|
|
Positioned(
|
|
bottom: 20,
|
|
left: 40,
|
|
child: _circle(40, AppColor.white, 0.04),
|
|
),
|
|
|
|
// Sparkle icons
|
|
..._sparkles(context),
|
|
|
|
// Wave pattern
|
|
Positioned.fill(
|
|
child: CustomPaint(
|
|
painter: WavePainter(
|
|
animation: 0.0,
|
|
color: AppColor.white.withOpacity(0.08),
|
|
),
|
|
),
|
|
),
|
|
|
|
// Radial gradient overlay
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
gradient: RadialGradient(
|
|
center: const Alignment(0.7, -0.5),
|
|
radius: 1.4,
|
|
colors: [
|
|
Colors.white.withOpacity(0.06),
|
|
Colors.transparent,
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
// --- Content ---
|
|
Padding(
|
|
padding: padding ?? const EdgeInsets.all(16),
|
|
child: child,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _circle(double size, Color color, double opacity) {
|
|
return Container(
|
|
width: size,
|
|
height: size,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: color.withOpacity(opacity),
|
|
),
|
|
);
|
|
}
|
|
|
|
List<Widget> _sparkles(BuildContext context) {
|
|
final width = MediaQuery.of(context).size.width;
|
|
return List.generate(5, (i) {
|
|
return Positioned(
|
|
left: (i * 70.0) % (width - 20),
|
|
top: 10 + (i * 18.0),
|
|
child: Icon(
|
|
Icons.auto_awesome,
|
|
size: 8 + (i % 3) * 3.0,
|
|
color: AppColor.white.withOpacity(0.18),
|
|
),
|
|
);
|
|
});
|
|
}
|
|
}
|