upload_zone.dart 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import 'dart:io';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_lucide/flutter_lucide.dart';
  4. import '../../theme/app_colors.dart';
  5. /// 文件上传区域组件
  6. class UploadZone extends StatefulWidget {
  7. final Function(List<File>)? onUpload;
  8. final Function(double)? onProgress;
  9. final List<String>? accept;
  10. final double? maxSize;
  11. const UploadZone({
  12. Key? key,
  13. this.onUpload,
  14. this.onProgress,
  15. this.accept,
  16. this.maxSize,
  17. }) : super(key: key);
  18. @override
  19. State<UploadZone> createState() => _UploadZoneState();
  20. }
  21. class _UploadZoneState extends State<UploadZone> {
  22. bool _isDragging = false;
  23. bool _isUploading = false;
  24. double _uploadProgress = 0.0;
  25. @override
  26. Widget build(BuildContext context) {
  27. return DragTarget<List<File>>(
  28. onWillAccept: (data) {
  29. setState(() => _isDragging = true);
  30. return true;
  31. },
  32. onLeave: (data) {
  33. setState(() => _isDragging = false);
  34. },
  35. onAccept: (files) {
  36. setState(() => _isDragging = false);
  37. _handleFiles(files);
  38. },
  39. builder: (context, candidateData, rejectedData) {
  40. return GestureDetector(
  41. onTap: _isUploading ? null : _pickFiles,
  42. child: Container(
  43. width: double.infinity,
  44. height: 300,
  45. decoration: BoxDecoration(
  46. color: _isDragging
  47. ? AppColors.primary.withOpacity(0.1)
  48. : Colors.white,
  49. border: Border.all(
  50. color: _isDragging ? AppColors.primary : AppColors.border,
  51. width: 2,
  52. style: BorderStyle.solid,
  53. ),
  54. borderRadius: BorderRadius.circular(12),
  55. ),
  56. child: Column(
  57. mainAxisAlignment: MainAxisAlignment.center,
  58. children: [
  59. Icon(
  60. LucideIcons.upload,
  61. size: 64,
  62. color:
  63. _isDragging ? AppColors.primary : AppColors.textSecondary,
  64. ),
  65. const SizedBox(height: 16),
  66. Text(
  67. _isDragging ? '释放以上传文件' : '拖拽文件到此处',
  68. style: TextStyle(
  69. fontSize: 18,
  70. fontWeight: FontWeight.w600,
  71. color:
  72. _isDragging ? AppColors.primary : AppColors.textPrimary,
  73. ),
  74. ),
  75. const SizedBox(height: 8),
  76. Text(
  77. '或点击选择文件',
  78. style: TextStyle(
  79. fontSize: 14,
  80. color: AppColors.textSecondary,
  81. ),
  82. ),
  83. const SizedBox(height: 16),
  84. Text(
  85. '支持格式:${widget.accept?.join(', ') ?? 'PDF, Word, 图片'}',
  86. style: TextStyle(
  87. fontSize: 12,
  88. color: AppColors.textSecondary,
  89. ),
  90. ),
  91. if (_isUploading) ...[
  92. const SizedBox(height: 24),
  93. SizedBox(
  94. width: 200,
  95. child: LinearProgressIndicator(
  96. value: _uploadProgress,
  97. backgroundColor: AppColors.borderLight,
  98. valueColor: const AlwaysStoppedAnimation<Color>(
  99. AppColors.primary,
  100. ),
  101. ),
  102. ),
  103. const SizedBox(height: 8),
  104. Text(
  105. '${(_uploadProgress * 100).toInt()}%',
  106. style: const TextStyle(
  107. fontSize: 12,
  108. color: AppColors.textSecondary,
  109. ),
  110. ),
  111. ],
  112. ],
  113. ),
  114. ),
  115. );
  116. },
  117. );
  118. }
  119. Future<void> _pickFiles() async {
  120. // 这里应该使用 file_picker,但为了演示使用静态数据
  121. // 实际项目中应该调用 FilePicker.platform.pickFiles()
  122. _simulateUpload();
  123. }
  124. Future<void> _handleFiles(List<File> files) async {
  125. _simulateUpload();
  126. }
  127. Future<void> _simulateUpload() async {
  128. setState(() {
  129. _isUploading = true;
  130. _uploadProgress = 0.0;
  131. });
  132. // 模拟上传进度
  133. for (int i = 0; i <= 100; i += 20) {
  134. await Future.delayed(const Duration(milliseconds: 60));
  135. setState(() {
  136. _uploadProgress = i / 100;
  137. });
  138. widget.onProgress?.call(_uploadProgress);
  139. }
  140. setState(() {
  141. _isUploading = false;
  142. _uploadProgress = 0.0;
  143. });
  144. // 模拟文件对象
  145. widget.onUpload?.call([]);
  146. }
  147. }