opt.: cancel sftp mission

This commit is contained in:
lollipopkit
2023-07-29 18:24:22 +08:00
parent e13c5910ec
commit 0f83d10bfa
12 changed files with 277 additions and 230 deletions

View File

@@ -0,0 +1,5 @@
extension DateTimeX on DateTime {
String get hourMinute {
return '${hour.toString().padLeft(2, '0')}:${minute.toString().padLeft(2, '0')}';
}
}

View File

@@ -24,6 +24,8 @@ class PathWithPrefix {
_path = pathJoin(_path, newPath); _path = pathJoin(_path, newPath);
} }
bool get canBack => path != '$_prefixPath/';
bool undo() { bool undo() {
if (_prePath == null || _path == _prePath) { if (_prePath == null || _path == _prePath) {
return false; return false;

View File

@@ -94,7 +94,7 @@ Future<void> _download(
return; return;
} }
// Read 10m each time // Read 10m each time
const defaultChunkSize = 1024 * 1024 * 10; const defaultChunkSize = 1024 * 1024;
final chunkSize = size > defaultChunkSize ? defaultChunkSize : size; final chunkSize = size > defaultChunkSize ? defaultChunkSize : size;
mainSendPort.send(size); mainSendPort.send(size);
mainSendPort.send(SftpWorkerStatus.downloading); mainSendPort.send(SftpWorkerStatus.downloading);

View File

@@ -8,21 +8,8 @@ class SftpProvider extends ProviderBase {
final List<SftpReqStatus> _status = []; final List<SftpReqStatus> _status = [];
List<SftpReqStatus> get status => _status; List<SftpReqStatus> get status => _status;
Iterable<SftpReqStatus> gets({int? id, String? fileName}) { SftpReqStatus? get(int id) {
Iterable<SftpReqStatus> found = []; return _status.singleWhere((element) => element.id == id);
if (id != null) {
found = _status.where((e) => e.id == id);
}
if (fileName != null) {
found = found.where((e) => e.req.localPath.split('/').last == fileName);
}
return found;
}
SftpReqStatus? get({int? id, String? name}) {
final found = gets(id: id, fileName: name);
if (found.isEmpty) return null;
return found.first;
} }
void add(SftpReq req, {Completer? completer}) { void add(SftpReq req, {Completer? completer}) {
@@ -32,4 +19,19 @@ class SftpProvider extends ProviderBase {
req: req, req: req,
)); ));
} }
@override
void dispose() {
for (final item in _status) {
item.worker.dispose();
}
super.dispose();
}
void cancel(int id) {
final idx = _status.indexWhere((element) => element.id == id);
_status[idx].worker.dispose();
_status.removeAt(idx);
notifyListeners();
}
} }

View File

@@ -25,7 +25,7 @@ import 'convert.dart';
import 'debug.dart'; import 'debug.dart';
import 'private_key/list.dart'; import 'private_key/list.dart';
import 'setting.dart'; import 'setting.dart';
import 'sftp/local.dart'; import 'storage/local.dart';
class HomePage extends StatefulWidget { class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key); const HomePage({Key? key}) : super(key: key);
@@ -219,7 +219,7 @@ class _HomePageState extends State<HomePage>
leading: const Icon(Icons.download), leading: const Icon(Icons.download),
title: Text(_s.download), title: Text(_s.download),
onTap: () => AppRoute( onTap: () => AppRoute(
const SFTPDownloadedPage(), const LocalStoragePage(),
'sftp local page', 'sftp local page',
).go(context), ).go(context),
), ),

View File

