2022-06-17 22:57:41 +08:00
import ' dart:io ' ;
2022-07-09 19:14:40 +08:00
import ' dart:math ' ;
2022-06-17 22:57:41 +08:00
import ' package:flutter/material.dart ' ;
import ' package:flutter_breadcrumb/flutter_breadcrumb.dart ' ;
import ' package:flutter_hbb/mobile/pages/file_manager_page.dart ' ;
import ' package:flutter_hbb/models/file_model.dart ' ;
import ' package:flutter_smart_dialog/flutter_smart_dialog.dart ' ;
import ' package:get/get.dart ' ;
import ' package:provider/provider.dart ' ;
import ' package:wakelock/wakelock.dart ' ;
import ' ../../common.dart ' ;
import ' ../../models/model.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 >
with AutomaticKeepAliveClientMixin {
2022-07-09 19:14:40 +08:00
final _localSelectedItems = SelectedItems ( ) ;
final _remoteSelectedItems = SelectedItems ( ) ;
2022-06-17 22:57:41 +08:00
/// FFI with name file_transfer_id
FFI get _ffi = > ffi ( ' ft_ ${ widget . id } ' ) ;
FileModel get model = > _ffi . fileModel ;
2022-07-09 19:14:40 +08:00
SelectedItems getSelectedItem ( bool isLocal ) {
return isLocal ? _localSelectedItems : _remoteSelectedItems ;
}
2022-06-17 22:57:41 +08:00
@ override
void initState ( ) {
super . initState ( ) ;
2022-06-21 17:58:27 +08:00
Get . put ( FFI . newFFI ( ) . . connect ( widget . id , isFileTransfer: true ) ,
tag: ' ft_ ${ widget . id } ' ) ;
// _ffi.ffiModel.updateEventListener(widget.id);
2022-06-17 22:57:41 +08:00
if ( ! Platform . isLinux ) {
Wakelock . enable ( ) ;
}
2022-06-21 17:58:27 +08:00
print ( " init success with id ${ widget . id } " ) ;
2022-06-17 22:57:41 +08:00
}
@ override
void dispose ( ) {
model . onClose ( ) ;
_ffi . close ( ) ;
SmartDialog . dismiss ( ) ;
if ( ! Platform . isLinux ) {
Wakelock . disable ( ) ;
}
Get . delete < FFI > ( tag: ' ft_ ${ widget . id } ' ) ;
super . dispose ( ) ;
}
@ override
Widget build ( BuildContext context ) {
super . build ( context ) ;
return ChangeNotifierProvider . value (
value: _ffi . fileModel ,
child: Consumer < FileModel > ( builder: ( _context , _model , _child ) {
return WillPopScope (
onWillPop: ( ) async {
if ( model . selectMode ) {
model . toggleSelectMode ( ) ;
}
return false ;
} ,
child: Scaffold (
backgroundColor: MyTheme . grayBg ,
2022-06-21 18:14:44 +08:00
body: Row (
children: [
2022-07-01 12:08:52 +08:00
Flexible ( flex: 3 , child: body ( isLocal: true ) ) ,
Flexible ( flex: 3 , child: body ( isLocal: false ) ) ,
Flexible ( flex: 2 , child: statusList ( ) )
2022-06-21 18:14:44 +08:00
] ,
) ,
2022-06-17 22:57:41 +08:00
bottomSheet: bottomSheet ( ) ,
) ) ;
} ) ) ;
}
2022-06-27 16:44:34 +08:00
Widget menu ( { bool isLocal = false } ) {
return PopupMenuButton < String > (
icon: Icon ( Icons . more_vert ) ,
itemBuilder: ( context ) {
return [
PopupMenuItem (
child: Row (
children: [
Icon (
2022-07-11 16:07:49 +08:00
model . getCurrentShowHidden ( isLocal )
2022-06-27 16:44:34 +08:00
? Icons . check_box_outlined
: Icons . check_box_outline_blank ,
color: Colors . black ) ,
SizedBox ( width: 5 ) ,
Text ( translate ( " Show Hidden Files " ) )
] ,
) ,
value: " hidden " ,
)
] ;
} ,
onSelected: ( v ) {
2022-07-11 16:07:49 +08:00
if ( v = = " hidden " ) {
2022-06-27 16:44:34 +08:00
model . toggleShowHidden ( local: isLocal ) ;
}
} ) ;
}
2022-06-21 18:14:44 +08:00
Widget body ( { bool isLocal = false } ) {
final fd = isLocal ? model . currentLocalDir : model . currentRemoteDir ;
2022-06-17 22:57:41 +08:00
final entries = fd . entries ;
2022-07-11 16:07:49 +08:00
final sortIndex = ( SortBy style ) {
switch ( style ) {
case SortBy . Name:
return 1 ;
case SortBy . Type:
return 0 ;
case SortBy . Modified:
return 2 ;
case SortBy . Size:
return 3 ;
}
} ( model . getSortStyle ( isLocal ) ) ;
final sortAscending =
isLocal ? model . localSortAscending : model . remoteSortAscending ;
2022-07-01 17:17:25 +08:00
return Container (
decoration: BoxDecoration (
2022-07-11 16:07:49 +08:00
color: Colors . white54 , border: Border . all ( color: Colors . black26 ) ) ,
2022-07-01 17:17:25 +08:00
margin: const EdgeInsets . all ( 16.0 ) ,
padding: const EdgeInsets . all ( 8.0 ) ,
child: Column ( crossAxisAlignment: CrossAxisAlignment . start , children: [
headTools ( isLocal ) ,
Expanded (
child: Row (
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
Expanded (
child: SingleChildScrollView (
child: DataTable (
showCheckboxColumn: true ,
2022-07-11 16:07:49 +08:00
dataRowHeight: 25 ,
headingRowHeight: 30 ,
2022-07-01 17:17:25 +08:00
columnSpacing: 8 ,
2022-07-11 16:07:49 +08:00
showBottomBorder: true ,
sortColumnIndex: sortIndex ,
sortAscending: sortAscending ,
2022-07-01 17:17:25 +08:00
columns: [
DataColumn ( label: Text ( translate ( " " ) ) ) , // icon
DataColumn (
label: Text (
2022-07-11 16:07:49 +08:00
translate ( " Name " ) ,
) ,
onSort: ( columnIndex , ascending ) {
model . changeSortStyle ( SortBy . Name ,
isLocal: isLocal , ascending: ascending ) ;
} ) ,
DataColumn (
label: Text (
translate ( " Modified " ) ,
) ,
onSort: ( columnIndex , ascending ) {
model . changeSortStyle ( SortBy . Modified ,
isLocal: isLocal , ascending: ascending ) ;
} ) ,
DataColumn (
label: Text ( translate ( " Size " ) ) ,
onSort: ( columnIndex , ascending ) {
model . changeSortStyle ( SortBy . Size ,
isLocal: isLocal , ascending: ascending ) ;
} ) ,
2022-07-01 17:17:25 +08:00
] ,
rows: entries . map ( ( entry ) {
final sizeStr = entry . isFile
? readableFileSize ( entry . size . toDouble ( ) )
: " " ;
return DataRow (
key: ValueKey ( entry . name ) ,
onSelectChanged: ( s ) {
2022-07-09 19:14:40 +08:00
if ( s ! = null ) {
if ( s ) {
getSelectedItem ( isLocal ) . add ( isLocal , entry ) ;
} else {
getSelectedItem ( isLocal ) . remove ( entry ) ;
}
2022-07-11 16:07:49 +08:00
setState ( ( ) { } ) ;
2022-07-09 19:14:40 +08:00
}
2022-07-01 17:17:25 +08:00
} ,
2022-07-09 19:14:40 +08:00
selected: getSelectedItem ( isLocal ) . contains ( entry ) ,
2022-07-01 17:17:25 +08:00
cells: [
DataCell ( Icon (
entry . isFile ? Icons . feed_outlined : Icons . folder ,
size: 25 ) ) ,
DataCell (
ConstrainedBox (
constraints: BoxConstraints ( maxWidth: 100 ) ,
2022-07-11 16:07:49 +08:00
child: Tooltip (
message: entry . name ,
child: Text ( entry . name ,
overflow: TextOverflow . ellipsis ) ,
) ) , onTap: ( ) {
2022-07-01 17:17:25 +08:00
if ( entry . isDirectory ) {
model . openDirectory ( entry . path , isLocal: isLocal ) ;
2022-07-11 16:07:49 +08:00
if ( isLocal ) {
_localSelectedItems . clear ( ) ;
} else {
_remoteSelectedItems . clear ( ) ;
}
2022-07-01 17:17:25 +08:00
} else {
// Perform file-related tasks.
2022-07-09 19:14:40 +08:00
final _selectedItems = getSelectedItem ( isLocal ) ;
if ( _selectedItems . contains ( entry ) ) {
_selectedItems . remove ( entry ) ;
} else {
_selectedItems . add ( isLocal , entry ) ;
}
2022-07-11 16:07:49 +08:00
setState ( ( ) { } ) ;
2022-07-01 17:17:25 +08:00
}
} ) ,
DataCell ( Text (
entry
. lastModified ( )
. toString ( )
. replaceAll ( " .000 " , " " ) +
" " ,
style: TextStyle (
fontSize: 12 , color: MyTheme . darkGray ) ,
) ) ,
DataCell ( Text (
sizeStr ,
style: TextStyle (
fontSize: 12 , color: MyTheme . darkGray ) ,
) ) ,
] ) ;
} ) . toList ( ) ,
) ,
2022-06-17 22:57:41 +08:00
) ,
2022-07-01 17:17:25 +08:00
)
] ,
) ) ,
2022-07-11 16:07:49 +08:00
// Center(child: listTail(isLocal: isLocal)),
2022-07-01 17:17:25 +08:00
// Expanded(
// child: ListView.builder(
// itemCount: entries.length + 1,
// itemBuilder: (context, index) {
// if (index >= entries.length) {
// return listTail(isLocal: isLocal);
// }
// var selected = false;
// if (model.selectMode) {
// selected = _selectedItems.contains(entries[index]);
// }
//
// final sizeStr = entries[index].isFile
// ? readableFileSize(entries[index].size.toDouble())
// : "";
// return Card(
// child: ListTile(
// leading: Icon(
// entries[index].isFile ? Icons.feed_outlined : Icons.folder,
// size: 40),
// title: Text(entries[index].name),
// selected: selected,
// subtitle: Text(
// entries[index]
// .lastModified()
// .toString()
// .replaceAll(".000", "") +
// " " +
// sizeStr,
// style: TextStyle(fontSize: 12, color: MyTheme.darkGray),
// ),
// trailing: needShowCheckBox()
// ? 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();
// }
// }),
// onTap: () {
// if (model.selectMode && !_selectedItems.isOtherPage(isLocal)) {
// if (selected) {
// _selectedItems.remove(entries[index]);
// } else {
// _selectedItems.add(isLocal, entries[index]);
// }
// setState(() {});
// return;
// }
// if (entries[index].isDirectory) {
// model.openDirectory(entries[index].path, isLocal: isLocal);
// breadCrumbScrollToEnd(isLocal);
// } else {
// // Perform file-related tasks.
// }
// },
// onLongPress: () {
// _selectedItems.clear();
// model.toggleSelectMode();
// if (model.selectMode) {
// _selectedItems.add(isLocal, entries[index]);
// }
// setState(() {});
// },
// ),
// );
// },
// ))
] ) ,
) ;
2022-06-17 22:57:41 +08:00
}
2022-07-01 12:08:52 +08:00
/// transfer status list
/// watch transfer status
Widget statusList ( ) {
2022-07-01 17:17:25 +08:00
return PreferredSize (
child: Container (
2022-07-11 16:07:49 +08:00
margin: const EdgeInsets . only ( top: 16.0 , bottom: 16.0 , right: 16.0 ) ,
2022-07-01 17:17:25 +08:00
padding: const EdgeInsets . all ( 8.0 ) ,
2022-07-11 16:07:49 +08:00
decoration: BoxDecoration (
color: Colors . white70 , border: Border . all ( color: Colors . grey ) ) ,
2022-07-09 19:14:40 +08:00
child: Obx (
( ) = > ListView . builder (
2022-07-11 10:30:45 +08:00
itemBuilder: ( BuildContext context , int index ) {
final item = model . jobTable [ index ] ;
return Column (
mainAxisSize: MainAxisSize . min ,
2022-07-09 19:14:40 +08:00
children: [
2022-07-11 10:30:45 +08:00
Row (
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
Transform . rotate (
angle: item . isRemote ? pi : 0 ,
child: Icon ( Icons . send ) ) ,
2022-07-11 16:07:49 +08:00
SizedBox (
width: 16.0 ,
) ,
2022-07-11 10:30:45 +08:00
Expanded (
child: Column (
mainAxisSize: MainAxisSize . min ,
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
Tooltip (
message: item . jobName ,
2022-07-11 16:07:49 +08:00
child: Text (
' ${ item . jobName } ' ,
2022-07-11 10:30:45 +08:00
maxLines: 1 ,
2022-07-11 16:07:49 +08:00
style: TextStyle ( color: Colors . black45 ) ,
overflow: TextOverflow . ellipsis ,
) ) ,
2022-07-11 10:30:45 +08:00
Wrap (
children: [
2022-07-11 16:07:49 +08:00
Text (
' ${ item . state . display ( ) } ${ max ( 0 , item . fileNum ) } / ${ item . fileCount } ' ) ,
Text (
' ${ translate ( " files " ) } ${ readableFileSize ( item . totalSize . toDouble ( ) ) } ' ) ,
Offstage (
offstage:
item . state ! = JobState . inProgress ,
child: Text (
' ${ readableFileSize ( item . speed ) + " /s " } ' ) ) ,
Offstage (
offstage: item . totalSize < = 0 ,
child: Text (
' ${ ( item . finishedSize . toDouble ( ) * 100 / item . totalSize . toDouble ( ) ) . toStringAsFixed ( 2 ) } % ' ) ,
) ,
2022-07-11 10:30:45 +08:00
] ,
) ,
] ,
) ,
) ,
Row (
mainAxisAlignment: MainAxisAlignment . end ,
children: [
2022-07-11 16:07:49 +08:00
IconButton (
icon: Icon ( Icons . delete ) ,
onPressed: ( ) {
model . jobTable . removeAt ( index ) ;
model . cancelJob ( item . id ) ;
} ,
) ,
2022-07-11 10:30:45 +08:00
] ,
)
] ,
) ,
2022-07-11 16:07:49 +08:00
SizedBox (
height: 8.0 ,
) ,
Divider (
height: 2.0 ,
)
2022-07-09 19:14:40 +08:00
] ,
) ;
2022-07-11 16:07:49 +08:00
} ,
2022-07-09 19:14:40 +08:00
itemCount: model . jobTable . length ,
) ,
) ,
2022-07-01 17:17:25 +08:00
) ,
preferredSize: Size ( 200 , double . infinity ) ) ;
2022-07-01 12:08:52 +08:00
}
2022-06-27 16:44:34 +08:00
goBack ( { bool ? isLocal } ) {
model . goToParentDirectory ( isLocal: isLocal ) ;
2022-06-17 22:57:41 +08:00
}
2022-06-27 16:44:34 +08:00
Widget headTools ( bool isLocal ) = > Container (
2022-07-11 16:07:49 +08:00
child: Column (
2022-06-17 22:57:41 +08:00
children: [
2022-07-11 16:07:49 +08:00
// symbols
PreferredSize (
child: Row (
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
Container (
width: 50 ,
height: 50 ,
decoration: BoxDecoration ( color: Colors . blue ) ,
padding: EdgeInsets . all ( 8.0 ) ,
child: FutureBuilder < String > (
future: _ffi . bind . sessionGetPlatform (
id: _ffi . id , isRemote: ! isLocal ) ,
builder: ( context , snapshot ) {
if ( snapshot . hasData & & snapshot . data ! . isNotEmpty ) {
return getPlatformImage ( ' ${ snapshot . data } ' ) ;
} else {
return CircularProgressIndicator ( color: Colors . white , ) ;
}
} ) ) ,
Text ( isLocal
? translate ( " Local Computer " )
: translate ( " Remote Computer " ) )
. marginOnly ( left: 8.0 )
] ,
2022-07-09 19:14:40 +08:00
) ,
2022-07-11 16:07:49 +08:00
preferredSize: Size ( double . infinity , 70 ) ) ,
// buttons
2022-06-17 22:57:41 +08:00
Row (
children: [
2022-07-11 16:07:49 +08:00
Row (
children: [
IconButton (
onPressed: ( ) {
model . goHome ( isLocal: isLocal ) ;
} ,
icon: Icon ( Icons . home_outlined ) ) ,
IconButton (
icon: Icon ( Icons . arrow_upward ) ,
onPressed: ( ) {
goBack ( isLocal: isLocal ) ;
} ,
) ,
menu ( isLocal: isLocal ) ,
] ,
2022-06-17 22:57:41 +08:00
) ,
2022-07-11 16:07:49 +08:00
Expanded (
child: Container (
decoration: BoxDecoration (
border: Border . all ( color: Colors . black12 ) ) ,
child: TextField (
decoration: InputDecoration (
border: InputBorder . none ,
isDense: true ,
prefix: Padding ( padding: EdgeInsets . only ( left: 4.0 ) ) ,
suffix: DropdownButton < String > (
isDense: true ,
underline: Offstage ( ) ,
items: [
// TODO: favourite
DropdownMenuItem ( child: Text ( ' / ' ) , value: ' / ' , )
] , onChanged: ( path ) {
if ( path is String & & path . isNotEmpty ) {
model . openDirectory ( path , isLocal: isLocal ) ;
}
} )
) ,
controller: TextEditingController (
text: isLocal
? model . currentLocalDir . path
: model . currentRemoteDir . path ) ,
onSubmitted: ( path ) {
model . openDirectory ( path , isLocal: isLocal ) ;
} ,
) ) ) ,
IconButton (
onPressed: ( ) {
model . refresh ( isLocal: isLocal ) ;
2022-06-17 22:57:41 +08:00
} ,
2022-07-11 16:07:49 +08:00
icon: Icon ( Icons . refresh ) )
2022-06-17 22:57:41 +08:00
] ,
2022-07-09 19:14:40 +08:00
) ,
2022-07-11 16:07:49 +08:00
Row (
textDirection: isLocal ? TextDirection . ltr : TextDirection . rtl ,
children: [
Expanded (
child: Row (
mainAxisAlignment:
isLocal ? MainAxisAlignment . start : MainAxisAlignment . end ,
children: [
IconButton (
onPressed: ( ) {
final name = TextEditingController ( ) ;
DialogManager . show ( ( setState , close ) = >
CustomAlertDialog (
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
. getCurrentDir ( isLocal )
. path ,
name . value . text ,
model . getCurrentIsWindows (
isLocal ) ) ,
isLocal: isLocal ) ;
close ( ) ;
}
} ,
child: Text ( translate ( " OK " ) ) )
] ) ) ;
} ,
icon: Icon ( Icons . create_new_folder_outlined ) ) ,
IconButton (
onPressed: ( ) async {
final items = isLocal
? _localSelectedItems
: _remoteSelectedItems ;
debugPrint ( " remove items: ${ items . items } " ) ;
await ( model . removeAction ( items ) ) ;
items . clear ( ) ;
} ,
icon: Icon ( Icons . delete_forever_outlined ) ) ,
] ,
) ,
2022-07-09 19:14:40 +08:00
) ,
2022-07-11 16:07:49 +08:00
TextButton . icon (
onPressed: ( ) {
final items = getSelectedItem ( isLocal ) ;
model . sendFiles ( items , isRemote: ! isLocal ) ;
} ,
icon: Transform . rotate (
angle: isLocal ? 0 : pi ,
child: Icon (
Icons . send ,
color: Colors . black54 ,
) ,
) ,
label: Text (
isLocal ? translate ( ' Send ' ) : translate ( ' Receive ' ) ,
style: TextStyle (
color: Colors . black54 ,
) ,
) ) ,
] ,
) . marginOnly ( top: 8.0 )
2022-06-17 22:57:41 +08:00
] ,
) ) ;
2022-07-01 12:08:52 +08:00
Widget listTail ( { bool isLocal = false } ) {
final dir = isLocal ? model . currentLocalDir : model . currentRemoteDir ;
2022-06-17 22:57:41 +08:00
return Container (
height: 100 ,
child: Column (
children: [
Padding (
padding: EdgeInsets . fromLTRB ( 30 , 5 , 30 , 0 ) ,
child: Text (
2022-07-01 12:08:52 +08:00
dir . path ,
2022-06-17 22:57:41 +08:00
style: TextStyle ( color: MyTheme . darkGray ) ,
) ,
) ,
Padding (
padding: EdgeInsets . all ( 2 ) ,
child: Text (
2022-07-01 12:08:52 +08:00
" ${ translate ( " Total " ) } : ${ dir . entries . length } ${ translate ( " items " ) } " ,
2022-06-17 22:57:41 +08:00
style: TextStyle ( color: MyTheme . darkGray ) ,
) ,
)
] ,
) ,
) ;
}
Widget ? bottomSheet ( ) {
final state = model . jobState ;
2022-07-09 19:14:40 +08:00
final isOtherPage = _localSelectedItems . isOtherPage ( model . isLocal ) ;
2022-07-11 16:07:49 +08:00
final selectedItemsLen =
" ${ _localSelectedItems . length } ${ translate ( " items " ) } " ;
2022-07-09 19:14:40 +08:00
final local = _localSelectedItems . isLocal = = null
2022-06-17 22:57:41 +08:00
? " "
2022-07-09 19:14:40 +08:00
: " [ ${ _localSelectedItems . isLocal ! ? translate ( " Local " ) : translate ( " Remote " ) } ] " ;
2022-06-17 22:57:41 +08:00
if ( model . selectMode ) {
2022-07-09 19:14:40 +08:00
if ( _localSelectedItems . length = = 0 | | ! isOtherPage ) {
2022-06-17 22:57:41 +08:00
return BottomSheetBody (
leading: Icon ( Icons . check ) ,
title: translate ( " Selected " ) ,
text: selectedItemsLen + local ,
onCanceled: ( ) = > model . toggleSelectMode ( ) ,
actions: [
IconButton (
icon: Icon ( Icons . compare_arrows ) ,
onPressed: model . togglePage ,
) ,
IconButton (
icon: Icon ( Icons . delete_forever ) ,
onPressed: ( ) {
2022-07-09 19:14:40 +08:00
if ( _localSelectedItems . length > 0 ) {
model . removeAction ( _localSelectedItems ) ;
2022-06-17 22:57:41 +08:00
}
} ,
)
] ) ;
} else {
return BottomSheetBody (
leading: Icon ( Icons . input ) ,
title: translate ( " Paste here? " ) ,
text: selectedItemsLen + local ,
onCanceled: ( ) = > model . toggleSelectMode ( ) ,
actions: [
IconButton (
icon: Icon ( Icons . compare_arrows ) ,
onPressed: model . togglePage ,
) ,
IconButton (
icon: Icon ( Icons . paste ) ,
onPressed: ( ) {
model . toggleSelectMode ( ) ;
2022-07-09 19:14:40 +08:00
model . sendFiles ( _localSelectedItems ) ;
2022-06-17 22:57:41 +08:00
} ,
)
] ) ;
}
}
switch ( state ) {
case JobState . inProgress:
return BottomSheetBody (
leading: CircularProgressIndicator ( ) ,
title: translate ( " Waiting " ) ,
text:
" ${ translate ( " Speed " ) } : ${ readableFileSize ( model . jobProgress . speed ) } /s " ,
onCanceled: ( ) = > model . cancelJob ( model . jobProgress . id ) ,
) ;
case JobState . done:
return BottomSheetBody (
leading: Icon ( Icons . check ) ,
title: " ${ translate ( " Successful " ) } ! " ,
text: " " ,
onCanceled: ( ) = > model . jobReset ( ) ,
) ;
case JobState . error:
return BottomSheetBody (
leading: Icon ( Icons . error ) ,
title: " ${ translate ( " Error " ) } ! " ,
text: " " ,
onCanceled: ( ) = > model . jobReset ( ) ,
) ;
case JobState . none:
break ;
}
return null ;
}
2022-07-01 17:17:25 +08:00
List < BreadCrumbItem > getPathBreadCrumbItems ( void Function ( ) onHome ,
void Function ( List < String > ) onPressed , bool isLocal ) {
2022-06-27 16:44:34 +08:00
final path = model . shortPath ( isLocal ) ;
2022-06-17 22:57:41 +08:00
final list = PathUtil . split ( path , model . currentIsWindows ) ;
final breadCrumbList = [
BreadCrumbItem (
content: IconButton (
icon: Icon ( Icons . home_filled ) ,
onPressed: onHome ,
) )
] ;
breadCrumbList . addAll ( list . asMap ( ) . entries . map ( ( e ) = > BreadCrumbItem (
content: TextButton (
child: Text ( e . value ) ,
style:
ButtonStyle ( minimumSize: MaterialStateProperty . all ( Size ( 0 , 0 ) ) ) ,
onPressed: ( ) = > onPressed ( list . sublist ( 0 , e . key + 1 ) ) ) ) ) ) ;
return breadCrumbList ;
}
@ override
bool get wantKeepAlive = > true ;
2022-07-11 16:07:49 +08:00
/// Get the image for the current [platform].
Widget getPlatformImage ( String platform ) {
platform = platform . toLowerCase ( ) ;
if ( platform = = ' mac os ' )
platform = ' mac ' ;
else if ( platform ! = ' linux ' & & platform ! = ' android ' ) platform = ' win ' ;
return Image . asset ( ' assets/ $ platform .png ' , width: 25 , height: 25 ) ;
}
2022-06-17 22:57:41 +08:00
}
class BottomSheetBody extends StatelessWidget {
BottomSheetBody (
{ required this . leading ,
required this . title ,
required this . text ,
this . onCanceled ,
this . actions } ) ;
final Widget leading ;
final String title ;
final String text ;
final VoidCallback ? onCanceled ;
final List < IconButton > ? actions ;
@ override
BottomSheet build ( BuildContext context ) {
final _actions = actions ? ? [ ] ;
return BottomSheet (
builder: ( BuildContext context ) {
return Container (
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: [
leading ,
SizedBox ( width: 16 ) ,
Column (
mainAxisAlignment: MainAxisAlignment . center ,
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
Text ( title , style: TextStyle ( fontSize: 18 ) ) ,
Text ( text ,
style: TextStyle (
fontSize: 14 , color: MyTheme . grayBg ) )
] ,
)
] ,
) ,
Row ( children: ( ) {
_actions . add ( IconButton (
icon: Icon ( Icons . cancel_outlined ) ,
onPressed: onCanceled ,
) ) ;
return _actions ;
} ( ) )
] ,
) ,
) ) ;
} ,
onClosing: ( ) { } ,
backgroundColor: MyTheme . grayBg ,
enableDrag: false ,
) ;
}
}