liuyuqi-dellpc 1 year ago
parent
commit
2898ca0335

+ 7 - 0
android/app/src/main/java/me.yoqi.flutter.file_manager/MainActivity.java

@@ -0,0 +1,7 @@
+package me.yoqi.flutter.file_manager;
+
+import io.flutter.embedding.android.FlutterActivity;
+
+class MainActivity extends FlutterActivity {
+
+}

+ 0 - 6
android/app/src/main/java/me.yoqi.flutter.file_manager/MainActivity.kt

@@ -1,6 +0,0 @@
-package me.yoqi.flutter.file_manager
-
-import io.flutter.embedding.android.FlutterActivity
-
-class MainActivity: FlutterActivity() {
-}

+ 87 - 0
lib/Item/Common.dart

@@ -0,0 +1,87 @@
+//单例模式设计
+//单例的类Common
+class Common{
+  //factory关键字
+  //工厂模式: 单例公开访问点
+  factory Common()=>_getInstance();
+  //静态未初始化对象--单例对象
+  static Common _instance;
+  static Common _getInstance(){
+    if(_instance==null){
+      _instance=Common._internal();
+    }
+    return _instance;
+  }
+
+  Common._internal();//初始化函数
+  //SD卡根路径
+  String sDCardDir;
+  //存放收藏文件夹目录的文件
+  String favoriteDir;
+  List<String> favoriteFileList=new List<String>();
+  String favoriteAll='';
+
+  String getFileSize(int fileSize){
+    String str ='';
+
+    if(fileSize<1024){
+      str='${fileSize.toStringAsFixed(2)}B';//toStringAsFixed(2)  保留两位小数 四舍五入
+    }else if(1024<=fileSize&&fileSize<1048576){
+      str='${(fileSize/1024).toStringAsFixed(2)}KB';
+    }else if(1048576<=fileSize&&fileSize<1073741824){
+      str = '${(fileSize / 1024 / 1024).toStringAsFixed(2)}MB';
+    }
+    return str;
+  }
+
+  String selectIcon(String ext) {
+    String iconImg = 'assets/images/unknown.png';
+
+    switch (ext) {
+      case '.ppt':
+      case '.pptx':
+        iconImg = 'assets/images/ppt.png';
+        break;
+      case '.doc':
+      case '.docx':
+        iconImg = 'assets/images/word.png';
+        break;
+      case '.xls':
+      case '.xlsx':
+        iconImg = 'assets/images/excel.png';
+        break;
+      case '.pdf':
+        iconImg = 'assets/images/pdf.png';
+        break;
+      case '.jpg':
+      case '.jpeg':
+      case '.png':
+        iconImg = 'assets/images/image.png';
+        break;
+      case '.txt':
+        iconImg = 'assets/images/txt.png';
+        break;
+      case '.flac':
+      case '.mp3':
+        iconImg = 'assets/images/audio.png';
+        break;
+      case '.mp4':
+        iconImg = 'assets/images/video.png';
+        break;
+      case '.rar':
+      case '.zip':
+        iconImg = 'assets/images/zip.png';
+        break;
+      case '.psd':
+        iconImg = 'assets/images/psd.png';
+        break;
+      case '.apk':
+        iconImg='assets/images/apk.png';
+        break;
+      default:
+        iconImg = 'assets/images/file.png';
+        break;
+    }
+    return iconImg;
+  }
+}

+ 9 - 0
lib/Item/Mode.dart

@@ -0,0 +1,9 @@
+import 'dart:io';
+
+class Mode {
+  bool cutMode; //是否点了剪切文件
+  bool copyMode; //是否点了复制文件
+  FileSystemEntity copyTempFile;
+  Directory parentDir; //父目录
+  Mode(this.cutMode, this.copyMode, this.copyTempFile, this.parentDir);
+}

+ 8 - 0
lib/Item/Preview/FilePre.dart

@@ -0,0 +1,8 @@
+import 'package:flutter/material.dart';
+
+class FilePre extends StatelessWidget{
+  @override
+  Widget build(BuildContext context) {
+    throw UnimplementedError();
+  }
+}

+ 9 - 0
lib/Item/Preview/Impl/FilePreDecrator.dart

@@ -0,0 +1,9 @@
+import 'package:neofilemanager/Item/Preview/FilePre.dart';
+
+import 'ProxyFilePre.dart';
+
+class FilePreDecrator extends FilePre {
+  ProxyFilePre filePre;
+
+  FilePreDecrator(this.filePre);
+}

+ 47 - 0
lib/Item/Preview/Impl/ProxyFilePre.dart

@@ -0,0 +1,47 @@
+import 'dart:io';
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:neofilemanager/Item/Common.dart';
+import 'package:neofilemanager/Item/Preview/FilePre.dart';
+import 'package:neofilemanager/Item/Preview/Impl/RealFilePre.dart';
+
+class ProxyFilePre extends FilePre {
+  Common common;
+  FileSystemEntity file;
+  String extension;
+  double iconHeight = 30.0;
+  double iconWidth = 30.0;
+  double fileHeight = 50.0;
+  double fileWidth = 70.0;
+  RealFilePre realFilePre;
+
+  ProxyFilePre(
+      this.common, this.file, this.extension, this.fileHeight, this.fileWidth);
+
+  Widget build(BuildContext context) {
+    // 对加载情况进行判断,如果加载完成才展示图片,否则展示一个小的代理图片
+    return FutureBuilder(
+      future: returnRealFIlePre(),
+      builder: (context, snapshot) {
+        if (snapshot.connectionState == ConnectionState.done) {
+          print('::::done');
+          return realFilePre;
+        } else {
+          print('::::else');
+          return Image.asset(
+            common.selectIcon(extension),
+            height: iconHeight,
+            width: iconWidth,
+          );
+        }
+      },
+    );
+  }
+
+  // 对真实图片的异步加载
+  Future<Widget> returnRealFIlePre() async {
+    return realFilePre =
+        new RealFilePre(common, file, extension, fileHeight, fileWidth);
+  }
+}

