feat: drop to send files to remote

Signed-off-by: Kingtous <kingtous@qq.com>
This commit is contained in:
Kingtous 2022-08-16 13:28:48 +08:00
parent 4bd5fe1509
commit a001b15335
4 changed files with 268 additions and 232 deletions

View File

@ -1,6 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:desktop_drop/desktop_drop.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_breadcrumb/flutter_breadcrumb.dart'; import 'package:flutter_breadcrumb/flutter_breadcrumb.dart';
import 'package:flutter_hbb/mobile/pages/file_manager_page.dart'; import 'package:flutter_hbb/mobile/pages/file_manager_page.dart';
@ -39,6 +40,8 @@ class _FileManagerPageState extends State<FileManagerPage>
final _breadCrumbScrollerLocal = ScrollController(); final _breadCrumbScrollerLocal = ScrollController();
final _breadCrumbScrollerRemote = ScrollController(); final _breadCrumbScrollerRemote = ScrollController();
final _dropMaskVisible = false.obs;
ScrollController getBreadCrumbScrollController(bool isLocal) { ScrollController getBreadCrumbScrollController(bool isLocal) {
return isLocal ? _breadCrumbScrollerLocal : _breadCrumbScrollerRemote; return isLocal ? _breadCrumbScrollerLocal : _breadCrumbScrollerRemote;
} }
@ -155,243 +158,248 @@ class _FileManagerPageState extends State<FileManagerPage>
decoration: BoxDecoration(border: Border.all(color: Colors.black26)), decoration: BoxDecoration(border: Border.all(color: Colors.black26)),
margin: const EdgeInsets.all(16.0), margin: const EdgeInsets.all(16.0),
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ child: DropTarget(
headTools(isLocal), onDragDone: (detail) => handleDragDone(detail, isLocal),
Expanded( onDragEntered: (enter) {
child: Row( _dropMaskVisible.value = true;
crossAxisAlignment: CrossAxisAlignment.start, },
children: [ onDragExited: (exit) {
Expanded( _dropMaskVisible.value = false;
child: SingleChildScrollView( },
child: entries.isEmpty child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
? Offstage() headTools(isLocal),
: Obx( Expanded(
() { child: Row(
final searchText = crossAxisAlignment: CrossAxisAlignment.start,
isLocal ? _searchTextLocal : _searchTextRemote; children: [
final filteredEntries = searchText.isEmpty Expanded(
? entries.where((element) { child: SingleChildScrollView(
if (searchText.isEmpty) { child: ObxValue<RxString>(
return true; (searchText) {
} else { final filteredEntries = searchText.isEmpty
return element.name ? entries.where((element) {
.contains(searchText.value); if (searchText.isEmpty) {
} return true;
}).toList(growable: false) } else {
: entries; return element.name.contains(searchText.value);
return DataTable(
key: ValueKey(isLocal ? 0 : 1),
showCheckboxColumn: true,
dataRowHeight: 25,
headingRowHeight: 30,
columnSpacing: 8,
showBottomBorder: true,
sortColumnIndex: sortIndex,
sortAscending: sortAscending,
columns: [
DataColumn(label: Text(translate(" "))), // icon
DataColumn(
label: Text(
translate("Name"),
),
onSort: (columnIndex, ascending) {
model.changeSortStyle(SortBy.Name,
isLocal: isLocal, ascending: ascending);
}),
DataColumn(
label: Text(
translate("Modified"),
),
onSort: (columnIndex, ascending) {
model.changeSortStyle(SortBy.Modified,
isLocal: isLocal, ascending: ascending);
}),
DataColumn(
label: Text(translate("Size")),
onSort: (columnIndex, ascending) {
model.changeSortStyle(SortBy.Size,
isLocal: isLocal, ascending: ascending);
}),
],
rows: filteredEntries.map((entry) {
final sizeStr = entry.isFile
? readableFileSize(entry.size.toDouble())
: "";
return DataRow(
key: ValueKey(entry.name),
onSelectChanged: (s) {
if (s != null) {
if (s) {
getSelectedItem(isLocal).add(isLocal, entry);
} else {
getSelectedItem(isLocal).remove(entry);
}
setState(() {});
} }
}, }).toList(growable: false)
selected: getSelectedItem(isLocal).contains(entry), : entries;
cells: [ return DataTable(
DataCell(Icon( key: ValueKey(isLocal ? 0 : 1),
entry.isFile showCheckboxColumn: true,
? Icons.feed_outlined dataRowHeight: 25,
: Icons.folder, headingRowHeight: 30,
size: 25)), columnSpacing: 8,
DataCell( showBottomBorder: true,
ConstrainedBox( sortColumnIndex: sortIndex,
constraints: sortAscending: sortAscending,
BoxConstraints(maxWidth: 100), columns: [
child: Tooltip( DataColumn(label: Text(translate(" "))), // icon
message: entry.name, DataColumn(
child: Text(entry.name, label: Text(
overflow: TextOverflow.ellipsis), translate("Name"),
)), onTap: () { ),
if (entry.isDirectory) { onSort: (columnIndex, ascending) {
openDirectory(entry.path, isLocal: isLocal); model.changeSortStyle(SortBy.Name,
if (isLocal) { isLocal: isLocal, ascending: ascending);
_localSelectedItems.clear(); }),
DataColumn(
label: Text(
translate("Modified"),
),
onSort: (columnIndex, ascending) {
model.changeSortStyle(SortBy.Modified,
isLocal: isLocal, ascending: ascending);
}),
DataColumn(
label: Text(translate("Size")),
onSort: (columnIndex, ascending) {
model.changeSortStyle(SortBy.Size,
isLocal: isLocal, ascending: ascending);
}),
],
rows: filteredEntries.map((entry) {
final sizeStr = entry.isFile
? readableFileSize(entry.size.toDouble())
: "";
return DataRow(
key: ValueKey(entry.name),
onSelectChanged: (s) {
if (s != null) {
if (s) {
getSelectedItem(isLocal)
.add(isLocal, entry);
} else { } else {
_remoteSelectedItems.clear(); getSelectedItem(isLocal).remove(entry);
}
} else {
// Perform file-related tasks.
final _selectedItems =
getSelectedItem(isLocal);
if (_selectedItems.contains(entry)) {
_selectedItems.remove(entry);
} else {
_selectedItems.add(isLocal, entry);
} }
setState(() {}); setState(() {});
} }
}), },
DataCell(Text( selected:
entry getSelectedItem(isLocal).contains(entry),
.lastModified() cells: [
.toString() DataCell(Icon(
.replaceAll(".000", "") + entry.isFile
" ", ? Icons.feed_outlined
style: TextStyle( : Icons.folder,
fontSize: 12, size: 25)),
color: MyTheme.darkGray), DataCell(
)), ConstrainedBox(
DataCell(Text( constraints:
sizeStr, BoxConstraints(maxWidth: 100),
style: TextStyle( child: Tooltip(
fontSize: 12, message: entry.name,
color: MyTheme.darkGray), child: Text(entry.name,
)), overflow: TextOverflow.ellipsis),
]); )), onTap: () {
}).toList(growable: false), if (entry.isDirectory) {
); openDirectory(entry.path, isLocal: isLocal);
}, if (isLocal) {
_localSelectedItems.clear();
} else {
_remoteSelectedItems.clear();
}
} else {
// Perform file-related tasks.
final _selectedItems =
getSelectedItem(isLocal);
if (_selectedItems.contains(entry)) {
_selectedItems.remove(entry);
} else {
_selectedItems.add(isLocal, entry);
}
setState(() {});
}
}),
DataCell(Text(
entry
.lastModified()
.toString()
.replaceAll(".000", "") +
" ",
style: TextStyle(
fontSize: 12, color: MyTheme.darkGray),
)),
DataCell(Text(
sizeStr,
style: TextStyle(
fontSize: 12, color: MyTheme.darkGray),
)),
]);
}).toList(growable: false),
);
},
isLocal ? _searchTextLocal : _searchTextRemote,
),
), ),
), )
) ],
], )),
)), // Center(child: listTail(isLocal: isLocal)),
// Center(child: listTail(isLocal: isLocal)), // Expanded(
// Expanded( // child: ListView.builder(
// child: ListView.builder( // itemCount: entries.length + 1,
// itemCount: entries.length + 1, // itemBuilder: (context, index) {
// itemBuilder: (context, index) { // if (index >= entries.length) {
// if (index >= entries.length) { // return listTail(isLocal: isLocal);
// return listTail(isLocal: isLocal); // }
// } // var selected = false;
// var selected = false; // if (model.selectMode) {
// if (model.selectMode) { // selected = _selectedItems.contains(entries[index]);
// selected = _selectedItems.contains(entries[index]); // }
// } //
// // final sizeStr = entries[index].isFile
// final sizeStr = entries[index].isFile // ? readableFileSize(entries[index].size.toDouble())
// ? readableFileSize(entries[index].size.toDouble()) // : "";
// : ""; // return Card(
// return Card( // child: ListTile(
// child: ListTile( // leading: Icon(
// leading: Icon( // entries[index].isFile ? Icons.feed_outlined : Icons.folder,
// entries[index].isFile ? Icons.feed_outlined : Icons.folder, // size: 40),
// size: 40), // title: Text(entries[index].name),
// title: Text(entries[index].name), // selected: selected,
// selected: selected, // subtitle: Text(
// subtitle: Text( // entries[index]
// entries[index] // .lastModified()
// .lastModified() // .toString()
// .toString() // .replaceAll(".000", "") +
// .replaceAll(".000", "") + // " " +
// " " + // sizeStr,
// sizeStr, // style: TextStyle(fontSize: 12, color: MyTheme.darkGray),
// style: TextStyle(fontSize: 12, color: MyTheme.darkGray), // ),
// ), // trailing: needShowCheckBox()
// trailing: needShowCheckBox() // ? Checkbox(
// ? Checkbox( // value: selected,
// value: selected, // onChanged: (v) {
// onChanged: (v) { // if (v == null) return;
// if (v == null) return; // if (v && !selected) {
// if (v && !selected) { // _selectedItems.add(isLocal, entries[index]);
// _selectedItems.add(isLocal, entries[index]); // } else if (!v && selected) {
// } else if (!v && selected) { // _selectedItems.remove(entries[index]);
// _selectedItems.remove(entries[index]); // }
// } // setState(() {});
// setState(() {}); // })
// }) // : PopupMenuButton<String>(
// : PopupMenuButton<String>( // icon: Icon(Icons.more_vert),
// icon: Icon(Icons.more_vert), // itemBuilder: (context) {
// itemBuilder: (context) { // return [
// return [ // PopupMenuItem(
// PopupMenuItem( // child: Text(translate("Delete")),
// child: Text(translate("Delete")), // value: "delete",
// value: "delete", // ),
// ), // PopupMenuItem(
// PopupMenuItem( // child: Text(translate("Multi Select")),
// child: Text(translate("Multi Select")), // value: "multi_select",
// value: "multi_select", // ),
// ), // PopupMenuItem(
// PopupMenuItem( // child: Text(translate("Properties")),
// child: Text(translate("Properties")), // value: "properties",
// value: "properties", // enabled: false,
// enabled: false, // )
// ) // ];
// ]; // },
// }, // onSelected: (v) {
// onSelected: (v) { // if (v == "delete") {
// if (v == "delete") { // final items = SelectedItems();
// final items = SelectedItems(); // items.add(isLocal, entries[index]);
// items.add(isLocal, entries[index]); // model.removeAction(items);
// model.removeAction(items); // } else if (v == "multi_select") {
// } else if (v == "multi_select") { // _selectedItems.clear();
// _selectedItems.clear(); // model.toggleSelectMode();
// model.toggleSelectMode(); // }
// } // }),
// }), // onTap: () {
// onTap: () { // if (model.selectMode && !_selectedItems.isOtherPage(isLocal)) {
// if (model.selectMode && !_selectedItems.isOtherPage(isLocal)) { // if (selected) {
// if (selected) { // _selectedItems.remove(entries[index]);
// _selectedItems.remove(entries[index]); // } else {
// } else { // _selectedItems.add(isLocal, entries[index]);
// _selectedItems.add(isLocal, entries[index]); // }
// } // setState(() {});
// setState(() {}); // return;
// return; // }
// } // if (entries[index].isDirectory) {
// if (entries[index].isDirectory) { // openDirectory(entries[index].path, isLocal: isLocal);
// openDirectory(entries[index].path, isLocal: isLocal); // breadCrumbScrollToEnd(isLocal);
// breadCrumbScrollToEnd(isLocal); // } else {
// } else { // // Perform file-related tasks.
// // Perform file-related tasks. // }
// } // },
// }, // onLongPress: () {
// onLongPress: () { // _selectedItems.clear();
// _selectedItems.clear(); // model.toggleSelectMode();
// model.toggleSelectMode(); // if (model.selectMode) {
// if (model.selectMode) { // _selectedItems.add(isLocal, entries[index]);
// _selectedItems.add(isLocal, entries[index]); // }
// } // setState(() {});
// setState(() {}); // },
// }, // ),
// ), // );
// ); // },
// }, // ))
// )) ]),
]), ),
); );
} }
@ -831,4 +839,23 @@ class _FileManagerPageState extends State<FileManagerPage>
breadCrumbScrollToEnd(isLocal); breadCrumbScrollToEnd(isLocal);
}); });
} }
void handleDragDone(DropDoneDetails details, bool isLocal) {
if (isLocal) {
// ignore local
return;
}
var items = SelectedItems();
details.files.forEach((file) {
final f = File(file.path);
items.add(
true,
Entry()
..path = file.path
..name = file.name
..size =
FileSystemEntity.isDirectorySync(f.path) ? 0 : f.lengthSync());
});
model.sendFiles(items, isRemote: false);
}
} }

