diff --git a/lib/main.dart b/lib/main.dart index b396836..cc30c25 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,6 +11,7 @@ class MainApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( + debugShowCheckedModeBanner: false, theme: ThemeData( useMaterial3: true, colorSchemeSeed: Colors.teal, diff --git a/lib/sftp_explorer.dart b/lib/sftp_explorer.dart index 91e5b84..f2de2d6 100644 --- a/lib/sftp_explorer.dart +++ b/lib/sftp_explorer.dart @@ -20,6 +20,8 @@ class _SftpExplorerState extends State { bool _isLoading = true; late List _dirContents; + + double? _progress; @override void initState() { @@ -45,7 +47,29 @@ class _SftpExplorerState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( + toolbarHeight: 75, title: Text('Explorer'), + elevation: 2, + actionsPadding: EdgeInsets.only(right: 20), + actions: [ + if (_progress != null) + Stack( + alignment: Alignment.center, + children: [ + TweenAnimationBuilder( + tween: Tween(begin: 0, end: _progress), + duration: Duration(milliseconds: 300), + builder: (context, value, _) => CircularProgressIndicator(strokeWidth: 3, value: value,) + ), + IconButton( + onPressed: () { + + }, + icon: Icon(Icons.upload) + ), + ] + ), + ], ), floatingActionButton: _buildFABs(context), body: _isLoading ? Center(child: CircularProgressIndicator()) : ListView.builder( @@ -234,7 +258,18 @@ class _SftpExplorerState extends State { final files = await openFiles(); filePaths = files.map((file) => file.path).toList(); } - await widget.sftpWorker.uploadFiles(widget.path, filePaths); + try { + await for (final progress in widget.sftpWorker.uploadFiles(widget.path, filePaths)) { + print(progress); + setState(() => _progress = progress); + } + } + catch (e) { + if (context.mounted) { + ScaffoldMessenger.of(context).showSnackBar(_buildErrorSnackBar(context, e.toString())); + } + } + setState(() => _progress = null); _listDir(); }, child: Icon(Icons.upload), diff --git a/lib/sftp_worker.dart b/lib/sftp_worker.dart index 02bd17c..61966e0 100644 --- a/lib/sftp_worker.dart +++ b/lib/sftp_worker.dart @@ -24,7 +24,7 @@ class SftpWorker { final ReceivePort _responses; final SendPort _commands; - final Map> _activeRequests = {}; + final Map _activeRequests = {}; int _idCounter = 0; SftpWorker._(this._responses, this._commands) { @@ -98,31 +98,52 @@ class SftpWorker { '$path${basename(filePath)}', mode: SftpFileOpenMode.create | SftpFileOpenMode.write | SftpFileOpenMode.exclusive ); + bool timeout = true; await remoteFile.write( file.openRead().cast(), onProgress: (progress) { - print(progress/fileSize); + if (timeout) { + timeout = false; + sendPort.send((id, progress/fileSize)); + Future.delayed(Duration(seconds: 2), () => timeout = true); + } } ); } on SftpStatusError catch (e) { sendPort.send((id, RemoteError(e.message, ''))); } + sendPort.send((id, 1.0)); } - sendPort.send((id, 0)); } }); } void _sftpResponseHandler(dynamic message) { final (int id, Object response) = message; - final completer = _activeRequests.remove(id)!; - if (response is RemoteError) { - completer.completeError(response); + if (_activeRequests[id] is Completer) { + final completer = _activeRequests.remove(id)!; + + if (response is RemoteError) { + completer.completeError(response); + } + else { + completer.complete(response); + } } - else { - completer.complete(response); + else if (_activeRequests[id] is StreamController) { + final controller = _activeRequests[id] as StreamController; + if (response is RemoteError) { + controller.addError(response); + } + else { + controller.add(response); + if (response == 1) { + controller.close(); + _activeRequests.remove(id); + } + } } } @@ -136,12 +157,12 @@ class SftpWorker { } - Future uploadFiles(String path, List filePaths) async { - final completer = Completer.sync(); + Stream uploadFiles(String path, List filePaths) { + final controller = StreamController(); final id = _idCounter++; - _activeRequests[id] = completer; + _activeRequests[id] = controller; _commands.send((id, UploadFiles(path, filePaths))); - await completer.future; + return controller.stream; }