diff --git a/lib/pages/stats_page.dart b/lib/pages/stats_page.dart index a129c5b..617eecc 100644 --- a/lib/pages/stats_page.dart +++ b/lib/pages/stats_page.dart @@ -7,6 +7,7 @@ import 'package:flisp_app/widgets/common.dart'; import 'package:flisp_app/widgets/flisp_widget.dart'; import 'package:flisp_app/widgets/stats.dart'; import 'package:flisp_app/widgets/todo_widget.dart'; +import 'package:flisp_app/widgets/year_selector.dart'; import 'package:flutter/material.dart'; class StatsPage extends StatefulWidget { @@ -18,7 +19,8 @@ class StatsPage extends StatefulWidget { class StatsPageState extends State { final double chartHeight = 400; - final double statsHeight = 160; + final double statsHeight = 120; + int currentYear = DateTime.now().year; final FlispService flispService = FlispService(); final TodoService todoService = TodoService(); @@ -33,11 +35,15 @@ class StatsPageState extends State { @override void initState() { super.initState(); - allFlisps = flispService.getAllFlisps(); + refreshStats(); + } + + void refreshStats() { + allFlisps = flispService.getAllFlispsByYear(currentYear); getFlispMonthlyStats(allFlisps); getFlispCategoryStats(allFlisps); - allTodos = todoService.getAllTodos(); + allTodos = todoService.getAllTodosByYear(currentYear); getTodoMonthlyStats(allTodos); getTodoMonthlyCompleteStats(allTodos); getTodoCategoryStats(allTodos); @@ -109,10 +115,15 @@ class StatsPageState extends State { Widget _buildFlispStats(BuildContext context) { return Column( children: [ - buildChartTitle(context, '闪灵统计', Icons.analytics), - const SizedBox(height: 3), - buildChartDivider(context), - const SizedBox(height: 3), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Icon(Icons.analytics, color: Theme.of(context).colorScheme.primary), + const SizedBox(width: 3), + const Text('闪灵统计'), + ], + ), + const SizedBox(height: 5), Expanded(child: buildFlispStatsCard(allFlisps)), ], ); @@ -159,10 +170,15 @@ class StatsPageState extends State { Widget _buildTodoStats(BuildContext context) { return Column( children: [ - buildChartTitle(context, '待办统计', Icons.analytics), - const SizedBox(height: 3), - buildChartDivider(context), - const SizedBox(height: 3), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Icon(Icons.analytics, color: Theme.of(context).colorScheme.primary), + const SizedBox(width: 3), + const Text('待办统计'), + ], + ), + const SizedBox(height: 5), Expanded(child: buildTodoStatsCard(allTodos)), ], ); @@ -212,14 +228,25 @@ class StatsPageState extends State { padding: EdgeInsets.all(10), child: Column( children: [ + YearSelector( + initialYear: DateTime.now().year, + minYear: 2000, + maxYear: 2100, + onYearChanged: (year) => { + setState(() { + currentYear = year; + refreshStats(); + }) + }, + ), SizedBox( height: statsHeight, - child: BuildCard(child: _buildFlispStats(context)), + child: _buildFlispStats(context), ), SizedBox(height: 10), SizedBox( height: statsHeight, - child: BuildCard(child: _buildTodoStats(context)), + child: _buildTodoStats(context), ), SizedBox(height: 10), SizedBox( diff --git a/lib/service/flisp_service.dart b/lib/service/flisp_service.dart index 11f9688..db0bf55 100644 --- a/lib/service/flisp_service.dart +++ b/lib/service/flisp_service.dart @@ -19,6 +19,13 @@ class FlispService { return box.values.toList(); } + List getAllFlispsByYear(int year) { + return box.values + .toList() + .where((flisp) => flisp.createTime.year == year) + .toList(); + } + Future updateFlisp(Flisp flisp) async { try { await flisp.save(); diff --git a/lib/service/todo_service.dart b/lib/service/todo_service.dart index 7f09476..c9748be 100644 --- a/lib/service/todo_service.dart +++ b/lib/service/todo_service.dart @@ -19,6 +19,13 @@ class TodoService { return box.values.toList(); } + List getAllTodosByYear(int year) { + return box.values + .toList() + .where((todo) => todo.createTime.year == year) + .toList(); + } + Future updateTodo(Todo todo) async { try { await todo.save(); diff --git a/lib/widgets/common.dart b/lib/widgets/common.dart index f608ef4..59eebc6 100644 --- a/lib/widgets/common.dart +++ b/lib/widgets/common.dart @@ -212,3 +212,21 @@ Widget buildToggleSwitch({ }, ); } + +Widget circleIconButton({ + required IconData icon, + required VoidCallback onPressed, + required BuildContext context, +}) { + return ElevatedButton( + onPressed: onPressed, + style: ElevatedButton.styleFrom( + shape: CircleBorder(), + elevation: 0, + backgroundColor: Theme.of(context).colorScheme.primary, + padding: EdgeInsets.zero, + minimumSize: const Size(0, 0), + ), + child: Icon(icon, color: Colors.white), + ); +} diff --git a/lib/widgets/year_selector.dart b/lib/widgets/year_selector.dart new file mode 100644 index 0000000..ec79d3b --- /dev/null +++ b/lib/widgets/year_selector.dart @@ -0,0 +1,119 @@ +import 'package:flisp_app/widgets/common.dart'; +import 'package:flutter/material.dart'; + +class YearSelector extends StatefulWidget { + final int initialYear; + final int? minYear; + final int? maxYear; + final Function(int) onYearChanged; + + const YearSelector({ + super.key, + required this.initialYear, + required this.onYearChanged, + this.minYear, + this.maxYear, + }); + + @override + State createState() => _YearSelectorState(); +} + +// SingleTickerProviderStateMixin 动画控制器 +class _YearSelectorState extends State + with SingleTickerProviderStateMixin { + late int _currentYear; + + // 用于动画效果 + late AnimationController _animationController; + late Animation _scaleAnimation; + + @override + void initState() { + super.initState(); + _currentYear = widget.initialYear; + + // 初始化动画控制器 + _animationController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 200), + ); + + // 缩放动画 + _scaleAnimation = Tween(begin: 1.0, end: 1.1).animate( + CurvedAnimation(parent: _animationController, curve: Curves.easeInOut), + ); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + /// 切换到上一年 + void _previousYear() { + if (widget.minYear == null || _currentYear > widget.minYear!) { + _animateYearChange(() { + setState(() { + _currentYear--; + }); + widget.onYearChanged(_currentYear); + }); + } + } + + /// 切换到下一年 + void _nextYear() { + if (widget.maxYear == null || _currentYear < widget.maxYear!) { + _animateYearChange(() { + setState(() { + _currentYear++; + }); + widget.onYearChanged(_currentYear); + }); + } + } + + /// 年份变化时的动画效果 + void _animateYearChange(VoidCallback onComplete) { + _animationController.forward().then((_) { + onComplete(); + _animationController.reverse(); + }); + } + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + circleIconButton( + context: context, + icon: Icons.chevron_left, + onPressed: () => _previousYear(), + ), + // 年份显示 + AnimatedBuilder( + animation: _scaleAnimation, + builder: (context, child) { + return Transform.scale(scale: _scaleAnimation.value, child: child); + }, + child: Text( + '$_currentYear', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Theme.of(context).primaryColor, + ), + ), + ), + circleIconButton( + context: context, + icon: Icons.chevron_right, + onPressed: () => _nextYear(), + ) + ], + ); + } +}