+ 34 - 0
lib/Item/Preview/Impl/RealFilePre.dart

@@ -0,0 +1,34 @@
+import 'dart:io';
+
+import 'package:flutter/cupertino.dart';
+import 'package:neofilemanager/Item/Common.dart';
+import 'package:neofilemanager/Item/Preview/FilePre.dart';
+
+class RealFilePre extends FilePre {
+  Common common;
+  FileSystemEntity file;
+  String extension;
+  double iconHeight = 30.0;
+  double iconWidth = 30.0;
+  double fileHeight = 50.0;
+  double fileWidth = 70.0;
+
+  RealFilePre(
+      this.common, this.file, this.extension, this.fileHeight, this.fileWidth);
+
+  Widget build(BuildContext context) {
+    print('开始加载'+file.path);
+    // 如果是图片类型的文件返回图片,否则返回文件图标
+    if (extension == '.png' || extension == '.jpg' || extension == '.jpeg' || extension == '.gif') {
+       Image tempImage=Image.file(file, height: fileHeight, width: fileWidth, fit: BoxFit.cover);
+    print('加载完毕'+file.path);
+      return ClipRRect(
+        borderRadius: BorderRadius.circular(10),
+        child: tempImage,
+      );
+    } else {
+      return Image.asset( common.selectIcon(extension), height: iconHeight, width: iconWidth,
+      );
+    }
+  }
+}

+ 16 - 0
lib/Item/Preview/Impl/TxtFilePre.dart

@@ -0,0 +1,16 @@
+
+import 'dart:io';
+
+import 'package:neofilemanager/Item/Preview/Impl/FilePreDecrator.dart';
+import 'package:neofilemanager/Item/Preview/Impl/ProxyFilePre.dart';
+
+class TxtFilePre extends FilePreDecrator{
+  TxtFilePre(ProxyFilePre filePre) : super(filePre);
+  // 读取txt内容
+  String getContent(){
+    File file=File(filePre.file.path);
+    var content=file.readAsStringSync();
+    print(content);
+    return content;
+  }
+}

+ 63 - 0
lib/Item/widgetItem.dart

@@ -0,0 +1,63 @@
+import 'dart:io';
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+class WidgetItem {
+  factory WidgetItem() => _getInstance();
+
+  static WidgetItem _instance;
+
+  static WidgetItem _getInstance() {
+    if (_instance == null) {
+      _instance = WidgetItem._internal();
+    }
+    return _instance;
+  }
+
+  WidgetItem._internal();
+
+  Widget returnFileOperateButton(BuildContext context, int cardColor,
+      Function(FileSystemEntity file,int type) fun,String titleText,FileSystemEntity file,int type) {
+    Color color;
+    switch(titleText){
+      case '删除':
+        color=Colors.blueGrey;
+        break;
+      case '重命名':
+        color=Colors.blue;
+        break;
+      case '复制':
+        color=Colors.blueAccent;
+        break;
+      case '剪切':
+        color=Colors.lightBlue;
+        break;
+      case '收藏':
+        color=Colors.cyan;
+        break;
+      case '取消收藏':
+        color=Colors.cyanAccent;
+        break;
+    }
+    return Container(
+      width: 170,
+      child: Card(
+        color: Color(cardColor),
+        margin: EdgeInsets.only(left: 5, right: 5, bottom: 20),
+        child: InkWell(
+          child: Center(
+            child: Padding(
+              padding: EdgeInsets.all(10),
+              child: Text(titleText, style: TextStyle(color: color)),
+            ),
+          ),
+          onTap: () {
+            Navigator.pop(context);
+            fun(file,type);
+          },
+        ),
+      ),
+    );
+  }
+}

+ 23 - 0
lib/OperationButtom/AddToFavOperationButton.dart

@@ -0,0 +1,23 @@
+import 'dart:io';
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:neofilemanager/OperationButtom/IOptButton.dart';
+import 'package:neofilemanager/Opertaion/Operation.dart';
+
+
+class AddToOperationButton extends IOptButton {
+  @override
+  Color color = Colors.cyan;
+
+  @override
+  String titleText = '收藏';
+
+  AddToOperationButton(BuildContext context, FileSystemEntity file, int type)
+      : super(context, file, type);
+
+  @override
+  void fun(FileSystemEntity file, int type) {
+    new Operation(leftFiles, rightFiles, context).addToFavorite(file, type);
+  }
+}

+ 33 - 0
lib/OperationButtom/CopyOperationButton.dart

@@ -0,0 +1,33 @@
+import 'dart:io';
+
+import 'dart:ui';
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:neofilemanager/OperationButtom/IOptButton.dart';
+import 'package:neofilemanager/Opertaion/Operation.dart';
+import '../Item/Mode.dart';
+
+class CopyOperationButton extends IOptButton {
+  @override
+  Color color = Colors.blueAccent;
+  @override
+  String titleText = '复制';
+
+  CopyOperationButton(
+      BuildContext context, FileSystemEntity file, int type, Mode mode)
+      : super(
+          context,
+          file,
+          type,
+        ) {
+    this.mode = mode;
+  }
+
+  @override
+  void fun(FileSystemEntity file, int type) {
+    // TODO: implement fun
+    new Operation(leftFiles, rightFiles, context, mode: mode)
+        .copyToDir(file, type);
+  }
+}

+ 30 - 0
lib/OperationButtom/CutOperationButton.dart

@@ -0,0 +1,30 @@
+import 'dart:io';
+
+import 'dart:ui';
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:neofilemanager/Item/Mode.dart';
+import 'package:neofilemanager/Opertaion/Operation.dart';
+
+import 'IOptButton.dart';
+
+class CutOperationButton extends IOptButton {
+  @override
+  Color color = Colors.lightBlue;
+  @override
+  String titleText = '剪切';
+
+  CutOperationButton(
+      BuildContext context, FileSystemEntity file, int type, Mode mode)
+      : super(context, file, type) {
+    this.mode = mode;
+  }
+
+//  CutOperationButton(this.context,this.file,this.type,this.mode);
+  @override
+  void fun(FileSystemEntity file, int type) {
+    new Operation(leftFiles, rightFiles, context, mode: mode)
+        .cutToDir(file, type);
+  }
+}

