likes
comments
collection
share

flutter desktop drop file 桌面端如何拖拽文件到应用呢

作者站长头像
站长
· 阅读数 50

flutter desktop drop file 桌面端如何拖拽文件到应用呢

 拖动文件到应用中是桌面端程序必不可少的功能,我们可以通过拖拽文件的方式在应用中显示图片,上传文件,打开文件。flutter实现桌面应用时肯定也有拖拽文件的功能需求,本文就来做flutter在这方面的功能探索。这次将涉及到LinearGradient渐变色、cross_file插件、desktop_drop插件、ListView组件的使用,实现的效果图如下:

flutter desktop drop file 桌面端如何拖拽文件到应用呢

 可能是截图软件功能有所欠缺的问题,导致效果图中没有录制到鼠标拖拽的操作,所以上图看起来是直接就显示出了图片的样子,实际上是有拖拽操作的。

 在使用desktop_drop插件时自己遇到了一个问题,就是在调试模式时出现拖拽插件失效的情况,导致自己一度认为这个插件不能用,找了半天资料也没有找到解决的方法。后来阴差阳错的编译了release版本的应用,结果拖拽功能突然又可以用了,原理是啥我也不清楚,有大佬知道是为什么吗。。。

1 、渐变背景实现

 我们首先实现一个渐变颜色的背景色,虽然是一个小的实验程序,但也要美观好看一点对不对。渐变色我们要使用到LinearGradient,LinearGradient 是 2D 线性渐变,注意LinearGradient并不是小部件。利用LinearGradient我们可以不仅可以实现背景色渐变,同样可以实现文字,appbar等其他元素的渐变颜色效果。

flutter desktop drop file 桌面端如何拖拽文件到应用呢

 LinearGradient定义如下:

  const LinearGradient({
    this.begin = Alignment.centerLeft,
    this.end = Alignment.centerRight,
    required super.colors,
    super.stops,
    this.tileMode = TileMode.clamp,
    super.transform,
  });
colors: [
   Colors.pink.withAlpha(100),
   Colors.yellow.withAlpha(150),
   Colors.blue.withAlpha(150),
],

 colors中放入我们喜欢的颜色,可以放入七个颜色实现彩虹效果。begin,end 决定了渐变的起点和终点,我们可以设置这两个参数决定渐变的方向,比如从上到下,从左到右,从左上到右下斜着也是可以。也可以设置transform: GradientRotation(math.pi / 4)改变旋转的角度。

flutter desktop drop file 桌面端如何拖拽文件到应用呢

flutter desktop drop file 桌面端如何拖拽文件到应用呢  如果想自定义每个颜色显示的多少,可以通过停靠点列表来准确定义,设置stops: [0.1, 0.20, 0.50, 0.75]

flutter desktop drop file 桌面端如何拖拽文件到应用呢

 我们就简单实现一个左上开始的渐变背景吧,代码如下:

import 'dart:io';
import 'package:desktop_drop/desktop_drop.dart';
import 'package:flutter/material.dart';
import 'package:cross_file/cross_file.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        theme: ThemeData(
          useMaterial3: true,
        ),
        home: Scaffold(
          appBar: AppBar(
            title: const Text('desktop_drop example'),
          ),
          body: Stack(children: [
            Container(
              decoration: BoxDecoration(
                gradient: LinearGradient(//渐变颜色
                  begin: Alignment.topLeft,//渐变颜色开始位置
                  colors: [
                    Colors.pink.withAlpha(100),
                    Colors.yellow.withAlpha(150),
                    Colors.blue.withAlpha(150),
                  ],
                ),
              ),
            ),
            const Center(
              child: ExampleDragTarget(),//拖拽文件部分
            )
          ]),
        ));
  }
}

 效果图:

flutter desktop drop file 桌面端如何拖拽文件到应用呢

2 、cross_file插件

 cross_file是跨平台的文件抽象,XFile对象包含了文件的一些基本信息,包括文件路径、名字、长度和修改时间等信息。

  XFile(
    String super.path, {
    String? mimeType,
    String? name,
    int? length,
    Uint8List? bytes,
    DateTime? lastModified,
    @visibleForTesting CrossFileTestOverrides? overrides,
  })

 我们可以跨端使用cross_file,使用方法也很简单,如下所示:

import 'package:cross_file/cross_file.dart';

final file = XFile('assets/hello.txt');

print('File information:');
print('- Path: ${file.path}');
print('- Name: ${file.name}');
print('- MIME type: ${file.mimeType}');

final fileContent = await file.readAsString();
print('Content of the file: ${fileContent}');  // e.g. "Moto G (4)"

