create project

This commit is contained in:
2026-05-19 11:46:42 +08:00
commit dcdf1f60b1
60 changed files with 9309 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import '../theme/colors.dart';
class CategoryGrid extends StatelessWidget {
final List<Map<String, String>> categories;
final String selected;
final ValueChanged<String> onSelect;
const CategoryGrid({
super.key,
required this.categories,
required this.selected,
required this.onSelect,
});
@override
Widget build(BuildContext context) {
return GridView.count(
crossAxisCount: 4,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
mainAxisSpacing: 8,
crossAxisSpacing: 8,
childAspectRatio: 1.1,
children: categories.map((cat) {
final isSelected = selected == cat['name'];
return GestureDetector(
onTap: () => onSelect(cat['name']!),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 6),
decoration: BoxDecoration(
color: isSelected ? AppColors.brand.withOpacity(0.1) : Colors.white.withOpacity(0.65),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isSelected ? AppColors.brand : Colors.white.withOpacity(0.5),
width: isSelected ? 1.5 : 1,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(cat['emoji'] ?? '', style: const TextStyle(fontSize: 22)),
const SizedBox(height: 2),
Text(
cat['name'] ?? '',
style: TextStyle(
fontSize: 11,
fontWeight: isSelected ? FontWeight.w600 : FontWeight.w500,
color: isSelected ? AppColors.brand : const Color(0xFF8E8E93),
),
),
],
),
),
);
}).toList(),
);
}
}

View File

@@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import '../theme/colors.dart';
class CategoryIcon extends StatelessWidget {
final String emoji;
final String category;
final double size;
const CategoryIcon({
super.key,
required this.emoji,
required this.category,
this.size = 40,
});
Color _bgColor() {
switch (category) {
case '餐饮': return AppColors.brand.withOpacity(0.08);
case '交通': return AppColors.brandLight.withOpacity(0.08);
case '购物': return AppColors.brand.withOpacity(0.06);
case '医疗': return AppColors.expense.withOpacity(0.06);
case '工资': return AppColors.income.withOpacity(0.08);
default: return AppColors.textSecondary.withOpacity(0.08);
}
}
@override
Widget build(BuildContext context) {
return Container(
width: size,
height: size,
decoration: BoxDecoration(
color: _bgColor(),
shape: BoxShape.circle,
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 4, offset: Offset(0, 2))],
),
child: Center(child: Text(emoji, style: TextStyle(fontSize: size * 0.45))),
);
}
}

View File

@@ -0,0 +1,55 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import '../theme/colors.dart';
class GlassCard extends StatelessWidget {
final Widget child;
final EdgeInsetsGeometry? padding;
final EdgeInsetsGeometry? margin;
final double borderRadius;
final double? width;
final double? height;
final Color? borderColor;
final List<BoxShadow>? boxShadow;
const GlassCard({
super.key,
required this.child,
this.padding,
this.margin,
this.borderRadius = 16,
this.width,
this.height,
this.borderColor,
this.boxShadow,
});
@override
Widget build(BuildContext context) {
return Container(
width: width,
height: height,
margin: margin,
padding: padding ?? const EdgeInsets.all(14),
decoration: BoxDecoration(
color: AppColors.cardBg,
borderRadius: BorderRadius.circular(borderRadius),
border: Border.all(color: borderColor ?? Colors.white.withOpacity(0.8)),
boxShadow: boxShadow ?? [
BoxShadow(
color: AppColors.brand.withOpacity(0.06),
blurRadius: 32,
offset: const Offset(0, 8),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(borderRadius),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 30, sigmaY: 30),
child: child,
),
),
);
}
}

View File

@@ -0,0 +1,48 @@
import 'package:flutter/material.dart';
import '../theme/colors.dart';
class NumberPad extends StatelessWidget {
final void Function(String key) onKeyTap;
final String? Function()? onDelete;
const NumberPad({super.key, required this.onKeyTap, this.onDelete});
@override
Widget build(BuildContext context) {
final keys = ['1','2','3','4','5','6','7','8','9','.', '0', 'delete'];
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: GridView.count(
crossAxisCount: 4,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
mainAxisSpacing: 8,
crossAxisSpacing: 8,
childAspectRatio: 1.5,
children: keys.map((key) {
final isDelete = key == 'delete';
return GestureDetector(
onTap: () => onKeyTap(key),
child: Container(
height: 48,
decoration: BoxDecoration(
color: isDelete ? AppColors.brand.withOpacity(0.08) : Colors.white.withOpacity(0.7),
borderRadius: BorderRadius.circular(12),
),
child: Center(
child: Text(
isDelete ? '' : key,
style: TextStyle(
fontSize: isDelete ? 20 : 22,
fontWeight: isDelete ? FontWeight.w400 : FontWeight.w500,
color: isDelete ? AppColors.brand : const Color(0xFF1C1C1E),
),
),
),
),
);
}).toList(),
),
);
}
}

View File

@@ -0,0 +1,52 @@
import 'dart:ui';
import 'package:flutter/material.dart';
class StatCard extends StatelessWidget {
final String label;
final String value;
final Color color;
final double? fontSize;
const StatCard({
super.key,
required this.label,
required this.value,
required this.color,
this.fontSize,
});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 8),
decoration: BoxDecoration(
color: const Color(0xA6FFFFFF),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.white.withOpacity(0.8)),
boxShadow: const [
BoxShadow(color: Color(0x0DFF6B35), blurRadius: 32, offset: Offset(0, 8)),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 30, sigmaY: 30),
child: Column(
children: [
Text(label, style: const TextStyle(fontSize: 11, color: Color(0xFF8E8E93), fontWeight: FontWeight.w500)),
const SizedBox(height: 4),
Text(
value,
style: TextStyle(
fontSize: fontSize ?? (value.length > 10 ? 13 : 15),
fontWeight: FontWeight.w700,
color: color,
),
),
],
),
),
),
);
}
}