259 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			259 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
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);
 | 
						|
        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,
 | 
						|
          );
 | 
						|
        }),
 | 
						|
        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);
 | 
						|
                    }
 | 
						|
                  },
 | 
						|
          ),
 | 
						|
        ),
 | 
						|
      ],
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 |