| 
									
										
										
										
											2022-03-07 22:54:34 +08:00
										 |  |  | import 'dart:async'; | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-03 14:58:57 +08:00
										 |  |  | import 'package:flutter/material.dart'; | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  | import 'package:flutter_breadcrumb/flutter_breadcrumb.dart'; | 
					
						
							| 
									
										
										
										
											2022-03-07 22:54:34 +08:00
										 |  |  | import 'package:flutter_hbb/models/file_model.dart'; | 
					
						
							|  |  |  | import 'package:provider/provider.dart'; | 
					
						
							| 
									
										
										
										
											2022-04-07 16:20:32 +08:00
										 |  |  | import 'package:toggle_switch/toggle_switch.dart'; | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  | import 'package:wakelock/wakelock.dart'; | 
					
						
							| 
									
										
										
										
											2022-03-03 14:58:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-24 23:33:00 +08:00
										 |  |  | import '../../common.dart'; | 
					
						
							| 
									
										
										
										
											2022-03-07 22:54:34 +08:00
										 |  |  | import '../widgets/dialog.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FileManagerPage extends StatefulWidget { | 
					
						
							|  |  |  |   FileManagerPage({Key? key, required this.id}) : super(key: key); | 
					
						
							|  |  |  |   final String id; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   State<StatefulWidget> createState() => _FileManagerPageState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _FileManagerPageState extends State<FileManagerPage> { | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |   final model = gFFI.fileModel; | 
					
						
							| 
									
										
										
										
											2022-03-11 01:28:13 +08:00
										 |  |  |   final _selectedItems = SelectedItems(); | 
					
						
							|  |  |  |   final _breadCrumbScroller = ScrollController(); | 
					
						
							| 
									
										
										
										
											2022-03-07 22:54:34 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							|  |  |  |     super.initState(); | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |     gFFI.start(widget.id, isFileTransfer: true); | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |     WidgetsBinding.instance.addPostFrameCallback((_) { | 
					
						
							|  |  |  |       gFFI.dialogManager | 
					
						
							| 
									
										
										
										
											2022-08-16 21:27:21 +08:00
										 |  |  |           .showLoading(translate('Connecting...'), onCancel: closeConnection); | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |     gFFI.ffiModel.updateEventListener(widget.id); | 
					
						
							| 
									
										
										
										
											2022-12-04 22:41:44 +09:00
										 |  |  |     model.onDirChanged = (_) => breadCrumbScrollToEnd(); | 
					
						
							| 
									
										
										
										
											2022-04-19 14:07:46 +08:00
										 |  |  |     Wakelock.enable(); | 
					
						
							| 
									
										
										
										
											2022-03-07 22:54:34 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void dispose() { | 
					
						
							| 
									
										
										
										
											2022-12-04 22:41:44 +09:00
										 |  |  |     model.onClose().whenComplete(() { | 
					
						
							|  |  |  |       gFFI.close(); | 
					
						
							|  |  |  |       gFFI.dialogManager.dismissAll(); | 
					
						
							|  |  |  |       Wakelock.disable(); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2022-03-07 22:54:34 +08:00
										 |  |  |     super.dispose(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-03-03 14:58:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							| 
									
										
										
										
											2022-03-25 16:34:27 +08:00
										 |  |  |   Widget build(BuildContext context) => ChangeNotifierProvider.value( | 
					
						
							| 
									
										
										
										
											2022-10-19 22:52:02 +09:00
										 |  |  |       value: model, | 
					
						
							| 
									
										
										
										
											2022-03-25 16:34:27 +08:00
										 |  |  |       child: Consumer<FileModel>(builder: (_context, _model, _child) { | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |         return WillPopScope( | 
					
						
							|  |  |  |             onWillPop: () async { | 
					
						
							|  |  |  |               if (model.selectMode) { | 
					
						
							|  |  |  |                 model.toggleSelectMode(); | 
					
						
							|  |  |  |               } else { | 
					
						
							| 
									
										
										
										
											2022-11-03 00:01:59 +09:00
										 |  |  |                 model.goBack(); | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |               } | 
					
						
							|  |  |  |               return false; | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             child: Scaffold( | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |               // backgroundColor: MyTheme.grayBg,
 | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |               appBar: AppBar( | 
					
						
							|  |  |  |                 leading: Row(children: [ | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |                   IconButton( | 
					
						
							|  |  |  |                       icon: Icon(Icons.close), | 
					
						
							| 
									
										
										
										
											2022-11-15 16:49:55 +08:00
										 |  |  |                       onPressed: () => | 
					
						
							|  |  |  |                           clientClose(widget.id, gFFI.dialogManager)), | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |                 ]), | 
					
						
							|  |  |  |                 centerTitle: true, | 
					
						
							| 
									
										
										
										
											2022-04-07 16:20:32 +08:00
										 |  |  |                 title: ToggleSwitch( | 
					
						
							|  |  |  |                   initialLabelIndex: model.isLocal ? 0 : 1, | 
					
						
							|  |  |  |                   activeBgColor: [MyTheme.idColor], | 
					
						
							| 
									
										
										
										
											2022-10-19 11:49:32 +09:00
										 |  |  |                   inactiveBgColor: | 
					
						
							|  |  |  |                       Theme.of(context).brightness == Brightness.light | 
					
						
							|  |  |  |                           ? MyTheme.grayBg | 
					
						
							|  |  |  |                           : null, | 
					
						
							|  |  |  |                   inactiveFgColor: | 
					
						
							|  |  |  |                       Theme.of(context).brightness == Brightness.light | 
					
						
							|  |  |  |                           ? Colors.black54 | 
					
						
							|  |  |  |                           : null, | 
					
						
							| 
									
										
										
										
											2022-04-07 16:20:32 +08:00
										 |  |  |                   totalSwitches: 2, | 
					
						
							|  |  |  |                   minWidth: 100, | 
					
						
							|  |  |  |                   fontSize: 15, | 
					
						
							|  |  |  |                   iconSize: 18, | 
					
						
							|  |  |  |                   labels: [translate("Local"), translate("Remote")], | 
					
						
							|  |  |  |                   icons: [Icons.phone_android_sharp, Icons.screen_share], | 
					
						
							|  |  |  |                   onToggle: (index) { | 
					
						
							|  |  |  |                     final current = model.isLocal ? 0 : 1; | 
					
						
							|  |  |  |                     if (index != current) { | 
					
						
							|  |  |  |                       model.togglePage(); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                   }, | 
					
						
							|  |  |  |                 ), | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |                 actions: [ | 
					
						
							|  |  |  |                   PopupMenuButton<String>( | 
					
						
							|  |  |  |                       icon: Icon(Icons.more_vert), | 
					
						
							|  |  |  |                       itemBuilder: (context) { | 
					
						
							|  |  |  |                         return [ | 
					
						
							|  |  |  |                           PopupMenuItem( | 
					
						
							|  |  |  |                             child: Row( | 
					
						
							|  |  |  |                               children: [ | 
					
						
							| 
									
										
										
										
											2022-10-19 11:49:32 +09:00
										 |  |  |                                 Icon(Icons.refresh, | 
					
						
							|  |  |  |                                     color: Theme.of(context).iconTheme.color), | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |                                 SizedBox(width: 5), | 
					
						
							|  |  |  |                                 Text(translate("Refresh File")) | 
					
						
							|  |  |  |                               ], | 
					
						
							|  |  |  |                             ), | 
					
						
							|  |  |  |                             value: "refresh", | 
					
						
							|  |  |  |                           ), | 
					
						
							|  |  |  |                           PopupMenuItem( | 
					
						
							| 
									
										
										
										
											2022-10-19 22:52:02 +09:00
										 |  |  |                             enabled: model.currentDir.path != "/", | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |                             child: Row( | 
					
						
							|  |  |  |                               children: [ | 
					
						
							| 
									
										
										
										
											2022-10-19 11:49:32 +09:00
										 |  |  |                                 Icon(Icons.check, | 
					
						
							|  |  |  |                                     color: Theme.of(context).iconTheme.color), | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |                                 SizedBox(width: 5), | 
					
						
							|  |  |  |                                 Text(translate("Multi Select")) | 
					
						
							|  |  |  |                               ], | 
					
						
							|  |  |  |                             ), | 
					
						
							|  |  |  |                             value: "select", | 
					
						
							|  |  |  |                           ), | 
					
						
							|  |  |  |                           PopupMenuItem( | 
					
						
							| 
									
										
										
										
											2022-10-19 22:52:02 +09:00
										 |  |  |                             enabled: model.currentDir.path != "/", | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |                             child: Row( | 
					
						
							|  |  |  |                               children: [ | 
					
						
							|  |  |  |                                 Icon(Icons.folder_outlined, | 
					
						
							| 
									
										
										
										
											2022-10-19 11:49:32 +09:00
										 |  |  |                                     color: Theme.of(context).iconTheme.color), | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |                                 SizedBox(width: 5), | 
					
						
							|  |  |  |                                 Text(translate("Create Folder")) | 
					
						
							|  |  |  |                               ], | 
					
						
							|  |  |  |                             ), | 
					
						
							|  |  |  |                             value: "folder", | 
					
						
							|  |  |  |                           ), | 
					
						
							|  |  |  |                           PopupMenuItem( | 
					
						
							| 
									
										
										
										
											2022-10-19 22:52:02 +09:00
										 |  |  |                             enabled: model.currentDir.path != "/", | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |                             child: Row( | 
					
						
							|  |  |  |                               children: [ | 
					
						
							|  |  |  |                                 Icon( | 
					
						
							| 
									
										
										
										
											2022-12-04 23:44:03 +09:00
										 |  |  |                                     model.getCurrentShowHidden() | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |                                         ? Icons.check_box_outlined | 
					
						
							|  |  |  |                                         : Icons.check_box_outline_blank, | 
					
						
							| 
									
										
										
										
											2022-10-19 11:49:32 +09:00
										 |  |  |                                     color: Theme.of(context).iconTheme.color), | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |                                 SizedBox(width: 5), | 
					
						
							|  |  |  |                                 Text(translate("Show Hidden Files")) | 
					
						
							|  |  |  |                               ], | 
					
						
							|  |  |  |                             ), | 
					
						
							|  |  |  |                             value: "hidden", | 
					
						
							|  |  |  |                           ) | 
					
						
							|  |  |  |                         ]; | 
					
						
							|  |  |  |                       }, | 
					
						
							|  |  |  |                       onSelected: (v) { | 
					
						
							|  |  |  |                         if (v == "refresh") { | 
					
						
							|  |  |  |                           model.refresh(); | 
					
						
							|  |  |  |                         } else if (v == "select") { | 
					
						
							|  |  |  |                           _selectedItems.clear(); | 
					
						
							|  |  |  |                           model.toggleSelectMode(); | 
					
						
							|  |  |  |                         } else if (v == "folder") { | 
					
						
							|  |  |  |                           final name = TextEditingController(); | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |                           gFFI.dialogManager | 
					
						
							|  |  |  |                               .show((setState, close) => CustomAlertDialog( | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |                                       title: Text(translate("Create Folder")), | 
					
						
							|  |  |  |                                       content: Column( | 
					
						
							|  |  |  |                                         mainAxisSize: MainAxisSize.min, | 
					
						
							|  |  |  |                                         children: [ | 
					
						
							|  |  |  |                                           TextFormField( | 
					
						
							|  |  |  |                                             decoration: InputDecoration( | 
					
						
							|  |  |  |                                               labelText: translate( | 
					
						
							|  |  |  |                                                   "Please enter the folder name"), | 
					
						
							|  |  |  |                                             ), | 
					
						
							|  |  |  |                                             controller: name, | 
					
						
							|  |  |  |                                           ), | 
					
						
							|  |  |  |                                         ], | 
					
						
							|  |  |  |                                       ), | 
					
						
							|  |  |  |                                       actions: [ | 
					
						
							|  |  |  |                                         TextButton( | 
					
						
							|  |  |  |                                             style: flatButtonStyle, | 
					
						
							|  |  |  |                                             onPressed: () => close(false), | 
					
						
							|  |  |  |                                             child: Text(translate("Cancel"))), | 
					
						
							|  |  |  |                                         ElevatedButton( | 
					
						
							|  |  |  |                                             style: flatButtonStyle, | 
					
						
							|  |  |  |                                             onPressed: () { | 
					
						
							|  |  |  |                                               if (name.value.text.isNotEmpty) { | 
					
						
							|  |  |  |                                                 model.createDir(PathUtil.join( | 
					
						
							|  |  |  |                                                     model.currentDir.path, | 
					
						
							|  |  |  |                                                     name.value.text, | 
					
						
							| 
									
										
										
										
											2022-12-04 23:44:03 +09:00
										 |  |  |                                                     model | 
					
						
							|  |  |  |                                                         .getCurrentIsWindows())); | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |                                                 close(); | 
					
						
							|  |  |  |                                               } | 
					
						
							|  |  |  |                                             }, | 
					
						
							|  |  |  |                                             child: Text(translate("OK"))) | 
					
						
							|  |  |  |                                       ])); | 
					
						
							|  |  |  |                         } else if (v == "hidden") { | 
					
						
							|  |  |  |                           model.toggleShowHidden(); | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                       }), | 
					
						
							|  |  |  |                 ], | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |               ), | 
					
						
							|  |  |  |               body: body(), | 
					
						
							|  |  |  |               bottomSheet: bottomSheet(), | 
					
						
							|  |  |  |             )); | 
					
						
							| 
									
										
										
										
											2022-03-25 16:34:27 +08:00
										 |  |  |       })); | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-19 11:49:32 +09:00
										 |  |  |   bool showCheckBox() { | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |     if (!model.selectMode) { | 
					
						
							| 
									
										
										
										
											2022-03-11 01:28:13 +08:00
										 |  |  |       return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return !_selectedItems.isOtherPage(model.isLocal); | 
					
						
							| 
									
										
										
										
											2022-03-07 22:54:34 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-11 01:28:13 +08:00
										 |  |  |   Widget body() { | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |     final isLocal = model.isLocal; | 
					
						
							|  |  |  |     final fd = model.currentDir; | 
					
						
							|  |  |  |     final entries = fd.entries; | 
					
						
							|  |  |  |     return Column(children: [ | 
					
						
							|  |  |  |       headTools(), | 
					
						
							|  |  |  |       Expanded( | 
					
						
							|  |  |  |           child: ListView.builder( | 
					
						
							| 
									
										
										
										
											2022-09-12 11:23:45 +08:00
										 |  |  |         controller: ScrollController(), | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |         itemCount: entries.length + 1, | 
					
						
							|  |  |  |         itemBuilder: (context, index) { | 
					
						
							|  |  |  |           if (index >= entries.length) { | 
					
						
							|  |  |  |             return listTail(); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           var selected = false; | 
					
						
							|  |  |  |           if (model.selectMode) { | 
					
						
							|  |  |  |             selected = _selectedItems.contains(entries[index]); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2022-03-17 21:03:52 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |           final sizeStr = entries[index].isFile | 
					
						
							|  |  |  |               ? readableFileSize(entries[index].size.toDouble()) | 
					
						
							|  |  |  |               : ""; | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |           return Card( | 
					
						
							|  |  |  |             child: ListTile( | 
					
						
							| 
									
										
										
										
											2022-10-20 10:31:31 +09:00
										 |  |  |               leading: entries[index].isDrive | 
					
						
							|  |  |  |                   ? Padding( | 
					
						
							|  |  |  |                       padding: EdgeInsets.symmetric(vertical: 8), | 
					
						
							|  |  |  |                       child: Image( | 
					
						
							|  |  |  |                           image: iconHardDrive, | 
					
						
							|  |  |  |                           fit: BoxFit.scaleDown, | 
					
						
							|  |  |  |                           color: Theme.of(context) | 
					
						
							|  |  |  |                               .iconTheme | 
					
						
							|  |  |  |                               .color | 
					
						
							|  |  |  |                               ?.withOpacity(0.7))) | 
					
						
							|  |  |  |                   : Icon( | 
					
						
							|  |  |  |                       entries[index].isFile | 
					
						
							|  |  |  |                           ? Icons.feed_outlined | 
					
						
							| 
									
										
										
										
											2022-10-19 11:49:32 +09:00
										 |  |  |                           : Icons.folder, | 
					
						
							| 
									
										
										
										
											2022-10-20 10:31:31 +09:00
										 |  |  |                       size: 40), | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |               title: Text(entries[index].name), | 
					
						
							|  |  |  |               selected: selected, | 
					
						
							| 
									
										
										
										
											2022-10-20 10:31:31 +09:00
										 |  |  |               subtitle: entries[index].isDrive | 
					
						
							|  |  |  |                   ? null | 
					
						
							|  |  |  |                   : Text( | 
					
						
							|  |  |  |                       "${entries[index].lastModified().toString().replaceAll(".000", "")}   $sizeStr", | 
					
						
							|  |  |  |                       style: TextStyle(fontSize: 12, color: MyTheme.darkGray), | 
					
						
							|  |  |  |                     ), | 
					
						
							| 
									
										
										
										
											2022-10-19 11:49:32 +09:00
										 |  |  |               trailing: entries[index].isDrive | 
					
						
							|  |  |  |                   ? null | 
					
						
							|  |  |  |                   : showCheckBox() | 
					
						
							|  |  |  |                       ? Checkbox( | 
					
						
							|  |  |  |                           value: selected, | 
					
						
							|  |  |  |                           onChanged: (v) { | 
					
						
							|  |  |  |                             if (v == null) return; | 
					
						
							|  |  |  |                             if (v && !selected) { | 
					
						
							|  |  |  |                               _selectedItems.add(isLocal, entries[index]); | 
					
						
							|  |  |  |                             } else if (!v && selected) { | 
					
						
							|  |  |  |                               _selectedItems.remove(entries[index]); | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                             setState(() {}); | 
					
						
							|  |  |  |                           }) | 
					
						
							|  |  |  |                       : PopupMenuButton<String>( | 
					
						
							|  |  |  |                           icon: Icon(Icons.more_vert), | 
					
						
							|  |  |  |                           itemBuilder: (context) { | 
					
						
							|  |  |  |                             return [ | 
					
						
							|  |  |  |                               PopupMenuItem( | 
					
						
							|  |  |  |                                 child: Text(translate("Delete")), | 
					
						
							|  |  |  |                                 value: "delete", | 
					
						
							|  |  |  |                               ), | 
					
						
							|  |  |  |                               PopupMenuItem( | 
					
						
							|  |  |  |                                 child: Text(translate("Multi Select")), | 
					
						
							|  |  |  |                                 value: "multi_select", | 
					
						
							|  |  |  |                               ), | 
					
						
							|  |  |  |                               PopupMenuItem( | 
					
						
							|  |  |  |                                 child: Text(translate("Properties")), | 
					
						
							|  |  |  |                                 value: "properties", | 
					
						
							|  |  |  |                                 enabled: false, | 
					
						
							|  |  |  |                               ) | 
					
						
							|  |  |  |                             ]; | 
					
						
							|  |  |  |                           }, | 
					
						
							|  |  |  |                           onSelected: (v) { | 
					
						
							|  |  |  |                             if (v == "delete") { | 
					
						
							|  |  |  |                               final items = SelectedItems(); | 
					
						
							|  |  |  |                               items.add(isLocal, entries[index]); | 
					
						
							|  |  |  |                               model.removeAction(items); | 
					
						
							|  |  |  |                             } else if (v == "multi_select") { | 
					
						
							|  |  |  |                               _selectedItems.clear(); | 
					
						
							|  |  |  |                               model.toggleSelectMode(); | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                           }), | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |               onTap: () { | 
					
						
							|  |  |  |                 if (model.selectMode && !_selectedItems.isOtherPage(isLocal)) { | 
					
						
							|  |  |  |                   if (selected) { | 
					
						
							|  |  |  |                     _selectedItems.remove(entries[index]); | 
					
						
							|  |  |  |                   } else { | 
					
						
							|  |  |  |                     _selectedItems.add(isLocal, entries[index]); | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                   setState(() {}); | 
					
						
							|  |  |  |                   return; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2022-10-19 11:49:32 +09:00
										 |  |  |                 if (entries[index].isDirectory || entries[index].isDrive) { | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |                   model.openDirectory(entries[index].path); | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                   // Perform file-related tasks.
 | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               }, | 
					
						
							| 
									
										
										
										
											2022-10-19 11:49:32 +09:00
										 |  |  |               onLongPress: entries[index].isDrive | 
					
						
							|  |  |  |                   ? null | 
					
						
							|  |  |  |                   : () { | 
					
						
							|  |  |  |                       _selectedItems.clear(); | 
					
						
							|  |  |  |                       model.toggleSelectMode(); | 
					
						
							|  |  |  |                       if (model.selectMode) { | 
					
						
							|  |  |  |                         _selectedItems.add(isLocal, entries[index]); | 
					
						
							|  |  |  |                       } | 
					
						
							|  |  |  |                       setState(() {}); | 
					
						
							|  |  |  |                     }, | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |             ), | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       )) | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |     ]); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-03-09 17:07:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-11 01:28:13 +08:00
										 |  |  |   breadCrumbScrollToEnd() { | 
					
						
							|  |  |  |     Future.delayed(Duration(milliseconds: 200), () { | 
					
						
							| 
									
										
										
										
											2022-12-05 21:57:08 +09:00
										 |  |  |       if (_breadCrumbScroller.hasClients) { | 
					
						
							|  |  |  |         _breadCrumbScroller.animateTo( | 
					
						
							|  |  |  |             _breadCrumbScroller.position.maxScrollExtent, | 
					
						
							|  |  |  |             duration: Duration(milliseconds: 200), | 
					
						
							|  |  |  |             curve: Curves.fastLinearToSlowEaseIn); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-03-11 01:28:13 +08:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |   Widget headTools() => Container( | 
					
						
							| 
									
										
										
										
											2022-03-09 17:07:24 +08:00
										 |  |  |           child: Row( | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |         children: [ | 
					
						
							|  |  |  |           Expanded( | 
					
						
							|  |  |  |               child: BreadCrumb( | 
					
						
							| 
									
										
										
										
											2022-03-17 21:03:52 +08:00
										 |  |  |             items: getPathBreadCrumbItems(() => model.goHome(), (list) { | 
					
						
							|  |  |  |               var path = ""; | 
					
						
							|  |  |  |               if (model.currentHome.startsWith(list[0])) { | 
					
						
							|  |  |  |                 // absolute path
 | 
					
						
							|  |  |  |                 for (var item in list) { | 
					
						
							| 
									
										
										
										
											2022-12-04 23:44:03 +09:00
										 |  |  |                   path = PathUtil.join(path, item, model.getCurrentIsWindows()); | 
					
						
							| 
									
										
										
										
											2022-03-17 21:03:52 +08:00
										 |  |  |                 } | 
					
						
							|  |  |  |               } else { | 
					
						
							|  |  |  |                 path += model.currentHome; | 
					
						
							|  |  |  |                 for (var item in list) { | 
					
						
							| 
									
										
										
										
											2022-12-04 23:44:03 +09:00
										 |  |  |                   path = PathUtil.join(path, item, model.getCurrentIsWindows()); | 
					
						
							| 
									
										
										
										
											2022-03-17 21:03:52 +08:00
										 |  |  |                 } | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |               model.openDirectory(path); | 
					
						
							|  |  |  |             }), | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |             divider: Icon(Icons.chevron_right), | 
					
						
							|  |  |  |             overflow: ScrollableOverflow(controller: _breadCrumbScroller), | 
					
						
							|  |  |  |           )), | 
					
						
							|  |  |  |           Row( | 
					
						
							| 
									
										
										
										
											2022-03-09 17:07:24 +08:00
										 |  |  |             children: [ | 
					
						
							| 
									
										
										
										
											2022-11-03 00:01:59 +09:00
										 |  |  |               IconButton( | 
					
						
							|  |  |  |                 icon: Icon(Icons.arrow_back), | 
					
						
							|  |  |  |                 onPressed: model.goBack, | 
					
						
							|  |  |  |               ), | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |               IconButton( | 
					
						
							|  |  |  |                 icon: Icon(Icons.arrow_upward), | 
					
						
							| 
									
										
										
										
											2022-11-03 00:01:59 +09:00
										 |  |  |                 onPressed: model.goToParentDirectory, | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |               ), | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |               PopupMenuButton<SortBy>( | 
					
						
							|  |  |  |                   icon: Icon(Icons.sort), | 
					
						
							|  |  |  |                   itemBuilder: (context) { | 
					
						
							|  |  |  |                     return SortBy.values | 
					
						
							|  |  |  |                         .map((e) => PopupMenuItem( | 
					
						
							| 
									
										
										
										
											2022-10-20 10:31:31 +09:00
										 |  |  |                               child: Text(translate(e.toString())), | 
					
						
							| 
									
										
										
										
											2022-03-09 22:43:05 +08:00
										 |  |  |                               value: e, | 
					
						
							|  |  |  |                             )) | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |                         .toList(); | 
					
						
							|  |  |  |                   }, | 
					
						
							|  |  |  |                   onSelected: model.changeSortStyle), | 
					
						
							| 
									
										
										
										
											2022-03-09 17:07:24 +08:00
										 |  |  |             ], | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |           ) | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |       )); | 
					
						
							| 
									
										
										
										
											2022-03-09 17:07:24 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   Widget listTail() { | 
					
						
							| 
									
										
										
										
											2022-03-17 21:03:52 +08:00
										 |  |  |     return Container( | 
					
						
							|  |  |  |       height: 100, | 
					
						
							|  |  |  |       child: Column( | 
					
						
							|  |  |  |         children: [ | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |           Padding( | 
					
						
							| 
									
										
										
										
											2022-04-07 22:58:47 +08:00
										 |  |  |             padding: EdgeInsets.fromLTRB(30, 5, 30, 0), | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |             child: Text( | 
					
						
							|  |  |  |               model.currentDir.path, | 
					
						
							|  |  |  |               style: TextStyle(color: MyTheme.darkGray), | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ), | 
					
						
							| 
									
										
										
										
											2022-03-17 21:03:52 +08:00
										 |  |  |           Padding( | 
					
						
							|  |  |  |             padding: EdgeInsets.all(2), | 
					
						
							|  |  |  |             child: Text( | 
					
						
							| 
									
										
										
										
											2022-04-20 23:11:04 +08:00
										 |  |  |               "${translate("Total")}: ${model.currentDir.entries.length} ${translate("items")}", | 
					
						
							| 
									
										
										
										
											2022-03-17 21:03:52 +08:00
										 |  |  |               style: TextStyle(color: MyTheme.darkGray), | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ) | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-03-09 17:07:24 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |   Widget? bottomSheet() { | 
					
						
							|  |  |  |     final state = model.jobState; | 
					
						
							|  |  |  |     final isOtherPage = _selectedItems.isOtherPage(model.isLocal); | 
					
						
							| 
									
										
										
										
											2022-03-25 16:34:27 +08:00
										 |  |  |     final selectedItemsLen = "${_selectedItems.length} ${translate("items")}"; | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |     final local = _selectedItems.isLocal == null | 
					
						
							|  |  |  |         ? "" | 
					
						
							| 
									
										
										
										
											2022-03-23 15:28:21 +08:00
										 |  |  |         : " [${_selectedItems.isLocal! ? translate("Local") : translate("Remote")}]"; | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (model.selectMode) { | 
					
						
							|  |  |  |       if (_selectedItems.length == 0 || !isOtherPage) { | 
					
						
							|  |  |  |         return BottomSheetBody( | 
					
						
							|  |  |  |             leading: Icon(Icons.check), | 
					
						
							| 
									
										
										
										
											2022-03-23 15:28:21 +08:00
										 |  |  |             title: translate("Selected"), | 
					
						
							| 
									
										
										
										
											2022-03-25 16:34:27 +08:00
										 |  |  |             text: selectedItemsLen + local, | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |             onCanceled: () => model.toggleSelectMode(), | 
					
						
							|  |  |  |             actions: [ | 
					
						
							| 
									
										
										
										
											2022-04-07 23:19:57 +08:00
										 |  |  |               IconButton( | 
					
						
							|  |  |  |                 icon: Icon(Icons.compare_arrows), | 
					
						
							|  |  |  |                 onPressed: model.togglePage, | 
					
						
							|  |  |  |               ), | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |               IconButton( | 
					
						
							|  |  |  |                 icon: Icon(Icons.delete_forever), | 
					
						
							|  |  |  |                 onPressed: () { | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |                   if (_selectedItems.length > 0) { | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |                     model.removeAction(_selectedItems); | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |               ) | 
					
						
							|  |  |  |             ]); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         return BottomSheetBody( | 
					
						
							|  |  |  |             leading: Icon(Icons.input), | 
					
						
							| 
									
										
										
										
											2022-03-23 15:28:21 +08:00
										 |  |  |             title: translate("Paste here?"), | 
					
						
							| 
									
										
										
										
											2022-03-25 16:34:27 +08:00
										 |  |  |             text: selectedItemsLen + local, | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |             onCanceled: () => model.toggleSelectMode(), | 
					
						
							|  |  |  |             actions: [ | 
					
						
							| 
									
										
										
										
											2022-04-07 23:19:57 +08:00
										 |  |  |               IconButton( | 
					
						
							|  |  |  |                 icon: Icon(Icons.compare_arrows), | 
					
						
							|  |  |  |                 onPressed: model.togglePage, | 
					
						
							|  |  |  |               ), | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |               IconButton( | 
					
						
							|  |  |  |                 icon: Icon(Icons.paste), | 
					
						
							|  |  |  |                 onPressed: () { | 
					
						
							|  |  |  |                   model.toggleSelectMode(); | 
					
						
							|  |  |  |                   model.sendFiles(_selectedItems); | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |               ) | 
					
						
							|  |  |  |             ]); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (state) { | 
					
						
							|  |  |  |       case JobState.inProgress: | 
					
						
							|  |  |  |         return BottomSheetBody( | 
					
						
							|  |  |  |           leading: CircularProgressIndicator(), | 
					
						
							| 
									
										
										
										
											2022-03-23 15:28:21 +08:00
										 |  |  |           title: translate("Waiting"), | 
					
						
							| 
									
										
										
										
											2022-03-25 16:34:27 +08:00
										 |  |  |           text: | 
					
						
							|  |  |  |               "${translate("Speed")}:  ${readableFileSize(model.jobProgress.speed)}/s", | 
					
						
							| 
									
										
										
										
											2022-04-07 22:58:47 +08:00
										 |  |  |           onCanceled: () => model.cancelJob(model.jobProgress.id), | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |         ); | 
					
						
							|  |  |  |       case JobState.done: | 
					
						
							|  |  |  |         return BottomSheetBody( | 
					
						
							|  |  |  |           leading: Icon(Icons.check), | 
					
						
							| 
									
										
										
										
											2022-03-23 15:28:21 +08:00
										 |  |  |           title: "${translate("Successful")}!", | 
					
						
							| 
									
										
										
										
											2022-12-05 21:57:08 +09:00
										 |  |  |           text: model.jobProgress.display(), | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |           onCanceled: () => model.jobReset(), | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       case JobState.error: | 
					
						
							|  |  |  |         return BottomSheetBody( | 
					
						
							|  |  |  |           leading: Icon(Icons.error), | 
					
						
							| 
									
										
										
										
											2022-03-23 15:28:21 +08:00
										 |  |  |           title: "${translate("Error")}!", | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |           text: "", | 
					
						
							|  |  |  |           onCanceled: () => model.jobReset(), | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       case JobState.none: | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |       case JobState.paused: | 
					
						
							|  |  |  |         // TODO: Handle this case.
 | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |   List<BreadCrumbItem> getPathBreadCrumbItems( | 
					
						
							| 
									
										
										
										
											2022-03-17 21:03:52 +08:00
										 |  |  |       void Function() onHome, void Function(List<String>) onPressed) { | 
					
						
							|  |  |  |     final path = model.currentShortPath; | 
					
						
							| 
									
										
										
										
											2022-12-04 23:44:03 +09:00
										 |  |  |     final list = PathUtil.split(path, model.getCurrentIsWindows()); | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |     final breadCrumbList = [ | 
					
						
							|  |  |  |       BreadCrumbItem( | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |           content: IconButton( | 
					
						
							|  |  |  |         icon: Icon(Icons.home_filled), | 
					
						
							|  |  |  |         onPressed: onHome, | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |       )) | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2022-03-17 21:03:52 +08:00
										 |  |  |     breadCrumbList.addAll(list.asMap().entries.map((e) => BreadCrumbItem( | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |         content: TextButton( | 
					
						
							| 
									
										
										
										
											2022-03-17 21:03:52 +08:00
										 |  |  |             child: Text(e.value), | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |             style: | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |                 ButtonStyle(minimumSize: MaterialStateProperty.all(Size(0, 0))), | 
					
						
							| 
									
										
										
										
											2022-03-17 21:03:52 +08:00
										 |  |  |             onPressed: () => onPressed(list.sublist(0, e.key + 1)))))); | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |     return breadCrumbList; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class BottomSheetBody extends StatelessWidget { | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |   BottomSheetBody( | 
					
						
							|  |  |  |       {required this.leading, | 
					
						
							|  |  |  |       required this.title, | 
					
						
							|  |  |  |       required this.text, | 
					
						
							|  |  |  |       this.onCanceled, | 
					
						
							|  |  |  |       this.actions}); | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   final Widget leading; | 
					
						
							|  |  |  |   final String title; | 
					
						
							|  |  |  |   final String text; | 
					
						
							|  |  |  |   final VoidCallback? onCanceled; | 
					
						
							|  |  |  |   final List<IconButton>? actions; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   BottomSheet build(BuildContext context) { | 
					
						
							|  |  |  |     final _actions = actions ?? []; | 
					
						
							| 
									
										
										
										
											2022-03-09 17:07:24 +08:00
										 |  |  |     return BottomSheet( | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |       builder: (BuildContext context) { | 
					
						
							|  |  |  |         return Container( | 
					
						
							| 
									
										
										
										
											2022-03-09 17:07:24 +08:00
										 |  |  |             height: 65, | 
					
						
							|  |  |  |             alignment: Alignment.centerLeft, | 
					
						
							|  |  |  |             decoration: BoxDecoration( | 
					
						
							|  |  |  |                 color: MyTheme.accent50, | 
					
						
							|  |  |  |                 borderRadius: BorderRadius.vertical(top: Radius.circular(10))), | 
					
						
							|  |  |  |             child: Padding( | 
					
						
							|  |  |  |               padding: EdgeInsets.symmetric(horizontal: 15), | 
					
						
							|  |  |  |               child: Row( | 
					
						
							|  |  |  |                 mainAxisAlignment: MainAxisAlignment.spaceBetween, | 
					
						
							|  |  |  |                 children: [ | 
					
						
							|  |  |  |                   Row( | 
					
						
							|  |  |  |                     children: [ | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |                       leading, | 
					
						
							| 
									
										
										
										
											2022-03-11 01:28:13 +08:00
										 |  |  |                       SizedBox(width: 16), | 
					
						
							|  |  |  |                       Column( | 
					
						
							|  |  |  |                         mainAxisAlignment: MainAxisAlignment.center, | 
					
						
							|  |  |  |                         crossAxisAlignment: CrossAxisAlignment.start, | 
					
						
							|  |  |  |                         children: [ | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |                           Text(title, style: TextStyle(fontSize: 18)), | 
					
						
							|  |  |  |                           Text(text, | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |                               style: TextStyle(fontSize: 14)) // TODO color
 | 
					
						
							| 
									
										
										
										
											2022-03-11 01:28:13 +08:00
										 |  |  |                         ], | 
					
						
							|  |  |  |                       ) | 
					
						
							| 
									
										
										
										
											2022-03-09 17:07:24 +08:00
										 |  |  |                     ], | 
					
						
							|  |  |  |                   ), | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |                   Row(children: () { | 
					
						
							|  |  |  |                     _actions.add(IconButton( | 
					
						
							|  |  |  |                       icon: Icon(Icons.cancel_outlined), | 
					
						
							|  |  |  |                       onPressed: onCanceled, | 
					
						
							|  |  |  |                     )); | 
					
						
							|  |  |  |                     return _actions; | 
					
						
							|  |  |  |                   }()) | 
					
						
							| 
									
										
										
										
											2022-03-09 17:07:24 +08:00
										 |  |  |                 ], | 
					
						
							|  |  |  |               ), | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |             )); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       onClosing: () {}, | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |       // backgroundColor: MyTheme.grayBg,
 | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |       enableDrag: false, | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-03-09 17:07:24 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-03-11 01:28:13 +08:00
										 |  |  | } |