+ 43 - 0
lib/OperationButtom/DeleteOperationButton.dart

@@ -0,0 +1,43 @@
+import 'dart:io';
+
+import 'dart:ui';
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:neofilemanager/Item/Mode.dart';
+import 'package:neofilemanager/Opertaion/Operation.dart';
+
+import 'IOptButton.dart';
+
+class DeleteOperationButton extends IOptButton {
+  @override
+  Color color = Colors.blueGrey;
+  @override
+  String titleText = '删除';
+
+  Mode mode;
+
+  DeleteOperationButton(
+      BuildContext context,
+      FileSystemEntity file,
+      int type,
+      List<FileSystemEntity> leftFiles,
+      List<FileSystemEntity> rightFiles,
+      Mode mode,
+      ValueNotifier<bool> uiShouldChange)
+      : super(context, file, type) {
+    this.leftFiles = leftFiles;
+    this.rightFiles = rightFiles;
+    this.mode = mode;
+    this.uiShouldChange = uiShouldChange;
+  }
+
+//  DeleteOperationButton(this.context,this.file,this.type,this.mode,this.leftFiles,this.rightFiles,this.parentDir,this.uiShouldChange);
+
+  @override
+  void fun(FileSystemEntity file, int type) {
+    new Operation(leftFiles, rightFiles, context,
+            mode: mode, uiShouldChange: uiShouldChange)
+        .deleteFile(file, type);
+  }
+}

+ 12 - 0
lib/OperationButtom/IOperateButtonBusiness.dart

@@ -0,0 +1,12 @@
+import 'dart:io';
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+abstract class IOptButtonBusiness {
+  String titleText;
+  Color color;
+
+  Widget returnButton(); //返回bytton
+  void fun(FileSystemEntity file, int type); //onTap的函数
+}

+ 45 - 0
lib/OperationButtom/IOptButton.dart

@@ -0,0 +1,45 @@
+import 'dart:io';
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:neofilemanager/Item/Mode.dart';
+import 'package:neofilemanager/OperationButtom/IOperateButtonBusiness.dart';
+
+abstract class IOptButton implements IOptButtonBusiness {
+  int cardColor = 0x22ffffff;
+  FileSystemEntity file;
+  int type;
+  BuildContext context;
+  Directory parentDir; //父目录
+  List<FileSystemEntity> leftFiles = []; //左列的文件
+  List<FileSystemEntity> rightFiles = []; //右列的文件
+  Mode mode; // 接收传递过来的状态,用与判断执行何种操作
+  ValueNotifier<bool> uiShouldChange; // 用来判断是否需要对UI重新刷新
+
+  IOptButton(this.context, this.file, this.type,
+      {this.leftFiles, this.rightFiles, this.mode, this.uiShouldChange});
+
+  // 构建按钮的模板, 只有触发的方法不同
+  Widget returnButton() {
+    return Container(
+      width: 170,
+      child: Card(
+        elevation: 0,
+        color: Color(cardColor),
+        margin: EdgeInsets.only(left: 5, right: 5, bottom: 20),
+        child: InkWell(
+          child: Center(
+            child: Padding(
+              padding: EdgeInsets.all(10),
+              child: Text(titleText, style: TextStyle(color: color)),
+            ),
+          ),
+          onTap: () {
+            Navigator.pop(context);
+            fun(file, type);
+          },
+        ),
+      ),
+    );
+  }
+}

+ 21 - 0
lib/OperationButtom/RemoveFavOperationButton.dart

@@ -0,0 +1,21 @@
+import 'dart:io';
+import 'dart:ui';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:neofilemanager/Opertaion/Operation.dart';
+import 'IOptButton.dart';
+
+class RemoveFavOperation extends IOptButton{
+  @override
+  Color color=Colors.cyanAccent;
+  @override
+  String titleText='取消收藏';
+
+  RemoveFavOperation(BuildContext context, FileSystemEntity file, int type) : super(context, file, type);
+
+  @override
+  void fun(FileSystemEntity file, int type) {
+    new Operation(leftFiles, rightFiles, context).removeFavorite(file, type);
+  }
+
+}

+ 37 - 0
lib/OperationButtom/RenameOperationButton.dart

@@ -0,0 +1,37 @@
+import 'dart:io';
+import 'dart:ui';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/src/widgets/framework.dart';
+import 'package:neofilemanager/Item/Mode.dart';
+import 'package:neofilemanager/OperationButtom/IOptButton.dart';
+import 'package:neofilemanager/Opertaion/Operation.dart';
+
+class RenameOperationButton extends IOptButton {
+  @override
+  Color color = Colors.blue;
+  @override
+  String titleText = '重命名';
+
+  RenameOperationButton(
+      BuildContext context,
+      FileSystemEntity file,
+      int type,
+      List<FileSystemEntity> leftFiles,
+      List<FileSystemEntity> rightFiles,
+      ValueNotifier<bool> uiShouldChange,
+      Mode mode)
+      : super(context, file, type) {
+    this.uiShouldChange = uiShouldChange;
+    this.mode = mode;
+  }
+
+//  RenameOperationButton(this.context, this.file, this.type, this.leftFiles, this.rightFiles, this.uiShouldChange, this.mode);
+
+  @override
+  void fun(FileSystemEntity file, int type) {
+    new Operation(leftFiles, rightFiles, context,
+            uiShouldChange: uiShouldChange, mode: mode)
+        .renameFile(file, type);
+  }
+}

+ 353 - 0
lib/Opertaion/Operation.dart

