feat:增加统计模块年份选择功能
This commit is contained in:
@@ -7,6 +7,7 @@ import 'package:flisp_app/widgets/common.dart';
|
|||||||
import 'package:flisp_app/widgets/flisp_widget.dart';
|
import 'package:flisp_app/widgets/flisp_widget.dart';
|
||||||
import 'package:flisp_app/widgets/stats.dart';
|
import 'package:flisp_app/widgets/stats.dart';
|
||||||
import 'package:flisp_app/widgets/todo_widget.dart';
|
import 'package:flisp_app/widgets/todo_widget.dart';
|
||||||
|
import 'package:flisp_app/widgets/year_selector.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class StatsPage extends StatefulWidget {
|
class StatsPage extends StatefulWidget {
|
||||||
@@ -18,7 +19,8 @@ class StatsPage extends StatefulWidget {
|
|||||||
|
|
||||||
class StatsPageState extends State<StatsPage> {
|
class StatsPageState extends State<StatsPage> {
|
||||||
final double chartHeight = 400;
|
final double chartHeight = 400;
|
||||||
final double statsHeight = 160;
|
final double statsHeight = 120;
|
||||||
|
int currentYear = DateTime.now().year;
|
||||||
final FlispService flispService = FlispService();
|
final FlispService flispService = FlispService();
|
||||||
final TodoService todoService = TodoService();
|
final TodoService todoService = TodoService();
|
||||||
|
|
||||||
@@ -33,11 +35,15 @@ class StatsPageState extends State<StatsPage> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
allFlisps = flispService.getAllFlisps();
|
refreshStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
void refreshStats() {
|
||||||
|
allFlisps = flispService.getAllFlispsByYear(currentYear);
|
||||||
getFlispMonthlyStats(allFlisps);
|
getFlispMonthlyStats(allFlisps);
|
||||||
getFlispCategoryStats(allFlisps);
|
getFlispCategoryStats(allFlisps);
|
||||||
|
|
||||||
allTodos = todoService.getAllTodos();
|
allTodos = todoService.getAllTodosByYear(currentYear);
|
||||||
getTodoMonthlyStats(allTodos);
|
getTodoMonthlyStats(allTodos);
|
||||||
getTodoMonthlyCompleteStats(allTodos);
|
getTodoMonthlyCompleteStats(allTodos);
|
||||||
getTodoCategoryStats(allTodos);
|
getTodoCategoryStats(allTodos);
|
||||||
@@ -109,10 +115,15 @@ class StatsPageState extends State<StatsPage> {
|
|||||||
Widget _buildFlispStats(BuildContext context) {
|
Widget _buildFlispStats(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
buildChartTitle(context, '闪灵统计', Icons.analytics),
|
Row(
|
||||||
const SizedBox(height: 3),
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
buildChartDivider(context),
|
children: [
|
||||||
const SizedBox(height: 3),
|
Icon(Icons.analytics, color: Theme.of(context).colorScheme.primary),
|
||||||
|
const SizedBox(width: 3),
|
||||||
|
const Text('闪灵统计'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
Expanded(child: buildFlispStatsCard(allFlisps)),
|
Expanded(child: buildFlispStatsCard(allFlisps)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -159,10 +170,15 @@ class StatsPageState extends State<StatsPage> {
|
|||||||
Widget _buildTodoStats(BuildContext context) {
|
Widget _buildTodoStats(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
buildChartTitle(context, '待办统计', Icons.analytics),
|
Row(
|
||||||
const SizedBox(height: 3),
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
buildChartDivider(context),
|
children: [
|
||||||
const SizedBox(height: 3),
|
Icon(Icons.analytics, color: Theme.of(context).colorScheme.primary),
|
||||||
|
const SizedBox(width: 3),
|
||||||
|
const Text('待办统计'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
Expanded(child: buildTodoStatsCard(allTodos)),
|
Expanded(child: buildTodoStatsCard(allTodos)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -212,14 +228,25 @@ class StatsPageState extends State<StatsPage> {
|
|||||||
padding: EdgeInsets.all(10),
|
padding: EdgeInsets.all(10),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
YearSelector(
|
||||||
|
initialYear: DateTime.now().year,
|
||||||
|
minYear: 2000,
|
||||||
|
maxYear: 2100,
|
||||||
|
onYearChanged: (year) => {
|
||||||
|
setState(() {
|
||||||
|
currentYear = year;
|
||||||
|
refreshStats();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: statsHeight,
|
height: statsHeight,
|
||||||
child: BuildCard(child: _buildFlispStats(context)),
|
child: _buildFlispStats(context),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: statsHeight,
|
height: statsHeight,
|
||||||
child: BuildCard(child: _buildTodoStats(context)),
|
child: _buildTodoStats(context),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
|||||||
@@ -19,6 +19,13 @@ class FlispService {
|
|||||||
return box.values.toList();
|
return box.values.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Flisp> getAllFlispsByYear(int year) {
|
||||||
|
return box.values
|
||||||
|
.toList()
|
||||||
|
.where((flisp) => flisp.createTime.year == year)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
Future<bool> updateFlisp(Flisp flisp) async {
|
Future<bool> updateFlisp(Flisp flisp) async {
|
||||||
try {
|
try {
|
||||||
await flisp.save();
|
await flisp.save();
|
||||||
|
|||||||
@@ -19,6 +19,13 @@ class TodoService {
|
|||||||
return box.values.toList();
|
return box.values.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Todo> getAllTodosByYear(int year) {
|
||||||
|
return box.values
|
||||||
|
.toList()
|
||||||
|
.where((todo) => todo.createTime.year == year)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
Future<bool> updateTodo(Todo todo) async {
|
Future<bool> updateTodo(Todo todo) async {
|
||||||
try {
|
try {
|
||||||
await todo.save();
|
await todo.save();
|
||||||
|
|||||||
@@ -212,3 +212,21 @@ Widget buildToggleSwitch<T extends Enum>({
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
119
lib/widgets/year_selector.dart
Normal file
119
lib/widgets/year_selector.dart
Normal file
@@ -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<YearSelector> createState() => _YearSelectorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// SingleTickerProviderStateMixin 动画控制器
|
||||||
|
class _YearSelectorState extends State<YearSelector>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
late int _currentYear;
|
||||||
|
|
||||||
|
// 用于动画效果
|
||||||
|
late AnimationController _animationController;
|
||||||
|
late Animation<double> _scaleAnimation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_currentYear = widget.initialYear;
|
||||||
|
|
||||||
|
// 初始化动画控制器
|
||||||
|
_animationController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(milliseconds: 200),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 缩放动画
|
||||||
|
_scaleAnimation = Tween<double>(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(),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user