View File

@ -732,6 +732,7 @@ class FileModel extends ChangeNotifier {
job.totalSize = total_size.toInt(); job.totalSize = total_size.toInt();
} }
debugPrint("update folder files: ${info}"); debugPrint("update folder files: ${info}");
notifyListeners();
} }
bool get remoteSortAscending => _remoteSortAscending; bool get remoteSortAscending => _remoteSortAscending;

View File

@ -239,6 +239,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.0.12" version: "0.0.12"
desktop_drop:
dependency: "direct main"
description:
name: desktop_drop
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.3"
desktop_multi_window: desktop_multi_window:
dependency: "direct main" dependency: "direct main"
description: description:
@ -817,7 +824,7 @@ packages:
name: qr_code_scanner name: qr_code_scanner
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.0" version: "1.0.1"
quiver: quiver:
dependency: transitive dependency: transitive
description: description:

View File

@ -69,6 +69,7 @@ dependencies:
get: ^4.6.5 get: ^4.6.5
visibility_detector: ^0.3.3 visibility_detector: ^0.3.3
contextmenu: ^3.0.0 contextmenu: ^3.0.0
desktop_drop: ^0.3.3
dev_dependencies: dev_dependencies:
flutter_launcher_icons: ^0.9.1 flutter_launcher_icons: ^0.9.1