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 {
FileManagerPage ( { Key ? key , required this . id } ) : super ( key: key ) ;
final String id ;
@ 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 ( ) ;
2022-09-27 20:35:02 +08:00
gFFI . start ( widget . id , isFileTransfer: true ) ;
2022-08-12 18:42:02 +08:00
WidgetsBinding . instance . addPostFrameCallback ( ( _ ) {
gFFI . dialogManager
2022-08-16 21:27:21 +08:00
. showLoading ( translate ( ' Connecting... ' ) , onCancel: closeConnection ) ;
2022-08-12 18:42:02 +08:00
} ) ;
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 ( ) ;
gFFI . dialogManager
2023-05-08 12:34:19 +08:00
. show ( ( setState , close , context ) = > CustomAlertDialog (
2023-03-09 21:09:17 +09:00
title: Text ( translate ( " Create Folder " ) ) ,
content: Column (
mainAxisSize: MainAxisSize . min ,
children: [
TextFormField (
decoration: InputDecoration (
labelText: translate (
" Please enter the folder name " ) ,
2022-04-07 20:40:51 +08:00
) ,
2023-03-09 21:09:17 +09:00
controller: name ,
) ,
] ,
) ,
actions: [
dialogButton ( " Cancel " ,
onPressed: ( ) = > close ( false ) ,
isOutline: true ) ,
dialogButton ( " OK " , onPressed: ( ) {
if ( name . value . text . isNotEmpty ) {
currentFileController . createDir (
PathUtil . join (
currentDir . path ,
name . value . text ,
currentOptions . isWindows ) ) ;
close ( ) ;
}
} )
] ) ) ;
} 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 ,
)
] ;
} ,
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 ( ( ) { } ) ;
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 ) {
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
}