swing_account/lib/pages/record_page.dart

322 lines
9.0 KiB
Dart

import 'package:flutter/material.dart';
import '../models/record.dart';
import '../data/categories.dart';
import 'package:uuid/uuid.dart';
class RecordPage extends StatefulWidget {
final Record? record;
final Function(Record) onSave; // 添加保存回调
const RecordPage({
Key? key,
this.record,
required this.onSave,
}) : super(key: key);
@override
State<RecordPage> createState() => _RecordPageState();
}
class _RecordPageState extends State<RecordPage> with SingleTickerProviderStateMixin {
late TabController _tabController;
String? _selectedCategoryId;
String _note = '';
double _amount = 0.0;
DateTime _selectedDate = DateTime.now();
final _uuid = const Uuid();
@override
void initState() {
super.initState();
// 如果是编辑模式,初始化现有记录的数据
if (widget.record != null) {
_selectedCategoryId = widget.record!.categoryId;
_note = widget.record!.note ?? '';
_amount = widget.record!.amount;
_selectedDate = widget.record!.createTime;
}
_tabController = TabController(
length: 2,
vsync: this,
initialIndex: widget.record?.type == RecordType.income ? 1 : 0,
);
_tabController.addListener(_onTabChanged);
}
/// 标签切换监听
void _onTabChanged() {
setState(() {
_selectedCategoryId = null; // 切换类型时重置选中的分类
});
}
/// 获取当前记录类型
RecordType get _currentType =>
_tabController.index == 0 ? RecordType.expense : RecordType.income;
/// 获取当前分类列表
List<Category> get _currentCategories =>
_currentType == RecordType.expense ? expenseCategories : incomeCategories;
/// 保存记录
void _saveRecord() {
if (_selectedCategoryId == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('请选择分类')),
);
return;
}
if (_amount <= 0) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('请输入金额')),
);
return;
}
final record = Record(
id: widget.record?.id ?? _uuid.v4(),
type: _currentType,
categoryId: _selectedCategoryId!,
note: _note.isEmpty ? null : _note,
amount: _amount,
createTime: _selectedDate,
);
widget.onSave(record);
Navigator.of(context).pop();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: TabBar(
controller: _tabController,
tabs: const [
Tab(text: '支出'),
Tab(text: '收入'),
],
),
),
body: Column(
children: [
// 分类网格
Expanded(
child: GridView.builder(
padding: const EdgeInsets.all(16),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4,
mainAxisSpacing: 16,
crossAxisSpacing: 16,
),
itemCount: _currentCategories.length,
itemBuilder: (context, index) {
final category = _currentCategories[index];
final isSelected = category.id == _selectedCategoryId;
return _buildCategoryItem(category, isSelected);
},
),
),
// 底部编辑区域
_buildBottomEditor(),
],
),
);
}
/// 构建分类项
Widget _buildCategoryItem(Category category, bool isSelected) {
return InkWell(
onTap: () => setState(() => _selectedCategoryId = category.id),
child: Container(
decoration: BoxDecoration(
color: isSelected ? Theme.of(context).primaryColor.withOpacity(0.1) : null,
border: Border.all(
color: isSelected ? Theme.of(context).primaryColor : Colors.grey,
),
borderRadius: BorderRadius.circular(8),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
category.icon,
color: isSelected ? Theme.of(context).primaryColor : Colors.grey,
),
const SizedBox(height: 4),
Text(
category.name,
style: TextStyle(
color: isSelected ? Theme.of(context).primaryColor : Colors.grey,
),
),
],
),
),
);
}
/// 构建底部编辑区域
Widget _buildBottomEditor() {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, -2),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 备注和金额
Row(
children: [
Expanded(
child: TextField(
decoration: const InputDecoration(
hintText: '添加备注',
border: InputBorder.none,
),
onChanged: (value) => setState(() => _note = value),
),
),
Text(
'¥${_amount.toStringAsFixed(2)}',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
],
),
// 配置按钮
Row(
children: [
TextButton.icon(
onPressed: () {
// TODO: 实现账户选择
},
icon: const Icon(Icons.account_balance_wallet),
label: const Text('默认账户'),
),
TextButton.icon(
onPressed: () async {
final date = await showDatePicker(
context: context,
initialDate: _selectedDate,
firstDate: DateTime(2000),
lastDate: DateTime.now(),
);
if (date != null) {
setState(() => _selectedDate = date);
}
},
icon: const Icon(Icons.calendar_today),
label: Text(_selectedDate == DateTime.now()
? '今天'
: '${_selectedDate.month}${_selectedDate.day}'),
),
TextButton.icon(
onPressed: () {
// TODO: 实现图片选择
},
icon: const Icon(Icons.photo),
label: const Text('图片'),
),
],
),
// 数字键盘
_buildNumberKeyboard(),
],
),
);
}
/// 构建数字键盘
Widget _buildNumberKeyboard() {
return GridView.count(
shrinkWrap: true,
crossAxisCount: 4,
childAspectRatio: 2,
children: [
_buildKeyboardButton('7'),
_buildKeyboardButton('8'),
_buildKeyboardButton('9'),
_buildKeyboardButton('删除', isFunction: true),
_buildKeyboardButton('4'),
_buildKeyboardButton('5'),
_buildKeyboardButton('6'),
_buildKeyboardButton('+'),
_buildKeyboardButton('1'),
_buildKeyboardButton('2'),
_buildKeyboardButton('3'),
_buildKeyboardButton('-'),
_buildKeyboardButton('again'),
_buildKeyboardButton('0'),
_buildKeyboardButton('.'),
_buildKeyboardButton('保存', isFunction: true),
],
);
}
/// 构建键盘按钮
Widget _buildKeyboardButton(String text, {bool isFunction = false}) {
return TextButton(
onPressed: () => _onKeyboardButtonPressed(text),
child: Text(
text,
style: TextStyle(
fontSize: 20,
color: isFunction ? Theme.of(context).primaryColor : Colors.black,
),
),
);
}
/// 处理键盘按钮点击
void _onKeyboardButtonPressed(String value) {
switch (value) {
case '删除':
setState(() {
final amountStr = _amount.toStringAsFixed(2);
if (amountStr.length > 1) {
_amount = double.parse(amountStr.substring(0, amountStr.length - 1));
} else {
_amount = 0;
}
});
break;
case '保存':
_saveRecord();
break;
case 'again':
// TODO: 实现again功能
break;
case '+':
case '-':
// TODO: 实现加减功能
break;
case '.':
// TODO: 实现小数点功能
break;
default:
if (_amount == 0) {
setState(() => _amount = double.parse(value));
} else {
setState(() => _amount = double.parse('$_amount$value'));
}
}
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
}