@@ -291,9 +291,8 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
pwd: authorization, pwd: authorization,
pubKeyId: usePublicKey ? _keyInfo!.id : null, pubKeyId: usePublicKey ? _keyInfo!.id : null,
tags: _tags, tags: _tags,
alterUrl: _alterUrlController.text == '' alterUrl:
? null _alterUrlController.text == '' ? null : _alterUrlController.text,
: _alterUrlController.text,
); );
if (widget.spi == null) { if (widget.spi == null) {

View File

@@ -29,7 +29,7 @@ import '../../widget/popup_menu.dart';
import '../../widget/round_rect_card.dart'; import '../../widget/round_rect_card.dart';
import '../docker.dart'; import '../docker.dart';
import '../pkg.dart'; import '../pkg.dart';
import '../sftp/remote.dart'; import '../storage/sftp.dart';
import '../ssh/term.dart'; import '../ssh/term.dart';
import 'detail.dart'; import 'detail.dart';
import 'edit.dart'; import 'edit.dart';
@@ -286,7 +286,7 @@ class _ServerPageState extends State<ServerPage>
AppRoute(PkgManagePage(spi), 'pkg manage').go(context); AppRoute(PkgManagePage(spi), 'pkg manage').go(context);
break; break;
case ServerTabMenuType.sftp: case ServerTabMenuType.sftp:
AppRoute(SFTPPage(spi), 'SFTP').go(context); AppRoute(SftpPage(spi), 'SFTP').go(context);
break; break;
case ServerTabMenuType.snippet: case ServerTabMenuType.snippet:
final provider = locator<SnippetProvider>(); final provider = locator<SnippetProvider>();

View File

@@ -1,141 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:provider/provider.dart';
import '../../../core/extension/numx.dart';
import '../../../core/utils/misc.dart';
import '../../../core/utils/ui.dart';
import '../../../data/model/sftp/req.dart';
import '../../../data/provider/sftp.dart';
import '../../../data/res/ui.dart';
import '../../widget/round_rect_card.dart';
class SFTPDownloadingPage extends StatefulWidget {
const SFTPDownloadingPage({Key? key}) : super(key: key);
@override
_SFTPDownloadingPageState createState() => _SFTPDownloadingPageState();
}
class _SFTPDownloadingPageState extends State<SFTPDownloadingPage> {
late S _s;
@override
void didChangeDependencies() {
super.didChangeDependencies();
_s = S.of(context)!;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
_s.mission,
style: textSize18,
),
),
body: _buildBody(),
);
}
Widget _buildBody() {
return Consumer<SftpProvider>(builder: (__, pro, _) {
if (pro.status.isEmpty) {
return Center(
child: Text(_s.sftpNoDownloadTask),
);
}
return ListView.builder(
padding: const EdgeInsets.all(11),
itemCount: pro.status.length,
itemBuilder: (context, index) {
final status = pro.status[index];
return _buildItem(status);
},
);
});
}
Widget _wrapInCard(SftpReqStatus status, String? subtitle,
{Widget? trailing}) {
return RoundRectCard(
ListTile(
title: Text(
status.fileName,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
subtitle: subtitle == null
? null
: Text(
subtitle,
style: grey,
),
trailing: trailing,
),
);
}
Widget _buildItem(SftpReqStatus status) {
if (status.error != null) {
final err = status.error.toString();
Future.delayed(
const Duration(milliseconds: 377),
() => showSnackBar(context, Text(err)),
);
status.error = null;
}
switch (status.status) {
case SftpWorkerStatus.finished:
final time = status.spentTime.toString();
final str = '${_s.finished} ${_s.spentTime(
time == 'null' ? _s.unknown : (time.substring(0, time.length - 7)),
)}';
return _wrapInCard(
status,
str,
trailing: IconButton(
onPressed: () => shareFiles(context, [status.req.localPath]),
icon: const Icon(Icons.open_in_new),
),
);
case SftpWorkerStatus.downloading:
final percentStr = (status.progress ?? 0.0).toStringAsFixed(2);
final percent = (status.progress ?? 0) / 100;
final size = (status.size ?? 0).convertBytes;
return _wrapInCard(
status,
_s.downloadStatus(percentStr, size),
trailing: SizedBox(
height: 27,
width: 27,
child: CircularProgressIndicator(
value: percent,
),
),
);
case SftpWorkerStatus.preparing:
return _wrapInCard(
status,
_s.sftpDlPrepare,
trailing: _loading,
);
case SftpWorkerStatus.sshConnectted:
return _wrapInCard(
status,
_s.sftpSSHConnected,
trailing: _loading,
);
default:
return _wrapInCard(
status,
_s.unknown,
trailing: const Icon(Icons.error),
);
}
}
}
const _loading =
SizedBox(height: 27, width: 27, child: CircularProgressIndicator());

