Flutter - 파일 처리 ② drag & drop으로 파일 입력 받기(desktop_drop)

3 분 소요


목차

1: Flutter - 파일 처리 ① 파일 들고 오기(file_picker)

  • file_picker 패키지를 사용하여 파일 들고 오기

2: Flutter - 파일 처리 ② drag & drop으로 파일 입력 받기(desktop_drop) <현재>

  • desktop_drop 패키지를 사용하여 웹에서 drag & drop으로 파일 입력 받기

3: Flutter - 파일 처리 ③: UTF-8·CP949 디코딩, CSV Converter

  • 읽어 온 UTF-8 또는 CP949로 인코딩된 파일을 디코딩하기


desktop_drop

desktop_drop 패키지로 다른 웹 페이지들처럼 파일을 drag & drop으로 편하게 업로드할 수 있다.

지원하는 플랫폼들이다.

Platform Availability
Windows
Linux
macOS
Android ✅(preview)
Web
  desktop_drop: ^0.3.2

dependencies에 추가

import 'package:desktop_drop/desktop_drop.dart';
import 'package:cross_file/cross_file.dart';

import 'dart:typed_data';

cross_file: XFile을 다룰 때 사용
typed_data: Uint8List를 다룰 때 사용


예제

2
드래그 드랍으로 파일 읽어 오는 예제를 만들어 보자
파일을 올리면 컨테이너 색을 파랗게 해서 티를 내주자

  bool _dragging = false;
  Color uploadingColor = Colors.blue[100]!;
  Color defaultColor = Colors.grey[400]!;


    DropTarget(
      onDragDone: (detail) async {
        debugPrint('onDragDone:');
        if( detail != null && detail.files.isNotEmpty ){
          String fileName = detail.files.first.name;
          Uint8List fileBytes = await detail.files.first.readAsBytes();
          debugPrint(fileName);
          setState(() {
            showFileName = "Now File Name: $fileName";
          });
          /*
          do jobs
          */
        }
      },
      onDragEntered: (detail) {
        setState(() {
          debugPrint('onDragEntered:');
          _dragging = true;
        });
      },
      onDragExited: (detail) {
        debugPrint('onDragExited:');
        setState(() {
          _dragging = false;
        });
      },
      child: makeDropZone(),
    )

childDropTarget으로 감싸 child에 드래그 앤 드랍으로 파일을 올릴 수 있다. 현재 코드에서 childmakeDropZone()은 저번 코드와 유사한 컨테이너다.

onDragDone: 유저가 드래그 & 드랍을 끝내 파일을 올렸을 때다. 외부에서 파일을 읽어 오는 것이므로, async이다.
읽어온 파일의 경우 XFile 타입이다.

.readAsBytes(): 파일 바이트를 Uint8List로 읽어올 수 있다.

onDragEntered: DropZone 안에 파일 드래그가 처음 들어 왔을 때다. 위 코드에서는 컨테이너의 색을 바꿔 주기 위해 bool 타입 변수 _draggingtrue로 바꾸고 setState해줬다.

onDragExited: DropZone 밖으로 파일 드래그가 나갔을 때다. 위 코드에서는 컨테이너의 색을 바꿔 주기 위해 bool 타입 변수 _draggingfalse로 바꾸고 setState해줬다.



전체 코드

전체 코드 보기
import 'package:flutter/material.dart';
import 'package:desktop_drop/desktop_drop.dart';
import 'package:cross_file/cross_file.dart';
import 'package:file_picker/file_picker.dart';
import 'dart:typed_data';

class FileDragAndDrop extends StatefulWidget {
  const FileDragAndDrop({Key? key}) : super(key: key);

  @override
  FileDragAndDropState createState() => FileDragAndDropState();
}

class FileDragAndDropState extends State<FileDragAndDrop> {
  final List<XFile> _list = [];

  String showFileName = "";

  bool _dragging = false;

  Color uploadingColor = Colors.blue[100]!;
  Color defaultColor = Colors.grey[400]!;

  Container makeDropZone(){
    Color color = _dragging ? uploadingColor : defaultColor;
    return Container(
      height: 200,
      width: 400,
      decoration: BoxDecoration(
        border: Border.all(width: 5, color: color,),
        borderRadius: const BorderRadius.all(Radius.circular(20)),
      ),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.end,
            children: [
              Text("Drop Your ", style: TextStyle(color: color, fontSize: 20,),),
              Text(".csv File", style: TextStyle(fontWeight: FontWeight.bold, color: color, fontSize: 20,),),
              Icon(Icons.insert_drive_file_rounded, color: color,),
              Text(" Here", style: TextStyle(color: color, fontSize: 20,),),
            ],
          ),
          InkWell(
            onTap: () async {
              FilePickerResult? result = await FilePicker.platform.pickFiles(
                type: FileType.custom,
                allowedExtensions: ['csv'],
              );
              if( result != null && result.files.isNotEmpty ){
                String fileName = result.files.first.name;
                Uint8List fileBytes = result.files.first.bytes!;
                debugPrint(fileName);
                setState(() {
                  showFileName = "Now File Name: $fileName";
                });
                /*
                do jobs
                 */
              }
            },
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.end,
              mainAxisSize: MainAxisSize.min,
              children: [
                Text("or ", style: TextStyle(color: color,),),
                Text("Find and Upload", style: TextStyle(fontWeight: FontWeight.bold, color: color, fontSize: 20,),),
                Icon(Icons.upload_rounded, color: color,),
              ],
            ),
          ),
          Text("(*.csv)", style: TextStyle(color: color,),),
          const SizedBox(height: 10,),
          Text(showFileName, style: TextStyle(color: defaultColor,),),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return DropTarget(
      onDragDone: (detail) async {
        debugPrint('onDragDone:');
        if( detail != null && detail.files.isNotEmpty ){
          String fileName = detail.files.first.name;
          Uint8List fileBytes = await detail.files.first.readAsBytes();
          debugPrint(fileName);
          setState(() {
            showFileName = "Now File Name: $fileName";
          });
          /*
          do jobs
          */
        }
      },
      onDragEntered: (detail) {
        setState(() {
          debugPrint('onDragEntered:');
          _dragging = true;
        });
      },
      onDragExited: (detail) {
        debugPrint('onDragExited:');
        setState(() {
          _dragging = false;
        });
      },
      child: makeDropZone(),
    );
  }
}


이전 포스트인 파일 처리 ① 파일 들고 오기(file_picker)에 이어진 코드다.

컨테이너에 파일을 바로 드래그해서 업로드하거나,
Find And Upload 버튼을 클릭해서 탐색기에서 파일을 찾아 올릴 수도 있다.



굿

댓글남기기