3 、desktop_drop插件

 desktop_drop是一个允许用户将文件拖动到flutter桌面应用程序的插件,他可以一次读取拖动单个或多个文件,获取的是以 XFile 对象存储的列表,XFile对象是刚刚提到的cross_file插件定义的。

 DropTarget的定义如下:

DropTarget({
    Key? key,
    required this.child,
    this.onDragEntered,拖动进入时
    this.onDragExited,拖动离开时
    this.onDragDone,拖动完成后
    this.onDragUpdated,拖动移动位置时
    this.enable = true,
  }) 

 onDragEntered监控拖动进入的动作,onDragExited监控拖动离开时的动作, onDragDone监控拖动完成后的动作,onDragUpdated监控拖动移动位置时的动作。

4 、拖拽部分实现

 通过判断_list.isEmpty是否为空来显示对应的界面,当_list为空时显示拖拽指示UI,UI由图片ICON和文本"drop files here"组成来指示拖拽。当_list不为空时显示拖进来的图片文件。

flutter desktop drop file 桌面端如何拖拽文件到应用呢

List<XFile> _list = [];
 bool _dragging = false;
 _list.isEmpty
        ? DropTarget(
        child: const Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(
            size: 40,
            Icons.image_outlined,
          ),
          Text(" drop files here"),
        ],
      ),
    )
    : const images();

 我们在onDragDone拖拽完成时将文件信息添加到_list列表中。在onDragEntered拖拽动作发生时改变_dragging的值来判断是否发生拖拽,进而根据_dragging改变背景色以显示拖拽动作。

DropTarget(
    onDragDone: (detail) {
      setState(() {
        _list.addAll(detail.files);
      });
    },
    onDragEntered: (detail) {
      setState(() {
        _dragging = true;
      });
    },
    onDragExited: (detail) {
      setState(() {
        _dragging = false;
      });
    },
    child: Container(
      alignment: Alignment.center,
      height: 200,
      width: 200,
      color: _dragging
          ? Colors.white.withAlpha(250)
          : Colors.white.withAlpha(50),
    ),
  )

 效果图:

flutter desktop drop file 桌面端如何拖拽文件到应用呢

5 、ListView显示图片

 接着就是拖拽动作完成后的图片显示部分实现了,我们使用ListView来生成图片列表,List.generate传入元素的长度和对应序列,然后再在Image.file组件中传入文件路径就可以以列表的方式显示多个图片了。_list[index].path就是我们拖入应用的图片路径。

class images extends StatelessWidget {
  const images({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: List.generate(_list.length, (index) {
        return SizedBox(
          width: 200,
          height: 400,
          child: Image.file(
            File(_list[index].path),
          ),
        );
      }),
    );
  }
}

6 、总结

 经过上诉多个组件和插件的组合完成了一个拖拽图片并显示的桌面端应用,功能很简单,但涉及到的知识对于我来说还是很多的,这些知识只是flutter的冰山一角,我还需要不停的学习呀。  完成效果图:

flutter desktop drop file 桌面端如何拖拽文件到应用呢

完整代码

import 'dart:io';

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        theme: ThemeData(
          useMaterial3: true,
        ),
        home: Scaffold(
          appBar: AppBar(
            title: const Text('desktop_drop example'),
          ),
          body: Stack(children: [
            Container(
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  begin: Alignment.topLeft,
                  // stops: [0.1, 0.20, 0.75],
                  colors: [
                    Colors.pink.withAlpha(100),
                    Colors.yellow.withAlpha(150),
                    Colors.blue.withAlpha(150),
                  ],
                ),
              ),
            ),
            const Center(
              child: ExampleDragTarget(),
            )
          ]),
        ));
  }
}

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

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

List<XFile> _list = [];

class _ExampleDragTargetState extends State<ExampleDragTarget> {
  bool _dragging = false;

  @override
  Widget build(BuildContext context) {
    return _list.isEmpty
        ? DropTarget(
            onDragDone: (detail) {
              setState(() {
                _list.addAll(detail.files);
              });
            },
            onDragEntered: (detail) {
              setState(() {
                _dragging = true;
              });
            },
            onDragExited: (detail) {
              setState(() {
                _dragging = false;
              });
            },
            child: Container(
              alignment: Alignment.center,
              height: 200,
              width: 200,
              color: _dragging
                  ? Colors.white.withAlpha(250)
                  : Colors.white.withAlpha(50),
              child: const Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(
                    size: 40,
                    Icons.image_outlined,
                  ),
                  Text(" drop files here"),
                ],
              ),
            ),
          )
        : const images();
  }
}

class images extends StatelessWidget {
  const images({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: List.generate(_list.length, (index) {
        return SizedBox(
          width: 200,
          height: 400,
          child: Image.file(
            File(_list[index].path),
          ),
        );
      }),
    );
  }
}
转载自:https://juejin.cn/post/7268135745951973431
评论
请登录