| 
									
										
										
										
											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'; | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  | import 'package:get/get.dart'; | 
					
						
							| 
									
										
										
										
											2022-04-07 16:20:32 +08:00
										 |  |  | import 'package:toggle_switch/toggle_switch.dart'; | 
					
						
							| 
									
										
										
										
											2023-10-31 21:10:23 +08:00
										 |  |  | import 'package:wakelock_plus/wakelock_plus.dart'; | 
					
						
							| 
									
										
										
										
											2022-03-03 14:58:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-24 23:33:00 +08:00
										 |  |  | import '../../common.dart'; | 
					
						
							| 
									
										
										
										
											2023-03-24 15:21:14 +08:00
										 |  |  | import '../../common/widgets/dialog.dart'; | 
					
						
							| 
									
										
										
										
											2022-03-07 22:54:34 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | class FileManagerPage extends StatefulWidget { | 
					
						
							| 
									
										
										
										
											2024-03-20 15:05:54 +08:00
										 |  |  |   FileManagerPage( | 
					
						
							|  |  |  |       {Key? key, required this.id, this.password, this.isSharedPassword}) | 
					
						
							|  |  |  |       : super(key: key); | 
					
						
							| 
									
										
										
										
											2022-03-07 22:54:34 +08:00
										 |  |  |   final String id; | 
					
						
							| 
									
										
										
										
											2024-03-20 15:05:54 +08:00
										 |  |  |   final String? password; | 
					
						
							|  |  |  |   final bool? isSharedPassword; | 
					
						
							| 
									
										
										
										
											2022-03-07 22:54:34 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   State<StatefulWidget> createState() => _FileManagerPageState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  | enum SelectMode { local, remote, none } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | extension SelectModeEq on SelectMode { | 
					
						
							|  |  |  |   bool eq(bool? currentIsLocal) { | 
					
						
							|  |  |  |     if (currentIsLocal == null) { | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (currentIsLocal) { | 
					
						
							|  |  |  |       return this == SelectMode.local; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       return this == SelectMode.remote; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | extension SelectModeExt on Rx<SelectMode> { | 
					
						
							|  |  |  |   void toggle(bool currentIsLocal) { | 
					
						
							|  |  |  |     switch (value) { | 
					
						
							|  |  |  |       case SelectMode.local: | 
					
						
							|  |  |  |         value = SelectMode.none; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       case SelectMode.remote: | 
					
						
							|  |  |  |         value = SelectMode.none; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       case SelectMode.none: | 
					
						
							|  |  |  |         if (currentIsLocal) { | 
					
						
							|  |  |  |           value = SelectMode.local; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           value = SelectMode.remote; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-07 22:54:34 +08:00
										 |  |  | class _FileManagerPageState extends State<FileManagerPage> { | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |   final model = gFFI.fileModel; | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |   final selectMode = SelectMode.none.obs; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |   var showLocal = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FileController get currentFileController => | 
					
						
							|  |  |  |       showLocal ? model.localController : model.remoteController; | 
					
						
							|  |  |  |   FileDirectory get currentDir => currentFileController.directory.value; | 
					
						
							|  |  |  |   DirectoryOptions get currentOptions => currentFileController.options.value; | 
					
						
							| 
									
										
										
										
											2022-03-07 22:54:34 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							|  |  |  |     super.initState(); | 
					
						
							| 
									
										
										
										
											2024-03-20 15:05:54 +08:00
										 |  |  |     gFFI.start(widget.id, | 
					
						
							|  |  |  |         isFileTransfer: true, | 
					
						
							|  |  |  |         password: widget.password, | 
					
						
							|  |  |  |         isSharedPassword: widget.isSharedPassword); | 
					
						
							| 
									
										
										
										
											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
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |     gFFI.ffiModel.updateEventListener(gFFI.sessionId, widget.id); | 
					
						
							| 
									
										
										
										
											2023-10-31 21:10:23 +08:00
										 |  |  |     WakelockPlus.enable(); | 
					
						
							| 
									
										
										
										
											2022-03-07 22:54:34 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void dispose() { | 
					
						
							| 
									
										
										
										
											2023-03-08 21:05:55 +09:00
										 |  |  |     model.close().whenComplete(() { | 
					
						
							| 
									
										
										
										
											2022-12-04 22:41:44 +09:00
										 |  |  |       gFFI.close(); | 
					
						
							|  |  |  |       gFFI.dialogManager.dismissAll(); | 
					
						
							| 
									
										
										
										
											2023-10-31 21:10:23 +08:00
										 |  |  |       WakelockPlus.disable(); | 
					
						
							| 
									
										
										
										
											2022-12-04 22:41:44 +09:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2022-03-07 22:54:34 +08:00
										 |  |  |     super.dispose(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-03-03 14:58:57 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |   Widget build(BuildContext context) => WillPopScope( | 
					
						
							|  |  |  |       onWillPop: () async { | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |         if (selectMode.value != SelectMode.none) { | 
					
						
							|  |  |  |           selectMode.value = SelectMode.none; | 
					
						
							|  |  |  |           setState(() {}); | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |         } else { | 
					
						
							|  |  |  |           currentFileController.goBack(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       child: Scaffold( | 
					
						
							|  |  |  |         // backgroundColor: MyTheme.grayBg,
 | 
					
						
							|  |  |  |         appBar: AppBar( | 
					
						
							|  |  |  |           leading: Row(children: [ | 
					
						
							|  |  |  |             IconButton( | 
					
						
							|  |  |  |                 icon: Icon(Icons.close), | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |                 onPressed: () => | 
					
						
							|  |  |  |                     clientClose(gFFI.sessionId, gFFI.dialogManager)), | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |           ]), | 
					
						
							|  |  |  |           centerTitle: true, | 
					
						
							|  |  |  |           title: ToggleSwitch( | 
					
						
							|  |  |  |             initialLabelIndex: showLocal ? 0 : 1, | 
					
						
							|  |  |  |             activeBgColor: [MyTheme.idColor], | 
					
						
							|  |  |  |             inactiveBgColor: Theme.of(context).brightness == Brightness.light | 
					
						
							|  |  |  |                 ? MyTheme.grayBg | 
					
						
							|  |  |  |                 : null, | 
					
						
							|  |  |  |             inactiveFgColor: Theme.of(context).brightness == Brightness.light | 
					
						
							|  |  |  |                 ? Colors.black54 | 
					
						
							|  |  |  |                 : null, | 
					
						
							|  |  |  |             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 = showLocal ? 0 : 1; | 
					
						
							|  |  |  |               if (index != current) { | 
					
						
							|  |  |  |                 setState(() => showLocal = !showLocal); | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |               } | 
					
						
							|  |  |  |             }, | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |           ), | 
					
						
							|  |  |  |           actions: [ | 
					
						
							|  |  |  |             PopupMenuButton<String>( | 
					
						
							| 
									
										
										
										
											2023-06-29 10:26:03 +08:00
										 |  |  |                 tooltip: "", | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                 icon: Icon(Icons.more_vert), | 
					
						
							|  |  |  |                 itemBuilder: (context) { | 
					
						
							|  |  |  |                   return [ | 
					
						
							|  |  |  |                     PopupMenuItem( | 
					
						
							|  |  |  |                       child: Row( | 
					
						
							|  |  |  |                         children: [ | 
					
						
							|  |  |  |                           Icon(Icons.refresh, | 
					
						
							|  |  |  |                               color: Theme.of(context).iconTheme.color), | 
					
						
							|  |  |  |                           SizedBox(width: 5), | 
					
						
							|  |  |  |                           Text(translate("Refresh File")) | 
					
						
							|  |  |  |                         ], | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                       value: "refresh", | 
					
						
							|  |  |  |                     ), | 
					
						
							|  |  |  |                     PopupMenuItem( | 
					
						
							|  |  |  |                       enabled: currentDir.path != "/", | 
					
						
							|  |  |  |                       child: Row( | 
					
						
							|  |  |  |                         children: [ | 
					
						
							|  |  |  |                           Icon(Icons.check, | 
					
						
							|  |  |  |                               color: Theme.of(context).iconTheme.color), | 
					
						
							|  |  |  |                           SizedBox(width: 5), | 
					
						
							|  |  |  |                           Text(translate("Multi Select")) | 
					
						
							|  |  |  |                         ], | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                       value: "select", | 
					
						
							|  |  |  |                     ), | 
					
						
							|  |  |  |                     PopupMenuItem( | 
					
						
							|  |  |  |                       enabled: currentDir.path != "/", | 
					
						
							|  |  |  |                       child: Row( | 
					
						
							|  |  |  |                         children: [ | 
					
						
							|  |  |  |                           Icon(Icons.folder_outlined, | 
					
						
							|  |  |  |                               color: Theme.of(context).iconTheme.color), | 
					
						
							|  |  |  |                           SizedBox(width: 5), | 
					
						
							|  |  |  |                           Text(translate("Create Folder")) | 
					
						
							|  |  |  |                         ], | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                       value: "folder", | 
					
						
							|  |  |  |                     ), | 
					
						
							|  |  |  |                     PopupMenuItem( | 
					
						
							|  |  |  |                       enabled: currentDir.path != "/", | 
					
						
							|  |  |  |                       child: Row( | 
					
						
							|  |  |  |                         children: [ | 
					
						
							|  |  |  |                           Icon( | 
					
						
							|  |  |  |                               currentOptions.showHidden | 
					
						
							|  |  |  |                                   ? Icons.check_box_outlined | 
					
						
							|  |  |  |                                   : Icons.check_box_outline_blank, | 
					
						
							|  |  |  |                               color: Theme.of(context).iconTheme.color), | 
					
						
							|  |  |  |                           SizedBox(width: 5), | 
					
						
							|  |  |  |                           Text(translate("Show Hidden Files")) | 
					
						
							|  |  |  |                         ], | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                       value: "hidden", | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |                   ]; | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |                 onSelected: (v) { | 
					
						
							|  |  |  |                   if (v == "refresh") { | 
					
						
							|  |  |  |                     currentFileController.refresh(); | 
					
						
							|  |  |  |                   } else if (v == "select") { | 
					
						
							|  |  |  |                     model.localController.selectedItems.clear(); | 
					
						
							|  |  |  |                     model.remoteController.selectedItems.clear(); | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |                     selectMode.toggle(showLocal); | 
					
						
							|  |  |  |                     setState(() {}); | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                   } else if (v == "folder") { | 
					
						
							|  |  |  |                     final name = TextEditingController(); | 
					
						
							| 
									
										
										
										
											2024-08-16 12:55:58 +08:00
										 |  |  |                     String? errorText; | 
					
						
							|  |  |  |                     gFFI.dialogManager.show((setState, close, context) { | 
					
						
							|  |  |  |                       name.addListener(() { | 
					
						
							|  |  |  |                         if (errorText != null) { | 
					
						
							|  |  |  |                           setState(() { | 
					
						
							|  |  |  |                             errorText = null; | 
					
						
							|  |  |  |                           }); | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                       }); | 
					
						
							|  |  |  |                       return CustomAlertDialog( | 
					
						
							|  |  |  |                           title: Text(translate("Create Folder")), | 
					
						
							|  |  |  |                           content: Column( | 
					
						
							|  |  |  |                             mainAxisSize: MainAxisSize.min, | 
					
						
							|  |  |  |                             children: [ | 
					
						
							|  |  |  |                               TextFormField( | 
					
						
							|  |  |  |                                 decoration: InputDecoration( | 
					
						
							|  |  |  |                                   labelText: | 
					
						
							|  |  |  |                                       translate("Please enter the folder name"), | 
					
						
							|  |  |  |                                   errorText: errorText, | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                                 ), | 
					
						
							| 
									
										
										
										
											2024-08-16 12:55:58 +08:00
										 |  |  |                                 controller: name, | 
					
						
							|  |  |  |                               ), | 
					
						
							|  |  |  |                             ], | 
					
						
							|  |  |  |                           ), | 
					
						
							|  |  |  |                           actions: [ | 
					
						
							|  |  |  |                             dialogButton("Cancel", | 
					
						
							|  |  |  |                                 onPressed: () => close(false), isOutline: true), | 
					
						
							|  |  |  |                             dialogButton("OK", onPressed: () { | 
					
						
							|  |  |  |                               if (name.value.text.isNotEmpty) { | 
					
						
							|  |  |  |                                 if (!PathUtil.validName( | 
					
						
							|  |  |  |                                     name.value.text, | 
					
						
							|  |  |  |                                     currentFileController | 
					
						
							|  |  |  |                                         .options.value.isWindows)) { | 
					
						
							|  |  |  |                                   setState(() { | 
					
						
							|  |  |  |                                     errorText = | 
					
						
							|  |  |  |                                         translate("Invalid folder name"); | 
					
						
							|  |  |  |                                   }); | 
					
						
							|  |  |  |                                   return; | 
					
						
							|  |  |  |                                 } | 
					
						
							|  |  |  |                                 currentFileController.createDir(PathUtil.join( | 
					
						
							|  |  |  |                                     currentDir.path, | 
					
						
							|  |  |  |                                     name.value.text, | 
					
						
							|  |  |  |                                     currentOptions.isWindows)); | 
					
						
							|  |  |  |                                 close(); | 
					
						
							|  |  |  |                               } | 
					
						
							|  |  |  |                             }) | 
					
						
							|  |  |  |                           ]); | 
					
						
							|  |  |  |                     }); | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                   } else if (v == "hidden") { | 
					
						
							|  |  |  |                     currentFileController.toggleShowHidden(); | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                 }), | 
					
						
							|  |  |  |           ], | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         body: showLocal | 
					
						
							|  |  |  |             ? FileManagerView( | 
					
						
							|  |  |  |                 controller: model.localController, | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |                 selectMode: selectMode, | 
					
						
							|  |  |  |               ) | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |             : FileManagerView( | 
					
						
							|  |  |  |                 controller: model.remoteController, | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |                 selectMode: selectMode, | 
					
						
							|  |  |  |               ), | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |         bottomSheet: bottomSheet(), | 
					
						
							|  |  |  |       )); | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |   Widget? bottomSheet() { | 
					
						
							|  |  |  |     return Obx(() { | 
					
						
							|  |  |  |       final selectedItems = getActiveSelectedItems(); | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |       final jobTable = model.jobController.jobTable; | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |       final localLabel = selectedItems?.isLocal == null | 
					
						
							|  |  |  |           ? "" | 
					
						
							|  |  |  |           : " [${selectedItems!.isLocal ? translate("Local") : translate("Remote")}]"; | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |       if (!(selectMode.value == SelectMode.none)) { | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |         final selectedItemsLen = | 
					
						
							|  |  |  |             "${selectedItems?.items.length ?? 0} ${translate("items")}"; | 
					
						
							|  |  |  |         if (selectedItems == null || | 
					
						
							|  |  |  |             selectedItems.items.isEmpty || | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |             selectMode.value.eq(showLocal)) { | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |           return BottomSheetBody( | 
					
						
							|  |  |  |               leading: Icon(Icons.check), | 
					
						
							|  |  |  |               title: translate("Selected"), | 
					
						
							|  |  |  |               text: selectedItemsLen + localLabel, | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |               onCanceled: () { | 
					
						
							|  |  |  |                 selectedItems?.items.clear(); | 
					
						
							|  |  |  |                 selectMode.value = SelectMode.none; | 
					
						
							|  |  |  |                 setState(() {}); | 
					
						
							|  |  |  |               }, | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |               actions: [ | 
					
						
							|  |  |  |                 IconButton( | 
					
						
							|  |  |  |                   icon: Icon(Icons.compare_arrows), | 
					
						
							|  |  |  |                   onPressed: () => setState(() => showLocal = !showLocal), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |                 IconButton( | 
					
						
							|  |  |  |                   icon: Icon(Icons.delete_forever), | 
					
						
							|  |  |  |                   onPressed: selectedItems != null | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |                       ? () async { | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                           if (selectedItems.items.isNotEmpty) { | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |                             await currentFileController | 
					
						
							|  |  |  |                                 .removeAction(selectedItems); | 
					
						
							|  |  |  |                             selectedItems.items.clear(); | 
					
						
							|  |  |  |                             selectMode.value = SelectMode.none; | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                           } | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                       : null, | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |               ]); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           return BottomSheetBody( | 
					
						
							|  |  |  |               leading: Icon(Icons.input), | 
					
						
							|  |  |  |               title: translate("Paste here?"), | 
					
						
							|  |  |  |               text: selectedItemsLen + localLabel, | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |               onCanceled: () { | 
					
						
							|  |  |  |                 selectedItems.items.clear(); | 
					
						
							|  |  |  |                 selectMode.value = SelectMode.none; | 
					
						
							|  |  |  |                 setState(() {}); | 
					
						
							|  |  |  |               }, | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |               actions: [ | 
					
						
							|  |  |  |                 IconButton( | 
					
						
							|  |  |  |                   icon: Icon(Icons.compare_arrows), | 
					
						
							|  |  |  |                   onPressed: () => setState(() => showLocal = !showLocal), | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |                 IconButton( | 
					
						
							|  |  |  |                   icon: Icon(Icons.paste), | 
					
						
							|  |  |  |                   onPressed: () { | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |                     selectMode.value = SelectMode.none; | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                     final otherSide = showLocal | 
					
						
							|  |  |  |                         ? model.remoteController | 
					
						
							|  |  |  |                         : model.localController; | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |                     final thisSideData = | 
					
						
							|  |  |  |                         DirectoryData(currentDir, currentOptions); | 
					
						
							|  |  |  |                     otherSide.sendFiles(selectedItems, thisSideData); | 
					
						
							|  |  |  |                     selectedItems.items.clear(); | 
					
						
							|  |  |  |                     selectMode.value = SelectMode.none; | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                   }, | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |               ]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (jobTable.isEmpty) { | 
					
						
							|  |  |  |         return Offstage(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       switch (jobTable.last.state) { | 
					
						
							|  |  |  |         case JobState.inProgress: | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |           return BottomSheetBody( | 
					
						
							|  |  |  |             leading: CircularProgressIndicator(), | 
					
						
							|  |  |  |             title: translate("Waiting"), | 
					
						
							|  |  |  |             text: | 
					
						
							|  |  |  |                 "${translate("Speed")}:  ${readableFileSize(jobTable.last.speed)}/s", | 
					
						
							|  |  |  |             onCanceled: () { | 
					
						
							|  |  |  |               model.jobController.cancelJob(jobTable.last.id); | 
					
						
							|  |  |  |               jobTable.clear(); | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |           ); | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |         case JobState.done: | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |           return BottomSheetBody( | 
					
						
							|  |  |  |             leading: Icon(Icons.check), | 
					
						
							|  |  |  |             title: "${translate("Successful")}!", | 
					
						
							|  |  |  |             text: jobTable.last.display(), | 
					
						
							|  |  |  |             onCanceled: () => jobTable.clear(), | 
					
						
							|  |  |  |           ); | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |         case JobState.error: | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |           return BottomSheetBody( | 
					
						
							|  |  |  |             leading: Icon(Icons.error), | 
					
						
							|  |  |  |             title: "${translate("Error")}!", | 
					
						
							|  |  |  |             text: "", | 
					
						
							|  |  |  |             onCanceled: () => jobTable.clear(), | 
					
						
							|  |  |  |           ); | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |         case JobState.none: | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         case JobState.paused: | 
					
						
							|  |  |  |           // TODO: Handle this case.
 | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return Offstage(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   SelectedItems? getActiveSelectedItems() { | 
					
						
							|  |  |  |     final localSelectedItems = model.localController.selectedItems; | 
					
						
							|  |  |  |     final remoteSelectedItems = model.remoteController.selectedItems; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (localSelectedItems.items.isNotEmpty && | 
					
						
							|  |  |  |         remoteSelectedItems.items.isNotEmpty) { | 
					
						
							|  |  |  |       // assert unreachable
 | 
					
						
							|  |  |  |       debugPrint("Wrong SelectedItems state, reset"); | 
					
						
							|  |  |  |       localSelectedItems.clear(); | 
					
						
							|  |  |  |       remoteSelectedItems.clear(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (localSelectedItems.items.isEmpty && remoteSelectedItems.items.isEmpty) { | 
					
						
							|  |  |  |       return null; | 
					
						
							| 
									
										
										
										
											2022-03-11 01:28:13 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (localSelectedItems.items.length > remoteSelectedItems.items.length) { | 
					
						
							|  |  |  |       return localSelectedItems; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       return remoteSelectedItems; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FileManagerView extends StatefulWidget { | 
					
						
							|  |  |  |   final FileController controller; | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |   final Rx<SelectMode> selectMode; | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |   FileManagerView({required this.controller, required this.selectMode}); | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   State<StatefulWidget> createState() => _FileManagerViewState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _FileManagerViewState extends State<FileManagerView> { | 
					
						
							|  |  |  |   final _listScrollController = ScrollController(); | 
					
						
							|  |  |  |   final _breadCrumbScroller = ScrollController(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bool get isLocal => widget.controller.isLocal; | 
					
						
							|  |  |  |   FileController get controller => widget.controller; | 
					
						
							|  |  |  |   SelectedItems get _selectedItems => widget.controller.selectedItems; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							|  |  |  |     super.initState(); | 
					
						
							|  |  |  |     controller.directory.listen((e) => breadCrumbScrollToEnd()); | 
					
						
							| 
									
										
										
										
											2022-03-07 22:54:34 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |     return Column(children: [ | 
					
						
							|  |  |  |       headTools(), | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |       Expanded(child: Obx(() { | 
					
						
							|  |  |  |         final entries = controller.directory.value.entries; | 
					
						
							|  |  |  |         return ListView.builder( | 
					
						
							|  |  |  |           controller: _listScrollController, | 
					
						
							|  |  |  |           itemCount: entries.length + 1, | 
					
						
							|  |  |  |           itemBuilder: (context, index) { | 
					
						
							|  |  |  |             if (index >= entries.length) { | 
					
						
							|  |  |  |               return listTail(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             var selected = false; | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |             if (widget.selectMode.value != SelectMode.none) { | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |               selected = _selectedItems.items.contains(entries[index]); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             final sizeStr = entries[index].isFile | 
					
						
							|  |  |  |                 ? readableFileSize(entries[index].size.toDouble()) | 
					
						
							|  |  |  |                 : ""; | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |             final showCheckBox = () { | 
					
						
							|  |  |  |               return widget.selectMode.value != SelectMode.none && | 
					
						
							|  |  |  |                   widget.selectMode.value.eq(controller.selectedItems.isLocal); | 
					
						
							|  |  |  |             }(); | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |             return Card( | 
					
						
							|  |  |  |               child: ListTile( | 
					
						
							|  |  |  |                 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 | 
					
						
							|  |  |  |                             : Icons.folder, | 
					
						
							|  |  |  |                         size: 40), | 
					
						
							|  |  |  |                 title: Text(entries[index].name), | 
					
						
							|  |  |  |                 selected: selected, | 
					
						
							|  |  |  |                 subtitle: entries[index].isDrive | 
					
						
							|  |  |  |                     ? null | 
					
						
							|  |  |  |                     : Text( | 
					
						
							|  |  |  |                         "${entries[index].lastModified().toString().replaceAll(".000", "")}   $sizeStr", | 
					
						
							|  |  |  |                         style: TextStyle(fontSize: 12, color: MyTheme.darkGray), | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                 trailing: entries[index].isDrive | 
					
						
							|  |  |  |                     ? null | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |                     : showCheckBox | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                         ? Checkbox( | 
					
						
							|  |  |  |                             value: selected, | 
					
						
							|  |  |  |                             onChanged: (v) { | 
					
						
							|  |  |  |                               if (v == null) return; | 
					
						
							|  |  |  |                               if (v && !selected) { | 
					
						
							|  |  |  |                                 _selectedItems.add(entries[index]); | 
					
						
							|  |  |  |                               } else if (!v && selected) { | 
					
						
							|  |  |  |                                 _selectedItems.remove(entries[index]); | 
					
						
							|  |  |  |                               } | 
					
						
							|  |  |  |                               setState(() {}); | 
					
						
							|  |  |  |                             }) | 
					
						
							|  |  |  |                         : PopupMenuButton<String>( | 
					
						
							| 
									
										
										
										
											2023-06-29 10:26:03 +08:00
										 |  |  |                             tooltip: "", | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                             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, | 
					
						
							| 
									
										
										
										
											2024-08-16 12:55:58 +08:00
										 |  |  |                                 ), | 
					
						
							|  |  |  |                                 if (!entries[index].isDrive && | 
					
						
							|  |  |  |                                     versionCmp(gFFI.ffiModel.pi.version, | 
					
						
							|  |  |  |                                             "1.3.0") >= | 
					
						
							|  |  |  |                                         0) | 
					
						
							|  |  |  |                                   PopupMenuItem( | 
					
						
							|  |  |  |                                     child: Text(translate("Rename")), | 
					
						
							|  |  |  |                                     value: "rename", | 
					
						
							|  |  |  |                                   ) | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                               ]; | 
					
						
							|  |  |  |                             }, | 
					
						
							|  |  |  |                             onSelected: (v) { | 
					
						
							|  |  |  |                               if (v == "delete") { | 
					
						
							|  |  |  |                                 final items = SelectedItems(isLocal: isLocal); | 
					
						
							|  |  |  |                                 items.add(entries[index]); | 
					
						
							|  |  |  |                                 controller.removeAction(items); | 
					
						
							|  |  |  |                               } else if (v == "multi_select") { | 
					
						
							|  |  |  |                                 _selectedItems.clear(); | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |                                 widget.selectMode.toggle(isLocal); | 
					
						
							|  |  |  |                                 setState(() {}); | 
					
						
							| 
									
										
										
										
											2024-08-16 12:55:58 +08:00
										 |  |  |                               } else if (v == "rename") { | 
					
						
							|  |  |  |                                 controller.renameAction( | 
					
						
							|  |  |  |                                     entries[index], isLocal); | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                               } | 
					
						
							|  |  |  |                             }), | 
					
						
							|  |  |  |                 onTap: () { | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |                   if (showCheckBox) { | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                     if (selected) { | 
					
						
							|  |  |  |                       _selectedItems.remove(entries[index]); | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                       _selectedItems.add(entries[index]); | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |                     setState(() {}); | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                     return; | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                   if (entries[index].isDirectory || entries[index].isDrive) { | 
					
						
							|  |  |  |                     controller.openDirectory(entries[index].path); | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |                   } else { | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                     // Perform file-related tasks.
 | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |                   } | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                 }, | 
					
						
							|  |  |  |                 onLongPress: entries[index].isDrive | 
					
						
							|  |  |  |                     ? null | 
					
						
							|  |  |  |                     : () { | 
					
						
							|  |  |  |                         _selectedItems.clear(); | 
					
						
							| 
									
										
										
										
											2023-03-09 22:34:43 +09:00
										 |  |  |                         widget.selectMode.toggle(isLocal); | 
					
						
							|  |  |  |                         if (widget.selectMode.value != SelectMode.none) { | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                           _selectedItems.add(entries[index]); | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                         setState(() {}); | 
					
						
							|  |  |  |                       }, | 
					
						
							|  |  |  |               ), | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       })) | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |     ]); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-03-09 17:07:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |   void breadCrumbScrollToEnd() { | 
					
						
							| 
									
										
										
										
											2022-03-11 01:28:13 +08:00
										 |  |  |     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: [ | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |           Expanded(child: Obx(() { | 
					
						
							|  |  |  |             final home = controller.options.value.home; | 
					
						
							|  |  |  |             final isWindows = controller.options.value.isWindows; | 
					
						
							|  |  |  |             return BreadCrumb( | 
					
						
							|  |  |  |               items: getPathBreadCrumbItems(controller.shortPath, isWindows, | 
					
						
							|  |  |  |                   () => controller.goToHomeDirectory(), (list) { | 
					
						
							|  |  |  |                 var path = ""; | 
					
						
							|  |  |  |                 if (home.startsWith(list[0])) { | 
					
						
							|  |  |  |                   // absolute path
 | 
					
						
							|  |  |  |                   for (var item in list) { | 
					
						
							|  |  |  |                     path = PathUtil.join(path, item, isWindows); | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                   path += home; | 
					
						
							|  |  |  |                   for (var item in list) { | 
					
						
							|  |  |  |                     path = PathUtil.join(path, item, isWindows); | 
					
						
							|  |  |  |                   } | 
					
						
							| 
									
										
										
										
											2022-03-17 21:03:52 +08:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                 controller.openDirectory(path); | 
					
						
							|  |  |  |               }), | 
					
						
							|  |  |  |               divider: Icon(Icons.chevron_right), | 
					
						
							|  |  |  |               overflow: ScrollableOverflow(controller: _breadCrumbScroller), | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |           })), | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |           Row( | 
					
						
							| 
									
										
										
										
											2022-03-09 17:07:24 +08:00
										 |  |  |             children: [ | 
					
						
							| 
									
										
										
										
											2022-11-03 00:01:59 +09:00
										 |  |  |               IconButton( | 
					
						
							|  |  |  |                 icon: Icon(Icons.arrow_back), | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                 onPressed: controller.goBack, | 
					
						
							| 
									
										
										
										
											2022-11-03 00:01:59 +09:00
										 |  |  |               ), | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |               IconButton( | 
					
						
							|  |  |  |                 icon: Icon(Icons.arrow_upward), | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                 onPressed: controller.goToParentDirectory, | 
					
						
							| 
									
										
										
										
											2022-04-07 20:40:51 +08:00
										 |  |  |               ), | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |               PopupMenuButton<SortBy>( | 
					
						
							| 
									
										
										
										
											2023-06-29 10:26:03 +08:00
										 |  |  |                   tooltip: "", | 
					
						
							| 
									
										
										
										
											2022-03-16 15:33:00 +08:00
										 |  |  |                   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(); | 
					
						
							|  |  |  |                   }, | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |                   onSelected: controller.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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |   Widget listTail() => Obx(() => Container( | 
					
						
							|  |  |  |         height: 100, | 
					
						
							|  |  |  |         child: Column( | 
					
						
							|  |  |  |           children: [ | 
					
						
							|  |  |  |             Padding( | 
					
						
							|  |  |  |               padding: EdgeInsets.fromLTRB(30, 5, 30, 0), | 
					
						
							|  |  |  |               child: Text( | 
					
						
							|  |  |  |                 controller.directory.value.path, | 
					
						
							|  |  |  |                 style: TextStyle(color: MyTheme.darkGray), | 
					
						
							| 
									
										
										
										
											2022-04-07 23:19:57 +08:00
										 |  |  |               ), | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |             ), | 
					
						
							|  |  |  |             Padding( | 
					
						
							|  |  |  |               padding: EdgeInsets.all(2), | 
					
						
							|  |  |  |               child: Text( | 
					
						
							|  |  |  |                 "${translate("Total")}: ${controller.directory.value.entries.length} ${translate("items")}", | 
					
						
							|  |  |  |                 style: TextStyle(color: MyTheme.darkGray), | 
					
						
							| 
									
										
										
										
											2022-04-07 23:19:57 +08:00
										 |  |  |               ), | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |             ) | 
					
						
							|  |  |  |           ], | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       )); | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |   List<BreadCrumbItem> getPathBreadCrumbItems(String shortPath, bool isWindows, | 
					
						
							| 
									
										
										
										
											2022-03-17 21:03:52 +08:00
										 |  |  |       void Function() onHome, void Function(List<String>) onPressed) { | 
					
						
							| 
									
										
										
										
											2023-03-09 21:09:17 +09:00
										 |  |  |     final list = PathUtil.split(shortPath, isWindows); | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							| 
									
										
										
										
											2024-02-12 21:39:19 +08:00
										 |  |  |     // ignore: no_leading_underscores_for_local_identifiers
 | 
					
						
							| 
									
										
										
										
											2022-03-12 21:42:05 +08:00
										 |  |  |     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
										 |  |  | } |