View File

@@ -23,7 +23,7 @@ import '../../../data/res/color.dart';
import '../../../data/res/terminal.dart'; import '../../../data/res/terminal.dart';
import '../../../data/store/setting.dart'; import '../../../data/store/setting.dart';
import '../../../locator.dart'; import '../../../locator.dart';
import '../sftp/remote.dart'; import '../storage/sftp.dart';
class SSHPage extends StatefulWidget { class SSHPage extends StatefulWidget {
final ServerPrivateInfo spi; final ServerPrivateInfo spi;
@@ -260,7 +260,7 @@ class _SSHPageState extends State<SSHPage> {
return; return;
} }
AppRoute( AppRoute(
SFTPPage( SftpPage(
widget.spi, widget.spi,
initPath: initPath, initPath: initPath,
), ),

View File

@@ -9,7 +9,7 @@ import 'package:toolbox/data/provider/sftp.dart';
import 'package:toolbox/data/res/misc.dart'; import 'package:toolbox/data/res/misc.dart';
import 'package:toolbox/locator.dart'; import 'package:toolbox/locator.dart';
import 'package:toolbox/view/page/editor.dart'; import 'package:toolbox/view/page/editor.dart';
import 'package:toolbox/view/page/sftp/remote.dart'; import 'package:toolbox/view/page/storage/sftp.dart';
import 'package:toolbox/view/widget/input_field.dart'; import 'package:toolbox/view/widget/input_field.dart';
import 'package:toolbox/view/widget/picker.dart'; import 'package:toolbox/view/widget/picker.dart';
import 'package:toolbox/view/widget/round_rect_card.dart'; import 'package:toolbox/view/widget/round_rect_card.dart';
@@ -23,20 +23,20 @@ import '../../../data/model/app/path_with_prefix.dart';
import '../../../data/res/path.dart'; import '../../../data/res/path.dart';
import '../../../data/res/ui.dart'; import '../../../data/res/ui.dart';
import '../../widget/fade_in.dart'; import '../../widget/fade_in.dart';
import 'mission.dart'; import 'sftp_mission.dart';
class SFTPDownloadedPage extends StatefulWidget { class LocalStoragePage extends StatefulWidget {
final bool isPickFile; final bool isPickFile;
const SFTPDownloadedPage({Key? key, this.isPickFile = false}) final String? initDir;
const LocalStoragePage({Key? key, this.isPickFile = false, this.initDir})
: super(key: key); : super(key: key);
@override @override
State<SFTPDownloadedPage> createState() => _SFTPDownloadedPageState(); State<LocalStoragePage> createState() => _LocalStoragePageState();
} }
class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> { class _LocalStoragePageState extends State<LocalStoragePage> {
PathWithPrefix? _path; PathWithPrefix? _path;
String? _prefixPath;
late S _s; late S _s;
@override @override
@@ -44,7 +44,9 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
super.initState(); super.initState();
sftpDir.then((dir) { sftpDir.then((dir) {
_path = PathWithPrefix(dir.path); _path = PathWithPrefix(dir.path);
_prefixPath = '${dir.path}/'; if (widget.initDir != null) {
_path!.update(widget.initDir!.replaceFirst('${dir.path}/', ''));
}
setState(() {}); setState(() {});
}); });
} }
@@ -64,7 +66,7 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
IconButton( IconButton(
icon: const Icon(Icons.downloading), icon: const Icon(Icons.downloading),
onPressed: () => onPressed: () =>
AppRoute(const SFTPDownloadingPage(), 'sftp downloading') AppRoute(const SftpMissionPage(), 'sftp downloading')
.go(context), .go(context),
) )
], ],
@@ -100,7 +102,7 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
} }
final dir = Directory(_path!.path); final dir = Directory(_path!.path);
final files = dir.listSync(); final files = dir.listSync();
final canGoBack = _path!.path != _prefixPath; final canGoBack = _path!.canBack;
return ListView.builder( return ListView.builder(
itemCount: canGoBack ? files.length + 1 : files.length, itemCount: canGoBack ? files.length + 1 : files.length,
padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 7), padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 7),
@@ -126,7 +128,7 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
? const Icon(Icons.folder) ? const Icon(Icons.folder)
: const Icon(Icons.insert_drive_file), : const Icon(Icons.insert_drive_file),
title: Text(fileName), title: Text(fileName),
subtitle: isDir ? null : Text(stat.size.convertBytes), subtitle: isDir ? null : Text(stat.size.convertBytes, style: grey),
trailing: Text( trailing: Text(
stat.modified stat.modified
.toString() .toString()
@@ -266,18 +268,16 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
final id = ids[idx]; final id = ids[idx];
final spi = serverProvider.servers[id]?.spi; final spi = serverProvider.servers[id]?.spi;
if (spi == null) { if (spi == null) {
showSnackBar(context, Text(_s.noResult));
return; return;
} }
final remotePath = await AppRoute( final remotePath = await AppRoute(
SFTPPage( SftpPage(
spi, spi,
selectPath: true, selectPath: true,
), ),
'SFTP page (select)', 'SFTP page (select)',
).go<String>(context); ).go<String>(context);
if (remotePath == null) { if (remotePath == null) {
showSnackBar(context, Text(_s.fieldMustNotEmpty));
return; return;
} }
locator<SftpProvider>().add(SftpReq( locator<SftpProvider>().add(SftpReq(

View File

@@ -8,7 +8,7 @@ import 'package:toolbox/core/extension/navigator.dart';
import 'package:toolbox/core/extension/sftpfile.dart'; import 'package:toolbox/core/extension/sftpfile.dart';
import 'package:toolbox/data/res/misc.dart'; import 'package:toolbox/data/res/misc.dart';
import 'package:toolbox/view/page/editor.dart'; import 'package:toolbox/view/page/editor.dart';
import 'package:toolbox/view/page/sftp/local.dart'; import 'package:toolbox/view/page/storage/local.dart';
import 'package:toolbox/view/widget/round_rect_card.dart'; import 'package:toolbox/view/widget/round_rect_card.dart';
import '../../../core/extension/numx.dart'; import '../../../core/extension/numx.dart';
@@ -29,14 +29,14 @@ import '../../../locator.dart';
import '../../widget/fade_in.dart'; import '../../widget/fade_in.dart';
import '../../widget/input_field.dart'; import '../../widget/input_field.dart';
import '../../widget/two_line_text.dart'; import '../../widget/two_line_text.dart';
import 'mission.dart'; import 'sftp_mission.dart';
class SFTPPage extends StatefulWidget { class SftpPage extends StatefulWidget {
final ServerPrivateInfo spi; final ServerPrivateInfo spi;
final String? initPath; final String? initPath;
final bool selectPath; final bool selectPath;
const SFTPPage( const SftpPage(
this.spi, { this.spi, {
Key? key, Key? key,
this.initPath, this.initPath,
@@ -44,10 +44,10 @@ class SFTPPage extends StatefulWidget {
}) : super(key: key); }) : super(key: key);
@override @override
_SFTPPageState createState() => _SFTPPageState(); _SftpPageState createState() => _SftpPageState();
} }
class _SFTPPageState extends State<SFTPPage> { class _SftpPageState extends State<SftpPage> {
final SftpBrowserStatus _status = SftpBrowserStatus(); final SftpBrowserStatus _status = SftpBrowserStatus();
final ScrollController _scrollController = ScrollController(); final ScrollController _scrollController = ScrollController();
@@ -82,7 +82,7 @@ class _SFTPPageState extends State<SFTPPage> {
IconButton( IconButton(
icon: const Icon(Icons.downloading), icon: const Icon(Icons.downloading),
onPressed: () => AppRoute( onPressed: () => AppRoute(
const SFTPDownloadingPage(), const SftpMissionPage(),
'sftp downloading', 'sftp downloading',
).go(context), ).go(context),
), ),
@@ -154,7 +154,7 @@ class _SFTPPageState extends State<SFTPPage> {
switch (idx) { switch (idx) {
case 0: case 0:
return await AppRoute( return await AppRoute(
const SFTPDownloadedPage( const LocalStoragePage(
isPickFile: true, isPickFile: true,
), ),
'sftp dled pick') 'sftp dled pick')
@@ -262,7 +262,8 @@ class _SFTPPageState extends State<SFTPPage> {
_status.path = AbsolutePath(p_); _status.path = AbsolutePath(p_);
_listDir(path: p_, client: _client); _listDir(path: p_, client: _client);
return centerLoading; return centerLoading;
} else { }
return RefreshIndicator( return RefreshIndicator(
child: FadeIn( child: FadeIn(
key: Key(widget.spi.name + _status.path!.path), key: Key(widget.spi.name + _status.path!.path),
@@ -270,19 +271,30 @@ class _SFTPPageState extends State<SFTPPage> {
itemCount: _status.files!.length, itemCount: _status.files!.length,
controller: _scrollController, controller: _scrollController,
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3), padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
itemBuilder: (context, index) { itemBuilder: (_, index) => _buildItem(_status.files![index]),
final file = _status.files![index]; ),
),
onRefresh: () => _listDir(path: _status.path?.path),
);
}
Widget _buildItem(SftpName file) {
final isDir = file.attr.isDirectory; final isDir = file.attr.isDirectory;
final trailing = Text(
'${getTime(file.attr.modifyTime)}\n${file.attr.mode?.str ?? ''}',
style: grey,
textAlign: TextAlign.right,
);
return RoundRectCard(ListTile( return RoundRectCard(ListTile(
leading: Icon(isDir ? Icons.folder : Icons.insert_drive_file), leading: Icon(isDir ? Icons.folder : Icons.insert_drive_file),
title: Text(file.filename), title: Text(file.filename),
trailing: Text( trailing: trailing,
'${getTime(file.attr.modifyTime)}\n${file.attr.mode?.str ?? ''}', subtitle: isDir
style: const TextStyle(color: Colors.grey), ? null
textAlign: TextAlign.right, : Text(
(file.attr.size ?? 0).convertBytes,
style: grey,
), ),
subtitle:
isDir ? null : Text((file.attr.size ?? 0).convertBytes),
onTap: () { onTap: () {
if (isDir) { if (isDir) {
_status.path?.update(file.filename); _status.path?.update(file.filename);
@@ -293,12 +305,6 @@ class _SFTPPageState extends State<SFTPPage> {
}, },
onLongPress: () => _onItemPress(context, file, !isDir), onLongPress: () => _onItemPress(context, file, !isDir),
)); ));
},
),
),
onRefresh: () => _listDir(path: _status.path?.path),
);
}
} }
void _onItemPress(BuildContext context, SftpName file, bool notDir) { void _onItemPress(BuildContext context, SftpName file, bool notDir) {
@@ -479,7 +485,7 @@ class _SFTPPageState extends State<SFTPPage> {
child: Text(_s.cancel), child: Text(_s.cancel),
), ),
TextButton( TextButton(
onPressed: () { onPressed: () async {
if (textController.text == '') { if (textController.text == '') {
showRoundDialog( showRoundDialog(
context: context, context: context,
@@ -493,8 +499,8 @@ class _SFTPPageState extends State<SFTPPage> {
); );
return; return;
} }
_status.client! final dir = '${_status.path!.path}/${textController.text}';
.mkdir('${_status.path!.path}/${textController.text}'); await _status.client!.mkdir(dir);
context.pop(); context.pop();
_listDir(); _listDir();
}, },
@@ -538,9 +544,9 @@ class _SFTPPageState extends State<SFTPPage> {
); );
return; return;
} }
(await _status.client! final path = '${_status.path!.path}/${textController.text}';
.open('${_status.path!.path}/${textController.text}')) final file = await _status.client!.open(path);
.writeBytes(Uint8List(0)); await file.writeBytes(Uint8List(0));
context.pop(); context.pop();
_listDir(); _listDir();
}, },

View File

@@ -0,0 +1,174 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:provider/provider.dart';
import 'package:toolbox/core/extension/datetime.dart';
import 'package:toolbox/core/extension/navigator.dart';
import 'package:toolbox/core/route.dart';
import 'package:toolbox/locator.dart';
import 'package:toolbox/view/page/storage/local.dart';
import '../../../core/extension/numx.dart';
import '../../../core/utils/misc.dart';
import '../../../core/utils/ui.dart';
import '../../../data/model/sftp/req.dart';
import '../../../data/provider/sftp.dart';
import '../../../data/res/ui.dart';
import '../../widget/round_rect_card.dart';
class SftpMissionPage extends StatefulWidget {
const SftpMissionPage({Key? key}) : super(key: key);
@override
_SftpMissionPageState createState() => _SftpMissionPageState();
}
class _SftpMissionPageState extends State<SftpMissionPage> {
late S _s;
@override
void didChangeDependencies() {
super.didChangeDependencies();
_s = S.of(context)!;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
_s.mission,
style: textSize18,
),
),
body: _buildBody(),
);
}
Widget _buildBody() {
return Consumer<SftpProvider>(builder: (__, pro, _) {
if (pro.status.isEmpty) {
return Center(
child: Text(_s.sftpNoDownloadTask),
);
}
return ListView.builder(
padding: const EdgeInsets.all(11),
itemCount: pro.status.length,
itemBuilder: (context, index) {
final status = pro.status[index];
return _buildItem(status);
},
);
});
}
Widget _buildItem(SftpReqStatus status) {
switch (status.status) {
case SftpWorkerStatus.finished:
final time = status.spentTime.toString();
final str = '${_s.finished} ${_s.spentTime(
time == 'null' ? _s.unknown : (time.substring(0, time.length - 7)),
)}';
return _wrapInCard(
status: status,
subtitle: str,
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () {
final idx = status.req.localPath.lastIndexOf('/');
final dir = status.req.localPath.substring(0, idx);
AppRoute(
LocalStoragePage(initDir: dir),
'sftp local',
).go(context);
},
icon: const Icon(Icons.file_open)),
IconButton(
onPressed: () => shareFiles(context, [status.req.localPath]),
icon: const Icon(Icons.open_in_new),
)
],
),
);
case SftpWorkerStatus.downloading:
final percentStr = (status.progress ?? 0.0).toStringAsFixed(2);
final size = (status.size ?? 0).convertBytes;
return _wrapInCard(
status: status,
subtitle: _s.downloadStatus(percentStr, size),
trailing: _buildDelete(status.fileName, status.id),
);
case SftpWorkerStatus.preparing:
return _wrapInCard(
status: status,
subtitle: _s.sftpDlPrepare,
trailing: _buildDelete(status.fileName, status.id),
);
case SftpWorkerStatus.sshConnectted:
return _wrapInCard(
status: status,
subtitle: _s.sftpSSHConnected,
trailing: _buildDelete(status.fileName, status.id),
);
default:
return _wrapInCard(
status: status,
subtitle: _s.unknown,
trailing: IconButton(
onPressed: () => showRoundDialog(
context: context,
title: Text(_s.error),
child: Text((status.error ?? _s.unknown).toString()),
),
icon: const Icon(Icons.error),
),
);
}
}
Widget _wrapInCard({
required SftpReqStatus status,
String? subtitle,
Widget? trailing,
}) {
final time = DateTime.fromMicrosecondsSinceEpoch(status.id);
return RoundRectCard(
ListTile(
leading: Text(time.hourMinute),
title: Text(
status.fileName,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
subtitle: subtitle == null
? null
: Text(
subtitle,
style: grey,
),
trailing: trailing,
),
);
}
Widget _buildDelete(String name, int id) {
return IconButton(
onPressed: () => showRoundDialog(
context: context,
title: Text(_s.attention),
child: Text(_s.sureDelete(name)),
actions: [
TextButton(
onPressed: () {
locator<SftpProvider>().cancel(id);
context.pop();
},
child: Text(_s.ok),
),
]),
icon: const Icon(Icons.delete),
);
}
}