Dropzone OK et refactor page event_add
This commit is contained in:
260
em2rp/lib/views/widgets/inputs/dropzone_upload_widget.dart
Normal file
260
em2rp/lib/views/widgets/inputs/dropzone_upload_widget.dart
Normal file
@ -0,0 +1,260 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:firebase_storage/firebase_storage.dart';
|
||||
import 'package:flutter_dropzone/flutter_dropzone.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
class DropzoneUploadWidget extends StatefulWidget {
|
||||
final List<Map<String, String>> uploadedFiles;
|
||||
final ValueChanged<List<Map<String, String>>> onFilesChanged;
|
||||
final bool isLoading;
|
||||
final String? error;
|
||||
final String? success;
|
||||
final double width;
|
||||
final double height;
|
||||
|
||||
const DropzoneUploadWidget({
|
||||
super.key,
|
||||
required this.uploadedFiles,
|
||||
required this.onFilesChanged,
|
||||
this.isLoading = false,
|
||||
this.error,
|
||||
this.success,
|
||||
this.width = 400,
|
||||
this.height = 200,
|
||||
});
|
||||
|
||||
@override
|
||||
State<DropzoneUploadWidget> createState() => _DropzoneUploadWidgetState();
|
||||
}
|
||||
|
||||
class _DropzoneUploadWidgetState extends State<DropzoneUploadWidget> {
|
||||
DropzoneViewController? _dropzoneController;
|
||||
bool _isDropzoneHighlighted = false;
|
||||
bool _isLoading = false;
|
||||
String? _error;
|
||||
String? _success;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_isLoading = widget.isLoading;
|
||||
_error = widget.error;
|
||||
_success = widget.success;
|
||||
}
|
||||
|
||||
Future<void> _handleFiles(List<dynamic> files) async {
|
||||
setState(() => _isLoading = true);
|
||||
try {
|
||||
List<Map<String, String>> newFiles = List.from(widget.uploadedFiles);
|
||||
for (final file in files) {
|
||||
final name = await _dropzoneController!.getFilename(file);
|
||||
final bytes = await _dropzoneController!.getFileData(file);
|
||||
if (bytes != null) {
|
||||
final ref = FirebaseStorage.instance.ref().child(
|
||||
'events/temp/${DateTime.now().millisecondsSinceEpoch}_$name');
|
||||
final uploadTask = await ref.putData(bytes);
|
||||
final url = await uploadTask.ref.getDownloadURL();
|
||||
if (!newFiles.any((f) => f['name'] == name && f['url'] == url)) {
|
||||
newFiles.add({'name': name, 'url': url});
|
||||
}
|
||||
}
|
||||
}
|
||||
widget.onFilesChanged(newFiles);
|
||||
setState(() {
|
||||
_success = "Fichier(s) ajouté(s) !";
|
||||
_error = null;
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_error = 'Erreur lors de l\'upload : $e';
|
||||
_success = null;
|
||||
});
|
||||
} finally {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
_isDropzoneHighlighted = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: widget.width,
|
||||
height: widget.height,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: _isDropzoneHighlighted ? Colors.blue : Colors.grey,
|
||||
width: 2,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: _isDropzoneHighlighted ? Colors.blue.withOpacity(0.1) : null,
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
DropzoneView(
|
||||
onCreated: (controller) => _dropzoneController = controller,
|
||||
onDropFiles: (files) async {
|
||||
if (files == null) return;
|
||||
await _handleFiles(files);
|
||||
},
|
||||
onHover: () => setState(() => _isDropzoneHighlighted = true),
|
||||
onLeave: () => setState(() => _isDropzoneHighlighted = false),
|
||||
),
|
||||
Positioned.fill(
|
||||
child: IgnorePointer(
|
||||
child: widget.uploadedFiles.isEmpty
|
||||
? Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.cloud_upload,
|
||||
size: 48, color: Colors.grey),
|
||||
const SizedBox(height: 12),
|
||||
const Text(
|
||||
'Glissez-déposez des fichiers ici ou cliquez sur "Ajouter"',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.grey, fontSize: 16),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
if (widget.uploadedFiles.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: _buildFileListAndButton(),
|
||||
),
|
||||
if (widget.uploadedFiles.isEmpty)
|
||||
Positioned(
|
||||
bottom: 16,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 160,
|
||||
child: ElevatedButton.icon(
|
||||
icon: const Icon(Icons.attach_file, size: 18),
|
||||
label: const Text('Ajouter'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8, vertical: 8),
|
||||
minimumSize: const Size(80, 36),
|
||||
textStyle: const TextStyle(fontSize: 14),
|
||||
),
|
||||
onPressed: _isLoading
|
||||
? null
|
||||
: () async {
|
||||
final files =
|
||||
await _dropzoneController?.pickFiles();
|
||||
if (files != null) {
|
||||
await _handleFiles(files);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_isLoading)
|
||||
const Positioned.fill(
|
||||
child: ColoredBox(
|
||||
color: Color.fromARGB(80, 255, 255, 255),
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 32,
|
||||
height: 32,
|
||||
child: CircularProgressIndicator()),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_error != null)
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 8.0),
|
||||
child:
|
||||
Text(_error!, style: const TextStyle(color: Colors.red)),
|
||||
),
|
||||
),
|
||||
if (_success != null)
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 32.0),
|
||||
child: Text(_success!,
|
||||
style: const TextStyle(color: Colors.green)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFileListAndButton() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
...widget.uploadedFiles.map((file) {
|
||||
final fileName = file['name']!;
|
||||
final ext = p.extension(fileName).toLowerCase();
|
||||
IconData icon;
|
||||
if ([".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp"]
|
||||
.contains(ext)) {
|
||||
icon = Icons.image;
|
||||
} else if (ext == ".pdf") {
|
||||
icon = Icons.picture_as_pdf;
|
||||
} else if ([".txt", ".md", ".csv", ".json", ".xml"].contains(ext)) {
|
||||
icon = Icons.description;
|
||||
} else {
|
||||
icon = Icons.attach_file;
|
||||
}
|
||||
return ListTile(
|
||||
leading: Icon(icon, color: Colors.blueGrey),
|
||||
title: Text(fileName, overflow: TextOverflow.ellipsis),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: _isLoading
|
||||
? null
|
||||
: () {
|
||||
final newFiles =
|
||||
List<Map<String, String>>.from(widget.uploadedFiles)
|
||||
..remove(file);
|
||||
widget.onFilesChanged(newFiles);
|
||||
},
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
dense: true,
|
||||
);
|
||||
}).toList(),
|
||||
SizedBox(
|
||||
width: 160,
|
||||
child: ElevatedButton.icon(
|
||||
icon: const Icon(Icons.attach_file, size: 18),
|
||||
label: const Text('Ajouter'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||
minimumSize: const Size(80, 36),
|
||||
textStyle: const TextStyle(fontSize: 14),
|
||||
),
|
||||
onPressed: _isLoading
|
||||
? null
|
||||
: () async {
|
||||
final files = await _dropzoneController?.pickFiles();
|
||||
if (files != null) {
|
||||
await _handleFiles(files);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user