@@ -0,0 +1,353 @@
+import 'dart:io';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:fluttertoast/fluttertoast.dart';
+import 'package:path/path.dart' as p;
+
+import '../Item/Common.dart';
+import '../Item/Mode.dart';
+
+class Operation {
+  List<FileSystemEntity> leftFiles = []; //左列的文件
+  List<FileSystemEntity> rightFiles = []; //右列的文件
+  BuildContext context;
+  ValueNotifier<bool> uiShouldChange;
+  Mode mode;
+
+  Operation(this.leftFiles, this.rightFiles, this.context,
+      {this.uiShouldChange, this.mode});
+
+  //添加到favorite
+  void addToFavorite(FileSystemEntity file, int type) {
+    bool flag = isInFavorite(file.path);
+    if (!flag) {
+      try {
+        File favoriteTxt = File(Common().favoriteDir + '/favorite.txt');
+        favoriteTxt.copy(Common().sDCardDir + '/1/test.txt');
+        if (Common().favoriteAll.length > 0) {
+          Common().favoriteAll =
+              Common().favoriteAll + '\n' + file.path.toString();
+        } else {
+          Common().favoriteAll = Common().favoriteAll + file.path.toString();
+        }
+        Common().favoriteFileList.add(file.path.toString());
+        favoriteTxt.writeAsStringSync(Common().favoriteAll);
+        print('收藏' + file.path);
+      } catch (err) {
+        print('错误信息' + err.toString());
+      }
+    }
+    showCupertinoDialog(
+        context: context,
+        builder: (BuildContext context) {
+          return CupertinoAlertDialog(
+            title: Text(flag ? '条目已存在' : '添加成功'),
+            actions: <Widget>[
+              CupertinoDialogAction(
+                child: Text('确定'),
+                onPressed: () {
+                  Navigator.pop(context);
+                },
+              )
+            ],
+          );
+        });
+  }
+
+  //重命名文件
+  void renameFile(FileSystemEntity file, int type) {
+    TextEditingController _controller = TextEditingController();
+    if (context == null) {
+      print('context为null2');
+    }
+
+    showCupertinoDialog(
+        context: context,
+        builder: (BuildContext context) {
+          return Scaffold(
+            backgroundColor: Colors.transparent,
+            body: Center(
+              child: CupertinoAlertDialog(
+                title: Text('重命名'),
+                content: Padding(
+                  padding: EdgeInsets.only(top: 10),
+                  child: TextField(
+                    controller: _controller,
+                    decoration: InputDecoration(
+                      hintText: '请输入新名称',
+                      border: OutlineInputBorder(
+                          borderRadius: BorderRadius.circular(10.0),
+                          borderSide: BorderSide(width: 0.3)),
+                      focusedBorder: OutlineInputBorder(
+                          borderRadius: BorderRadius.circular(10.0),
+                          borderSide: BorderSide(width: 0.5)),
+                      contentPadding: EdgeInsets.all(8.0),
+                    ),
+                  ),
+                ),
+                actions: <Widget>[
+                  CupertinoButton(
+                    child: Text(
+                      '取消',
+                      style: TextStyle(color: Colors.blue),
+                    ),
+                    onPressed: () {
+                      Navigator.pop(context);
+                    },
+                  ),
+                  CupertinoButton(
+                    child: Text(
+                      '确认',
+                      style: TextStyle(color: Colors.blue),
+                    ),
+                    onPressed: () async {
+                      String newName = _controller.text;
+                      if (newName.trim().length == 0) {
+                        Fluttertoast.showToast(
+                            msg: '名字不能为空', gravity: ToastGravity.BOTTOM);
+                        return;
+                      }
+                      String newPath = "";
+                      if (file.statSync().type == FileSystemEntityType.file) {
+                        newPath = file.parent.path +
+                            '/' +
+                            newName +
+                            p.extension(file.path);
+                      } else {
+                        newPath = file.parent.path + '/' + newName;
+                      }
+                      file.renameSync(newPath);
+                      if (type == -1) {
+                        initPathFiles(newPath, -3);
+                      } else {
+                        initPathFiles(file.parent.path, type);
+                      }
+                      Navigator.pop(context);
+                    },
+                  )
+                ],
+              ),
+            ),
+          );
+        });
+  }
+
+  //用传来的路径刷新该目录下的文件.文件夹
+  void initPathFiles(String path, int type) {
+    try {
+      //可变的Widget可以使用setState()函数--重新绘制试图--调用build方法--绘制不一样的地方
+      //-1: 右列-->父目录下  (点了左边列表,刷新右边列表,parentDir时左边的)
+      // 1: 右列-->父目录下  (点了右列的文件夹的情况,parenDir换成了右边的)
+      //    左列-->父目录同级
+      //-2: 左列-->当前父目录下 (初始化时,用sdcard刷线左列)
+      // 2: 右列-->父目录同级   (按了返回键)
+      //    左列-->父目录的父目录同级
+      //-3: 左列-->父目录同级  (原地刷新)
+      //    右列-->父目录下
+      mode.parentDir = Directory(path);
+      print('parentDir 更改为' + mode.parentDir.path);
+      if (type == -1) {
+        //点击左列
+        //刷新右列表
+        sortFile(mode.parentDir, -1);
+      } else if (type == 1) {
+        //点击右列
+        //往右走 先刷右列表 再刷左列表
+        sortFile(mode.parentDir, -1);
+        sortFile(mode.parentDir.parent, 1);
+      } else if (type == -2) {
+        //初始化
+        sortFile(mode.parentDir, 1);
+      } else if (type == 2) {
+        //返回时
+        sortFile(mode.parentDir.parent, -1);
+        sortFile(mode.parentDir.parent.parent, 1);
+        mode.parentDir = mode.parentDir.parent;
+      } else if (type == -3) {
+        //左列重命名文件夹
+        sortFile(mode.parentDir.parent, 1);
+        sortFile(mode.parentDir, -1);
+      }
+    } catch (e) {
+      print(e);
+      print("Directory does not exist");
+    }
+    changeUI();
+  }
+
+  void changeUI() {
+    if (uiShouldChange == null) {
+      print('uiShouldChange is null');
+    }
+    if (uiShouldChange.value) {
+      uiShouldChange.value = false;
+    } else {
+      uiShouldChange.value = true;
+    }
+  }
+
+  //目录排序
+  void sortFile(Directory parentDir, int type) {
+    List<FileSystemEntity> _Files = [];
+    List<FileSystemEntity> _Folders = [];
+    //parenDire--路径
+    //Directory.listSync 会列出当前目录下所有的文件和文件夹
+    for (var v in parentDir.listSync()) {
+      //去除.开头的文件/文件夹
+      if (p.basename(v.path).substring(0, 1) == '.') {
+        continue;
+      }
+      if (FileSystemEntity.isFileSync(v.path))
+        _Files.add(v);
+      else
+        _Folders.add(v);
+    }
+    _Files.sort((a, b) => a.path.toLowerCase().compareTo(b.path.toLowerCase()));
+    _Folders.sort(
+        (a, b) => a.path.toLowerCase().compareTo(b.path.toLowerCase()));
+    if (type == -1) {
+      //刷新右列
+      rightFiles.clear();
+      rightFiles.addAll(_Folders);
+      rightFiles.addAll(_Files);
+    } else {
+      //刷新左列
+      leftFiles.clear();
+      leftFiles.addAll(_Folders);
+      leftFiles.addAll(_Files);
+    }
+    changeUI();
+  }
+
+  //文件是否在favorite中
+  bool isInFavorite(String filePath) {
+    bool flag = false;
+    for (var fileItem in Common().favoriteFileList) {
+      if (fileItem == filePath) {
+        flag = true;
+      }
+    }
+    return flag;
+  }
+
+  //将更新后的喜爱条目写回文件
+  void writeIntoLocal() {
+    String temp = '';
+    int i = 0;
+    for (var item in Common().favoriteFileList) {
+      print('写入' + item);
+      if (i == 0) {
+        temp = item.toString();
+      } else {
+        temp = temp + '\n' + item.toString();
+      }
+      i++;
+    }
+    Common().favoriteAll = temp;
+    try {
+      File favoriteTxt = File(Common().favoriteDir + '/favorite.txt');
+      favoriteTxt.writeAsStringSync(Common().favoriteAll);
+    } catch (err) {
+      print('错误信息' + err.toString());
+    }
+  }
+
+  void copyToDir(FileSystemEntity file, int type) {
+    mode.cutMode = false;
+    mode.copyMode = true;
+    mode.copyTempFile = file;
+  }
+
+  //剪切文件到
+  void cutToDir(FileSystemEntity file, int type) {
+    mode.copyMode = false;
+    mode.cutMode = true;
+    mode.copyTempFile = file;
+  }
+
+  //从favorite中移除
+  void removeFavorite(FileSystemEntity file, int type) {
+    bool flag = isInFavorite(file.path);
+    if (flag) {
+      //如果要取消收藏的文件在favorite条目中存在
+      int i = 0;
+      //从Common中的喜爱条目中删除它
+      for (var item in Common().favoriteFileList) {
+        print(i.toString() + '  ' + item.toString());
+        if (item.toString() == file.path) {
+          Common().favoriteFileList.removeAt(i);
+          break;
+        }
+        i++;
+      }
+      //将更新后的喜爱条目写回文件
+      writeIntoLocal();
+      changeUI();
+    }
+    showCupertinoDialog(
+        context: context,
+        builder: (BuildContext context) {
+          return CupertinoAlertDialog(
+            title: Text(flag ? '已取消收藏' : '取消收藏失败'),
+            actions: <Widget>[
+              CupertinoDialogAction(
+                child: Text('确定'),
+                onPressed: () {
+                  Navigator.pop(context);
+                },
+              )
+            ],
+          );
+          Navigator.pop(context);
+        });
+  }
+
+  //删除文件
+  void deleteFile(FileSystemEntity file, int type) {
+    showCupertinoDialog(
+        context: context,
+        builder: (BuildContext context) {
+          return CupertinoAlertDialog(
+            title: Text('重命名'),
+            content: Text('删除后不可恢复'),
+            actions: <Widget>[
+              CupertinoDialogAction(
+                child: Text(
+                  '取消',
+                  style: TextStyle(color: Colors.blue),
+                ),
+                onPressed: () {
+                  Navigator.pop(context);
+                },
+              ),
+              CupertinoDialogAction(
+                child: Text(
+                  '确定',
+                  style: TextStyle(color: Colors.blue),
+                ),
+                onPressed: () {
+                  String temp = file.parent.path;
+                  if (file.statSync().type == FileSystemEntityType.directory) {
+                    Directory directory = Directory(file.path);
+                    directory.deleteSync(recursive: true);
+                  } else if (file.statSync().type ==
+                      FileSystemEntityType.file) {
+                    file.deleteSync();
+                  }
+                  //TODO
+                  if (type == -1) {
+                    //删除了左边的文件
+                    sortFile(Directory(temp), 1);
+                  } else if (type == 1) {
+                    //删除了右边的文件
+                    initPathFiles(mode.parentDir.path, -1);
+                  }
+                  changeUI();
+                  Navigator.pop(context);
+                },
+              )
+            ],
+          );
+        });
+  }
+}

