feat:增加对话框工具类
This commit is contained in:
@@ -12,6 +12,15 @@ List<T> convertList<T>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> convertStringList(dynamic data) {
|
||||||
|
if (data is List) {
|
||||||
|
return data.map((item) => item.toString()).toList();
|
||||||
|
}
|
||||||
|
throw FormatException(
|
||||||
|
'Expected a list of items for conversion, but got ${data.runtimeType}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
PageResult<T> convertPage<T>(
|
PageResult<T> convertPage<T>(
|
||||||
dynamic data,
|
dynamic data,
|
||||||
T Function(Map<String, dynamic>) fromJson,
|
T Function(Map<String, dynamic>) fromJson,
|
||||||
|
|||||||
50
lib/utils/file_utils.dart
Normal file
50
lib/utils/file_utils.dart
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'package:crypto/crypto.dart';
|
||||||
|
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||||
|
|
||||||
|
import 'log_utils.dart';
|
||||||
|
|
||||||
|
String getFileExtension(String fileName) {
|
||||||
|
if (fileName.contains('.')) {
|
||||||
|
return '.${fileName.split('.').last.toLowerCase()}';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> generateMD5HashName(String filePath) async {
|
||||||
|
final file = File(filePath);
|
||||||
|
final bytes = await file.readAsBytes();
|
||||||
|
final hash = md5.convert(bytes);
|
||||||
|
return hash.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 图片压缩方法
|
||||||
|
Future<File> compressImage(File file) async {
|
||||||
|
try {
|
||||||
|
// 获取压缩后的文件路径
|
||||||
|
final result = await FlutterImageCompress.compressAndGetFile(
|
||||||
|
file.absolute.path,
|
||||||
|
'${file.parent.path}/compressed_${DateTime.now().millisecondsSinceEpoch}.jpg',
|
||||||
|
minWidth: 800,
|
||||||
|
minHeight: 600,
|
||||||
|
quality: 70,
|
||||||
|
format: CompressFormat.jpeg,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
throw Exception('图片压缩失败');
|
||||||
|
}
|
||||||
|
|
||||||
|
return File(result.path);
|
||||||
|
} catch (e) {
|
||||||
|
logger.e('图片压缩失败,使用原文件: $e');
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否为图片文件
|
||||||
|
bool isImageFile(String fileName) {
|
||||||
|
final imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'];
|
||||||
|
final extension = fileName.toLowerCase().substring(fileName.lastIndexOf('.'));
|
||||||
|
return imageExtensions.contains(extension);
|
||||||
|
}
|
||||||
58
lib/utils/minio_utils.dart
Normal file
58
lib/utils/minio_utils.dart
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
|
import 'package:minio/io.dart';
|
||||||
|
import 'package:minio/minio.dart';
|
||||||
|
|
||||||
|
import 'file_utils.dart';
|
||||||
|
|
||||||
|
class MinIOHelper {
|
||||||
|
static final MinIOHelper _instance = MinIOHelper._internal();
|
||||||
|
|
||||||
|
factory MinIOHelper() => _instance;
|
||||||
|
|
||||||
|
final String ip = '14.103.235.151';
|
||||||
|
final String fileUrl = 'http://14.103.235.151:9100';
|
||||||
|
|
||||||
|
MinIOHelper._internal() {
|
||||||
|
_minio = Minio(
|
||||||
|
endPoint: ip,
|
||||||
|
port: 9100,
|
||||||
|
accessKey: "tHSFfcDW8qpCzKa2Xg6Y",
|
||||||
|
secretKey: "oq79EeYJ4jdczRp2IHUMCnbKtSw58NgDlG3sOkvX",
|
||||||
|
useSSL: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late Minio _minio;
|
||||||
|
|
||||||
|
Future<String> uploadFile({
|
||||||
|
required PlatformFile file,
|
||||||
|
required String bucketName,
|
||||||
|
Function(double)? onProgress,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
if (isImageFile(file.name)) {
|
||||||
|
// 压缩图片
|
||||||
|
final compressedFile = await compressImage(File(file.path!));
|
||||||
|
|
||||||
|
String hashName = await generateMD5HashName(compressedFile.path);
|
||||||
|
String fileName = '$hashName${getFileExtension(file.name)}';
|
||||||
|
|
||||||
|
await _minio.fPutObject(bucketName, fileName, compressedFile.path);
|
||||||
|
await compressedFile.delete();
|
||||||
|
|
||||||
|
return fileName;
|
||||||
|
} else {
|
||||||
|
String hashName = await generateMD5HashName(file.path!);
|
||||||
|
String fileName = '$hashName${getFileExtension(file.name)}';
|
||||||
|
|
||||||
|
await _minio.fPutObject(bucketName, fileName, file.path!);
|
||||||
|
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception('文件上传失败: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
lib/utils/toast_util.dart
Normal file
48
lib/utils/toast_util.dart
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
|
||||||
|
class ToastUtil {
|
||||||
|
static void success(String message) {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: message,
|
||||||
|
toastLength: Toast.LENGTH_SHORT,
|
||||||
|
gravity: ToastGravity.TOP,
|
||||||
|
backgroundColor: Colors.green,
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 16.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void error(String message) {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: message,
|
||||||
|
toastLength: Toast.LENGTH_SHORT,
|
||||||
|
gravity: ToastGravity.TOP,
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 16.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void warning(String message) {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: message,
|
||||||
|
toastLength: Toast.LENGTH_SHORT,
|
||||||
|
gravity: ToastGravity.TOP,
|
||||||
|
backgroundColor: Colors.orange,
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 16.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void info(String message) {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: message,
|
||||||
|
toastLength: Toast.LENGTH_SHORT,
|
||||||
|
gravity: ToastGravity.TOP,
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
textColor: Colors.white,
|
||||||
|
fontSize: 16.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,83 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:syncfusion_flutter_charts/charts.dart';
|
import 'package:syncfusion_flutter_charts/charts.dart';
|
||||||
|
|
||||||
import '../models/common_model.dart';
|
import '../models/common_model.dart';
|
||||||
|
import 'common_widget.dart';
|
||||||
|
|
||||||
|
class StatsCard extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
|
final String value;
|
||||||
|
final String unit;
|
||||||
|
final IconData icon;
|
||||||
|
|
||||||
|
const StatsCard(
|
||||||
|
{super.key,
|
||||||
|
required this.title,
|
||||||
|
required this.value,
|
||||||
|
required this.unit,
|
||||||
|
required this.icon});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
|
return CommonCard(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colors.primary.withAlpha(50),
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Icon(icon, size: 26, color: colors.primary),
|
||||||
|
),
|
||||||
|
SizedBox(width: 6),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 28,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: colors.primary,
|
||||||
|
height: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
unit,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.grey.shade600,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class LineChart extends StatelessWidget {
|
class LineChart extends StatelessWidget {
|
||||||
final String title;
|
final String title;
|
||||||
@@ -168,6 +245,7 @@ class BarChart extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DoubleBarChart extends StatelessWidget {
|
class DoubleBarChart extends StatelessWidget {
|
||||||
|
final String title;
|
||||||
final String xAxisName;
|
final String xAxisName;
|
||||||
final String yAxisName;
|
final String yAxisName;
|
||||||
final String unit;
|
final String unit;
|
||||||
@@ -178,6 +256,7 @@ class DoubleBarChart extends StatelessWidget {
|
|||||||
|
|
||||||
const DoubleBarChart(
|
const DoubleBarChart(
|
||||||
{super.key,
|
{super.key,
|
||||||
|
required this.title,
|
||||||
required this.xAxisName,
|
required this.xAxisName,
|
||||||
required this.yAxisName,
|
required this.yAxisName,
|
||||||
required this.unit,
|
required this.unit,
|
||||||
@@ -190,7 +269,13 @@ class DoubleBarChart extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
return SfCartesianChart(
|
return Column(children: [
|
||||||
|
_buildChartTitle(context, title, Icons.bar_chart),
|
||||||
|
const SizedBox(height: 3),
|
||||||
|
_buildChartDivider(context),
|
||||||
|
const SizedBox(height: 3),
|
||||||
|
Expanded(
|
||||||
|
child: SfCartesianChart(
|
||||||
// X轴配置(类别轴)
|
// X轴配置(类别轴)
|
||||||
primaryXAxis:
|
primaryXAxis:
|
||||||
CategoryAxis(majorGridLines: const MajorGridLines(width: 0)),
|
CategoryAxis(majorGridLines: const MajorGridLines(width: 0)),
|
||||||
@@ -264,7 +349,8 @@ class DoubleBarChart extends StatelessWidget {
|
|||||||
animationDuration: 2000,
|
animationDuration: 2000,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
))
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,3 +64,9 @@ Widget buildErrorInfo({
|
|||||||
Widget buildLoadingIndicator() {
|
Widget buildLoadingIndicator() {
|
||||||
return Center(child: CircularProgressIndicator());
|
return Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget buildEmptyData() {
|
||||||
|
return Center(
|
||||||
|
child: Text('暂无数据', style: TextStyle(fontSize: 16, color: Colors.grey)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
100
lib/widget/dialog_widget.dart
Normal file
100
lib/widget/dialog_widget.dart
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import 'package:awesome_dialog/awesome_dialog.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
void showAwesomeDialog({
|
||||||
|
required BuildContext context,
|
||||||
|
required Widget body,
|
||||||
|
required VoidCallback onOk,
|
||||||
|
required VoidCallback onCancel,
|
||||||
|
}) {
|
||||||
|
AwesomeDialog(
|
||||||
|
context: context,
|
||||||
|
dialogType: DialogType.noHeader,
|
||||||
|
animType: AnimType.scale,
|
||||||
|
body: body,
|
||||||
|
dialogBackgroundColor: Theme.of(context).colorScheme.surfaceContainer,
|
||||||
|
btnOkText: "确认",
|
||||||
|
btnCancelText: "取消",
|
||||||
|
btnOkColor: Colors.orange,
|
||||||
|
btnCancelColor: Colors.grey,
|
||||||
|
buttonsBorderRadius: BorderRadius.circular(10),
|
||||||
|
headerAnimationLoop: false,
|
||||||
|
dismissOnTouchOutside: false,
|
||||||
|
dismissOnBackKeyPress: true,
|
||||||
|
btnOk: ElevatedButton(
|
||||||
|
onPressed: onOk,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 6),
|
||||||
|
textStyle: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontFamily: 'CustomFont'),
|
||||||
|
),
|
||||||
|
child: Text("确认"),
|
||||||
|
),
|
||||||
|
btnCancelOnPress: onCancel,
|
||||||
|
).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示成功提示
|
||||||
|
void showSuccessTip(BuildContext context, String message) {
|
||||||
|
AwesomeDialog(
|
||||||
|
context: context,
|
||||||
|
dialogType: DialogType.success,
|
||||||
|
animType: AnimType.scale,
|
||||||
|
title: message,
|
||||||
|
btnOkText: "好的",
|
||||||
|
btnOkColor: Colors.green,
|
||||||
|
btnOkOnPress: () {},
|
||||||
|
autoHide: Duration(seconds: 2),
|
||||||
|
).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示失败提示
|
||||||
|
void showErrorTip(BuildContext context, String message) {
|
||||||
|
AwesomeDialog(
|
||||||
|
context: context,
|
||||||
|
dialogType: DialogType.error,
|
||||||
|
animType: AnimType.scale,
|
||||||
|
title: message,
|
||||||
|
btnOkText: "好的",
|
||||||
|
btnOkColor: Colors.red,
|
||||||
|
btnOkOnPress: () {},
|
||||||
|
autoHide: Duration(seconds: 2),
|
||||||
|
).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认对话框
|
||||||
|
Future<bool?> showConfirmDialog(BuildContext context, String text) async {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
|
return showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('确认'),
|
||||||
|
backgroundColor: colors.surfaceContainer,
|
||||||
|
content: Text(text),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
|
child: Text(
|
||||||
|
'取消',
|
||||||
|
style: TextStyle(color: colors.secondary.withAlpha(150)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(true),
|
||||||
|
child: Text('确认', style: TextStyle(color: colors.primary)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
52
lib/widget/loading_widget.dart
Normal file
52
lib/widget/loading_widget.dart
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class LoadingDialog {
|
||||||
|
static void show(BuildContext context, {String? message}) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return PopScope(
|
||||||
|
canPop: false,
|
||||||
|
child: AlertDialog(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
elevation: 0,
|
||||||
|
content: _buildLoadingContent(context, message),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hide(BuildContext context) {
|
||||||
|
Navigator.of(context, rootNavigator: true).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Widget _buildLoadingContent(BuildContext context, String? message) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.all(20),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
CircularProgressIndicator(
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(colors.primary),
|
||||||
|
strokeWidth: 3,
|
||||||
|
),
|
||||||
|
if (message != null) ...[
|
||||||
|
SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
message,
|
||||||
|
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,26 +3,21 @@ import 'package:flutter/material.dart';
|
|||||||
import 'common_widget.dart';
|
import 'common_widget.dart';
|
||||||
|
|
||||||
class YearSelector extends StatefulWidget {
|
class YearSelector extends StatefulWidget {
|
||||||
final int initialYear;
|
final int currentYear;
|
||||||
final int? minYear;
|
|
||||||
final int? maxYear;
|
|
||||||
final Function(int) onYearChanged;
|
final Function(int) onYearChanged;
|
||||||
|
|
||||||
const YearSelector({
|
const YearSelector(
|
||||||
super.key,
|
{super.key, required this.currentYear, required this.onYearChanged});
|
||||||
required this.initialYear,
|
|
||||||
required this.onYearChanged,
|
|
||||||
this.minYear,
|
|
||||||
this.maxYear,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<YearSelector> createState() => _YearSelectorState();
|
State<YearSelector> createState() => _YearSelectorState();
|
||||||
}
|
}
|
||||||
|
|
||||||
// SingleTickerProviderStateMixin 动画控制器
|
|
||||||
class _YearSelectorState extends State<YearSelector>
|
class _YearSelectorState extends State<YearSelector>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
|
final int minYear = 2000;
|
||||||
|
final int maxYear = 2100;
|
||||||
|
|
||||||
late int _currentYear;
|
late int _currentYear;
|
||||||
|
|
||||||
// 用于动画效果
|
// 用于动画效果
|
||||||
@@ -32,7 +27,8 @@ class _YearSelectorState extends State<YearSelector>
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_currentYear = widget.initialYear;
|
|
||||||
|
_currentYear = widget.currentYear;
|
||||||
|
|
||||||
// 初始化动画控制器
|
// 初始化动画控制器
|
||||||
_animationController = AnimationController(
|
_animationController = AnimationController(
|
||||||
@@ -46,6 +42,17 @@ class _YearSelectorState extends State<YearSelector>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(YearSelector oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
|
if (oldWidget.currentYear != widget.currentYear) {
|
||||||
|
setState(() {
|
||||||
|
_currentYear = widget.currentYear;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_animationController.dispose();
|
_animationController.dispose();
|
||||||
@@ -54,24 +61,26 @@ class _YearSelectorState extends State<YearSelector>
|
|||||||
|
|
||||||
/// 切换到上一年
|
/// 切换到上一年
|
||||||
void _previousYear() {
|
void _previousYear() {
|
||||||
if (widget.minYear == null || _currentYear > widget.minYear!) {
|
if (_currentYear > minYear) {
|
||||||
_animateYearChange(() {
|
_animateYearChange(() {
|
||||||
|
final newYear = _currentYear - 1;
|
||||||
setState(() {
|
setState(() {
|
||||||
_currentYear--;
|
_currentYear = newYear;
|
||||||
});
|
});
|
||||||
widget.onYearChanged(_currentYear);
|
widget.onYearChanged(newYear);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 切换到下一年
|
/// 切换到下一年
|
||||||
void _nextYear() {
|
void _nextYear() {
|
||||||
if (widget.maxYear == null || _currentYear < widget.maxYear!) {
|
if (_currentYear < maxYear) {
|
||||||
_animateYearChange(() {
|
_animateYearChange(() {
|
||||||
|
final newYear = _currentYear + 1;
|
||||||
setState(() {
|
setState(() {
|
||||||
_currentYear++;
|
_currentYear = newYear;
|
||||||
});
|
});
|
||||||
widget.onYearChanged(_currentYear);
|
widget.onYearChanged(newYear);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,7 +100,7 @@ class _YearSelectorState extends State<YearSelector>
|
|||||||
children: [
|
children: [
|
||||||
CircleIconButton(
|
CircleIconButton(
|
||||||
icon: Icons.chevron_left,
|
icon: Icons.chevron_left,
|
||||||
onPressed: () => _previousYear(),
|
onPressed: _previousYear,
|
||||||
),
|
),
|
||||||
AnimatedBuilder(
|
AnimatedBuilder(
|
||||||
animation: _scaleAnimation,
|
animation: _scaleAnimation,
|
||||||
@@ -109,8 +118,8 @@ class _YearSelectorState extends State<YearSelector>
|
|||||||
),
|
),
|
||||||
CircleIconButton(
|
CircleIconButton(
|
||||||
icon: Icons.chevron_right,
|
icon: Icons.chevron_right,
|
||||||
onPressed: () => _nextYear(),
|
onPressed: _nextYear,
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
154
pubspec.lock
154
pubspec.lock
@@ -33,6 +33,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.12.0"
|
version: "2.12.0"
|
||||||
|
awesome_dialog:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: awesome_dialog
|
||||||
|
sha256: "4c5821a0a637ceee022084e78c1b8237dd4b8bfca4dd24ac2484662a56707338"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.3.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -41,6 +49,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
|
buffer:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: buffer
|
||||||
|
sha256: "389da2ec2c16283c8787e0adaede82b1842102f8c8aae2f49003a766c5c6b3d1"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.3"
|
||||||
build:
|
build:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -153,8 +169,16 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.2"
|
version: "3.1.2"
|
||||||
crypto:
|
cross_file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cross_file
|
||||||
|
sha256: "942a4791cd385a68ccb3b32c71c427aba508a1bb949b86dff2adbe4049f16239"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.5"
|
||||||
|
crypto:
|
||||||
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||||
@@ -177,6 +201,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "3.1.1"
|
||||||
|
dbus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dbus
|
||||||
|
sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.11"
|
||||||
dio:
|
dio:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -217,6 +249,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.1"
|
version: "7.0.1"
|
||||||
|
file_picker:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: file_picker
|
||||||
|
sha256: "7872545770c277236fd32b022767576c562ba28366204ff1a5628853cf8f2200"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "10.3.7"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -230,6 +270,54 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_image_compress:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_image_compress
|
||||||
|
sha256: "51d23be39efc2185e72e290042a0da41aed70b14ef97db362a6b5368d0523b27"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
|
flutter_image_compress_common:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_image_compress_common
|
||||||
|
sha256: c5c5d50c15e97dd7dc72ff96bd7077b9f791932f2076c5c5b6c43f2c88607bfb
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.6"
|
||||||
|
flutter_image_compress_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_image_compress_macos
|
||||||
|
sha256: "20019719b71b743aba0ef874ed29c50747461e5e8438980dfa5c2031898f7337"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.3"
|
||||||
|
flutter_image_compress_ohos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_image_compress_ohos
|
||||||
|
sha256: e76b92bbc830ee08f5b05962fc78a532011fcd2041f620b5400a593e96da3f51
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.3"
|
||||||
|
flutter_image_compress_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_image_compress_platform_interface
|
||||||
|
sha256: "579cb3947fd4309103afe6442a01ca01e1e6f93dc53bb4cbd090e8ce34a41889"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.5"
|
||||||
|
flutter_image_compress_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_image_compress_web
|
||||||
|
sha256: b9b141ac7c686a2ce7bb9a98176321e1182c9074650e47bb140741a44b6f5a96
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.5"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@@ -238,6 +326,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
version: "5.0.0"
|
||||||
|
flutter_plugin_android_lifecycle:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_plugin_android_lifecycle
|
||||||
|
sha256: c2fe1001710127dfa7da89977a08d591398370d099aacdaa6d44da7eb14b8476
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.31"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -248,6 +344,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
fluttertoast:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: fluttertoast
|
||||||
|
sha256: "90778fe0497fe3a09166e8cf2e0867310ff434b794526589e77ec03cf08ba8e8"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "8.2.14"
|
||||||
frontend_server_client:
|
frontend_server_client:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -416,6 +520,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.0.0"
|
||||||
|
minio:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: minio
|
||||||
|
sha256: ee2ce47766e46c7d164f960f2f5ed6a9a82844d877f6b82574f6876ec50c56d1
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.5.8"
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -464,6 +576,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.0"
|
||||||
|
petitparser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: petitparser
|
||||||
|
sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.0"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -512,6 +632,22 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.0"
|
version: "1.5.0"
|
||||||
|
rive:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: rive
|
||||||
|
sha256: fc0abf65d03d1c9afaeb35be9e71c7cf04d2d1f76e94e69d2af1b3ba413cddf9
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.14.0-dev.14"
|
||||||
|
rive_native:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: rive_native
|
||||||
|
sha256: e9c7d36f19eb6d32f563825d4e9d5032b19a36d3ca3341641431035bca022d19
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.17"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -741,6 +877,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.3"
|
||||||
|
win32:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32
|
||||||
|
sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "5.13.0"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -749,6 +893,14 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
|
xml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xml
|
||||||
|
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "6.5.0"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -18,6 +18,12 @@ dependencies:
|
|||||||
dio: ^5.7.0
|
dio: ^5.7.0
|
||||||
logger: ^2.6.0
|
logger: ^2.6.0
|
||||||
json_annotation: ^4.9.0
|
json_annotation: ^4.9.0
|
||||||
|
minio: ^3.5.8
|
||||||
|
crypto: ^3.0.7
|
||||||
|
file_picker: ^10.3.3
|
||||||
|
fluttertoast: ^8.2.2
|
||||||
|
flutter_image_compress: ^2.4.0
|
||||||
|
awesome_dialog: ^3.3.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user