+ 577 - 0
lib/file_manager.dart

@@ -0,0 +1,577 @@
+import 'dart:io';
+import 'dart:ui';
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter/widgets.dart';
+import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
+import 'package:intl/intl.dart';
+import 'package:neofilemanager/Item/Preview/FilePre.dart';
+import 'package:neofilemanager/Item/Preview/Impl/ProxyFilePre.dart';
+import 'package:neofilemanager/Item/Preview/Impl/TxtFilePre.dart';
+import 'package:neofilemanager/OperationButtom/CutOperationButton.dart';
+import 'package:neofilemanager/OperationButtom/DeleteOperationButton.dart';
+import 'package:neofilemanager/Opertaion/Operation.dart';
+import 'package:open_file/open_file.dart';
+import 'package:path/path.dart' as p;
+import 'Item/Common.dart';
+import 'Item/Mode.dart';
+import 'Item/widgetItem.dart';
+import 'OperationButtom/AddToFavOperationButton.dart';
+import 'OperationButtom/CopyOperationButton.dart';
+import 'OperationButtom/RemoveFavOperationButton.dart';
+import 'OperationButtom/RenameOperationButton.dart';
+
+class MyHome extends StatefulWidget {
+  MyHome({Key key, this.title}) : super(key: key);
+
+  final String title;
+
+  @override
+  _MyHomePageState createState() => _MyHomePageState();
+}
+
+class _MyHomePageState extends State<MyHome> {
+  ValueNotifier<bool> uiShouldChange = ValueNotifier<bool>(false);
+
+  List<FileSystemEntity> leftFiles = []; //左列的文件
+  List<FileSystemEntity> rightFiles = []; //右列的文件
+  Mode mode = new Mode(false, false, null, null);
+  int cardColor = 0x22ffffff;
+  double iconHeight = 30.0;
+  double iconWidth = 30.0;
+  double fileFontSize = 9.0;
+  double subTitleFontSize = 6.0;
+  ScrollController controller = ScrollController();
+  List<double> position = [];
+  Common common = new Common();
+
+  ///初始化: 拿到根路径 刷新目录
+  @override
+  void initState() {
+    super.initState();
+    mode.parentDir = Directory(common.sDCardDir); //获取根路径
+    new Operation(leftFiles, rightFiles, context,
+            uiShouldChange: uiShouldChange, mode: mode)
+        .initPathFiles(common.sDCardDir, -2);
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return ValueListenableBuilder(
+      valueListenable: uiShouldChange,
+      builder: (BuildContext context, bool value, Widget child) {
+        return WillPopScope(
+          onWillPop: onWillPop,
+          child: Scaffold(
+            backgroundColor: Colors.black,
+            appBar: AppBar(
+              title: Text(
+                mode.parentDir?.path == common.sDCardDir
+                    ? 'SD card'
+                    : p.basename(mode.parentDir.path),
+              ),
+              centerTitle: true,
+              elevation: 0.0,
+              //浮起来的高度
+              leading: mode.parentDir?.path == common.sDCardDir
+                  ? Container()
+                  : IconButton(
+                      icon: Icon(
+                        Icons.arrow_left,
+                      ),
+                      onPressed: onWillPop,
+                    ), //再标题前面显示的一个控件
+            ),
+            body: returnBody(),
+            bottomNavigationBar: returnBottomBar(),
+          ),
+        );
+      },
+    );
+  }
+
+  Widget returnBody() {
+    return Row(
+      children: <Widget>[
+        Expanded(
+          child: _fileListView(leftFiles, -1),
+        ),
+        Expanded(
+          child: _fileListView(rightFiles, 1),
+        ),
+      ],
+    );
+  }
+
+  Widget returnBottomBar() {
+    return Container(
+      color: Color(0x44000000),
+      child: Row(
+        crossAxisAlignment: CrossAxisAlignment.center,
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: <Widget>[
+          Expanded(
+            child: CupertinoButton(
+              child: Icon(Icons.add),
+              onPressed: () {
+                moreAction(-1);
+              },
+            ),
+          ),
+          Expanded(
+            child: CupertinoButton(
+              child: Icon(Icons.home),
+              onPressed: () {
+                new Operation(leftFiles, rightFiles, context,
+                        uiShouldChange: uiShouldChange, mode: mode)
+                    .initPathFiles(common.sDCardDir, -2);
+              },
+            ),
+          ),
+          Expanded(
+            child: CupertinoButton(
+              child: Icon(Icons.favorite),
+              onPressed: () {
+                listFavorite();
+              },
+            ),
+          ),
+          Expanded(
+            child: CupertinoButton(
+              child: Icon(Icons.add),
+              onPressed: () {
+                moreAction(1);
+              },
+            ),
+          ),
+        ],
+      ),
+    );
+  }
+
+  ///用于导航返回拦截器的onWillPop
+  Future<bool> onWillPop() async {
+    if (mode.parentDir.parent.path != common.sDCardDir &&
+        mode.parentDir.path != common.sDCardDir) {
+      /// 如果不是根目录,就跳转到上层目录并刷新目录
+//      initPathFiles(parentDir.path, 2);
+      new Operation(leftFiles, rightFiles, context,
+              uiShouldChange: uiShouldChange, mode: mode)
+          .initPathFiles(mode.parentDir.path, 2);
+    } else {
+      ///否则退出
+      ///https://blog.csdn.net/weixin_33979203/article/details/88019065
+      ///Router: 路由 对屏幕界面的抽象 每一个页面都有相应的Page
+      print('退出时parentDir为:' + mode.parentDir.path);
+      SystemNavigator.pop();
+    }
+    return false;
+  }
+
+  Widget _fileListView(List<FileSystemEntity> files, int type) {
+    if (files.length == 0)
+      return Center(child: Text('The folder is empty'));
+    else {
+      return Scrollbar(
+        child: ListView.builder(
+            physics: BouncingScrollPhysics(),
+            //控制列表的滚动的发生方式(再列表结束时退回列表,iOS中有类似效果)
+            controller: controller,
+            itemCount: files.length,
+            itemBuilder: (BuildContext context, int index) {
+              if (FileSystemEntity.isFileSync(files[index].path))
+                return _buildFileItem(files[index], type, index);
+              else
+                return _buildFolderItem(files[index], type, index);
+            }),
+      );
+    }
+  }
+
+  /// 创建文件的ListTile
+  Widget _buildFileItem(FileSystemEntity file, int type, int index) {
+    //获取文件最后改动日期
+    String modifiledTime = DateFormat('yyyy-MM-dd HH:mm:ss', 'zh_CN')
+        .format(file.statSync().modified.toLocal());
+    FilePre fileIcon;
+    fileIcon = ProxyFilePre(
+      common,
+      file,
+      p.extension(file.path),
+      50,
+      70,
+    );
+    Image tempImage;
+    ///InkWell: 水波纹效果
+    return InkWell(
+      child: Container(
+        decoration: BoxDecoration(
+          color: Colors.black,
+          border: Border(bottom: BorderSide(width: 0.5, color: Colors.black12)),
+        ),
+        child: ListTile(
+          leading: FutureBuilder(
+            future: (file,tempImage)async{
+              return tempImage;
+            },
+            builder: (context,snapshot){
+                if(snapshot.connectionState==ConnectionState.done){
+                  
+                }
+            }
+          ),
+          title: Text(file.path.substring(file.parent.path.length + 1),
+              style: TextStyle(fontSize: fileFontSize)),
+          ///从文件路径中截取文件名字 (file.parent.length+1-文件父目录长度)
+          subtitle: Text(
+            '$modifiledTime ${common.getFileSize(file.statSync().size)}',
+            style: TextStyle(fontSize: subTitleFontSize),
+          ), //时间和文件大小
+        ),
+      ),
+      onTap: () {
+        OpenFile.open(file.path);
+      },
+      onLongPress: () {
+        Container fileIconPre;
+        FilePre filePre;
+        if (p.extension(file.path) == '.txt') {
+          filePre = TxtFilePre(filePre);
+          fileIconPre = Container(
+            margin: EdgeInsets.all(10),
+            padding: EdgeInsets.all(10),
+            decoration: BoxDecoration(
+              color: Color(0x22ffffff),
+              borderRadius: BorderRadius.all(Radius.circular(5)),
+            ),
+            width: MediaQuery.of(context).size.width / 2,
+            child: ListView(
+              children: <Widget>[Text((filePre as TxtFilePre).getContent())],
+            ),
+          );
+        } else {
+          fileIconPre = Container(
+            width: MediaQuery.of(context).size.width / 2,
+            child: Padding(
+              padding: const EdgeInsets.all(14.0),
+              child: filePre,
+            ),
+          );
+        }
+        showModalBottomSheet(
+            backgroundColor: Color(0x00000000),
+            context: context,
+            builder: (BuildContext context) {
+              return BackdropFilter(
+                  filter: ImageFilter.blur(
+                    sigmaX: 5.0,
+                    sigmaY: 5.0,
+                  ),
+                  child: Row(
+                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                    children: <Widget>[
+                      type == -1
+                          ? Container(
+                              width: 0,
+                            )
+                          : fileIconPre,
+                      Column(
+                        mainAxisSize: MainAxisSize.min,
+                        crossAxisAlignment: type == -1
+                            ? CrossAxisAlignment.start
+                            : CrossAxisAlignment.end,
+                        children: <Widget>[
+                          RenameOperationButton(context, file, type, leftFiles,
+                                  rightFiles, uiShouldChange, mode)
+                              .returnButton(),
+                          CopyOperationButton(context, file, type, mode)
+                              .returnButton(),
+                          CutOperationButton(context, file, type, mode)
+                              .returnButton(),
+                          AddToOperationButton(context, file, type)
+                              .returnButton(),
+                          RemoveFavOperation(context, file, type)
+                              .returnButton(),
+                          DeleteOperationButton(context, file, type, leftFiles,
+                                  rightFiles, mode, uiShouldChange)
+                              .returnButton(),
+                        ],
+                      ),
+                      type == -1
+                          ? fileIconPre
+                          : Container(
+                              width: 0,
+                            ),
+                    ],
+                  ));
+            });
+      },
+    );
+  }
+
+  Future<Widget> getPreview(file) async{
+       return Image.file(file, height:30, width:40, fit: BoxFit.cover);
+  }
+
+  Widget _buildFolderItem(FileSystemEntity file, int type, int index) {
+    String modifiledTime = DateFormat('yyyy-MM-dd HH:mm:ss', 'zh_CN')
+        .format(file.statSync().modified.toLocal());
+    //InkWell: 水波纹效果
+    return AnimationConfiguration.staggeredList(
+      position: index,
+      duration: const Duration(milliseconds: 100),
+      child: SlideAnimation(
+        horizontalOffset: 50,
+        child: FadeInAnimation(
+          child: Card(
+            color: Color(0xff222222),
+            elevation: 15.0,
+            margin: EdgeInsets.only(left: 5, right: 5, bottom: 5, top: 5),
+            child: InkWell(
+              child: Container(
+                decoration: BoxDecoration(
+//            color: Colors.black,
+//            border:
+//                Border(bottom: BorderSide(width: 0.5, color: Colors.white)),
+                    ),
+                child: ListTile(
+                    leading: Image.asset(
+                      'assets/images/folder.png',
+                      height: iconHeight,
+                      width: iconWidth,
+                    ),
+                    title: Row(
+                      children: <Widget>[
+                        Expanded(
+                          child: Text(
+                              file.path.substring(file.parent.path.length + 1),
+                              style: TextStyle(fontSize: fileFontSize)),
+                        ),
+                      ],
+                    ),
+                    subtitle: Text(
+                      modifiledTime,
+                      style: TextStyle(fontSize: subTitleFontSize),
+                    ),
+                    trailing: Icon(
+                      Icons.arrow_right,
+                      size: 12.0,
+                    )
+
+                    ///trailing: 和leading相对,最后面
+                    ),
+              ),
+              onTap: () {
+                new Operation(leftFiles, rightFiles, context,
+                        uiShouldChange: uiShouldChange, mode: mode)
+                    .initPathFiles(file.path, type);
+              },
+              onLongPress: () {
+                showModalBottomSheet(
+                    backgroundColor: Color(0x00000000),
+                    context: context,
+                    builder: (BuildContext context) {
+                      return BackdropFilter(
+                        filter: ImageFilter.blur(
+                          sigmaX: 5.0,
+                          sigmaY: 5.0,
+                        ),
+                        child: Container(
+                          child: Column(
+                            mainAxisSize: MainAxisSize.min,
+                            crossAxisAlignment: type == -1
+                                ? CrossAxisAlignment.start
+                                : CrossAxisAlignment.end,
+                            children: <Widget>[
+                              new RenameOperationButton(
+                                      context,
+                                      file,
+                                      type,
+                                      leftFiles,
+                                      rightFiles,
+                                      uiShouldChange,
+                                      mode)
+                                  .returnButton(),
+                              new AddToOperationButton(context, file, type)
+                                  .returnButton(),
+                              new RemoveFavOperation(context, file, type)
+                                  .returnButton(),
+                              new DeleteOperationButton(
+                                      context,
+                                      file,
+                                      type,
+                                      leftFiles,
+                                      rightFiles,
+                                      mode,
+                                      uiShouldChange)
+                                  .returnButton(),
+                            ],
+                          ),
+                        ),
+                      );
+                    });
+              },
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+
+  Widget returnFileOperateButton(int cardColor,
+      Function(FileSystemEntity file) fun, FileSystemEntity file) {
+    return Container(
+      width: 170,
+      child: Card(
+        color: Color(cardColor),
+        margin: EdgeInsets.only(left: 5, right: 5, bottom: 20),
+        child: InkWell(
+          child: Center(
+            child: Padding(
+              padding: EdgeInsets.all(10),
+              child: Text('收藏', style: TextStyle(color: Colors.cyan)),
+            ),
+          ),
+          onTap: () {
+            Navigator.pop(context);
+            fun(file);
+          },
+        ),
+      ),
+    );
+  }
+
+  //粘贴要复制的文件
+  //左右两边的加号
+  void moreAction(int side) {
+    String destinationDir = '';
+    if (side == -1) {
+      destinationDir = mode.parentDir.parent.path;
+    } else {
+      destinationDir = mode.parentDir.path;
+    }
+    showModalBottomSheet(
+        backgroundColor: Color(0x00000000),
+        context: context,
+        builder: (BuildContext context) {
+          return BackdropFilter(
+            filter: ImageFilter.blur(
+              sigmaX: 5.0,
+              sigmaY: 5.0,
+            ),
+            child: Container(
+              child: Column(
+                mainAxisSize: MainAxisSize.min,
+                crossAxisAlignment: side == -1
+                    ? CrossAxisAlignment.start
+                    : CrossAxisAlignment.end,
+                children: <Widget>[
+                  mode.copyMode | mode.cutMode
+                      ? Column(
+                          children: <Widget>[
+                            Container(
+                              width: 170,
+                              child: Card(
+                                margin: EdgeInsets.only(
+                                    left: 5, right: 5, bottom: 20),
+                                child: InkWell(
+                                  child: Center(
+                                    child: Padding(
+                                      padding: EdgeInsets.all(10),
+                                      child: Text('粘贴',
+                                          style: TextStyle(color: Colors.blue)),
+                                    ),
+                                  ),
+                                  onTap: () {
+                                    print('copyTempFile-->file_manager:' +
+                                        mode.copyTempFile.path.toString());
+                                    if (mode.copyTempFile.statSync().type ==
+                                        FileSystemEntityType.file) {
+                                      String path = mode.copyTempFile.path;
+                                      File temp = mode.copyTempFile;
+                                      print('剪切的文件 ' + temp.path);
+                                      temp.copy(destinationDir +
+                                          '/' +
+                                          temp.path.substring(
+                                              temp.parent.path.length + 1));
+                                      mode.copyTempFile = null; //复制之后取消复制模式
+                                      if (mode.cutMode) {
+                                        temp = File(path);
+                                        temp.delete();
+                                        print('删除' + temp.path);
+                                      }
+                                      if (side == -1) {
+                                        new Operation(
+                                                leftFiles, rightFiles, context,
+                                                uiShouldChange: uiShouldChange,
+                                                mode: mode)
+                                            .initPathFiles(
+                                                mode.parentDir.path, -3);
+                                      } else {
+                                        new Operation(
+                                                leftFiles, rightFiles, context,
+                                                uiShouldChange: uiShouldChange,
+                                                mode: mode)
+                                            .initPathFiles(
+                                                mode.parentDir.path, -3);
+                                      }
+                                      mode.copyMode = false;
+                                      mode.cutMode = false;
+                                      Navigator.pop(context);
+                                    }
+                                  },
+                                ),
+                              ),
+                            ),
+                            Container(
+                              width: 170,
+                              child: Card(
+                                margin: EdgeInsets.only(
+                                    left: 5, right: 5, bottom: 20),
+                                child: InkWell(
+                                  child: Center(
+                                    child: Padding(
+                                      padding: EdgeInsets.all(10),
+                                      child: Text('取消',
+                                          style: TextStyle(
+                                              color: Colors.cyanAccent)),
+                                    ),
+                                  ),
+                                  onTap: () {
+                                    mode.copyMode = false;
+                                    mode.copyTempFile = null; //取消复制模式
+                                    Navigator.pop(context);
+                                  },
+                                ),
+                              ),
+                            )
+                          ],
+                        )
+                      : Container(
+                          width: 0,
+                        ),
+                  WidgetItem().returnFileOperateButton(context, cardColor,
+                      (file, type) => null, '新建文件', null, null)
+                ],
+              ),
+            ),
+          );
+        });
+  }
+
+  //切换favorite
+  void listFavorite() {
+    setState(() {
+      leftFiles.clear();
+      rightFiles.clear();
+      if (common.favoriteFileList != null) {
+        for (var fileItem in common.favoriteFileList) {
+          print(fileItem);
+          leftFiles.add(new File(fileItem));
+        }
+      }
+    });
+  }
+}