mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 15:24:35 +01:00
New feat
- SFTP download - open downloaded files in other apps
This commit is contained in:
@@ -8,6 +8,8 @@ PODS:
|
|||||||
- Flutter
|
- Flutter
|
||||||
- r_upgrade (0.0.1):
|
- r_upgrade (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- share_plus (0.0.1):
|
||||||
|
- Flutter
|
||||||
- url_launcher_ios (0.0.1):
|
- url_launcher_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
|
||||||
@@ -17,6 +19,7 @@ DEPENDENCIES:
|
|||||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||||
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
|
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
|
||||||
- r_upgrade (from `.symlinks/plugins/r_upgrade/ios`)
|
- r_upgrade (from `.symlinks/plugins/r_upgrade/ios`)
|
||||||
|
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
@@ -30,6 +33,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/path_provider_ios/ios"
|
:path: ".symlinks/plugins/path_provider_ios/ios"
|
||||||
r_upgrade:
|
r_upgrade:
|
||||||
:path: ".symlinks/plugins/r_upgrade/ios"
|
:path: ".symlinks/plugins/r_upgrade/ios"
|
||||||
|
share_plus:
|
||||||
|
:path: ".symlinks/plugins/share_plus/ios"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||||
|
|
||||||
@@ -39,6 +44,7 @@ SPEC CHECKSUMS:
|
|||||||
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
|
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
|
||||||
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
|
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
|
||||||
r_upgrade: 44d715c61914cce3d01ea225abffe894fd51c114
|
r_upgrade: 44d715c61914cce3d01ea225abffe894fd51c114
|
||||||
|
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
|
||||||
url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
|
url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
|
||||||
|
|
||||||
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
|
PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
|
||||||
|
|||||||
@@ -354,7 +354,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 125;
|
CURRENT_PROJECT_VERSION = 126;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@@ -362,7 +362,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.125;
|
MARKETING_VERSION = 1.0.126;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
@@ -484,7 +484,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 125;
|
CURRENT_PROJECT_VERSION = 126;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@@ -492,7 +492,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.125;
|
MARKETING_VERSION = 1.0.126;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
@@ -508,7 +508,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 125;
|
CURRENT_PROJECT_VERSION = 126;
|
||||||
DEVELOPMENT_TEAM = BA88US33G6;
|
DEVELOPMENT_TEAM = BA88US33G6;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@@ -516,7 +516,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.0.125;
|
MARKETING_VERSION = 1.0.126;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
|||||||
11
lib/core/extension/colorx.dart
Normal file
11
lib/core/extension/colorx.dart
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
extension ColorX on Color {
|
||||||
|
bool get isBrightColor {
|
||||||
|
return getBrightnessFromColor == Brightness.light;
|
||||||
|
}
|
||||||
|
|
||||||
|
Brightness get getBrightnessFromColor {
|
||||||
|
return ThemeData.estimateBrightnessForColor(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:toolbox/data/model/distribution.dart';
|
import 'package:toolbox/data/model/distribution.dart';
|
||||||
|
|
||||||
extension StringX on String {
|
extension StringX on String {
|
||||||
@@ -21,4 +22,40 @@ extension StringX on String {
|
|||||||
Uri get uri {
|
Uri get uri {
|
||||||
return Uri.parse(this);
|
return Uri.parse(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget omitStartStr(
|
||||||
|
{TextStyle? style, TextOverflow? overflow, int? maxLines}) {
|
||||||
|
return LayoutBuilder(builder: (context, size) {
|
||||||
|
bool exceeded = false;
|
||||||
|
int len = 0;
|
||||||
|
for (; !exceeded && len < length; len++) {
|
||||||
|
// Build the textspan
|
||||||
|
var span = TextSpan(
|
||||||
|
text: 'A' * 7 + substring(length - len),
|
||||||
|
style: style ?? Theme.of(context).textTheme.bodyText2,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Use a textpainter to determine if it will exceed max lines
|
||||||
|
var tp = TextPainter(
|
||||||
|
maxLines: maxLines ?? 1,
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
text: span,
|
||||||
|
);
|
||||||
|
|
||||||
|
// trigger it to layout
|
||||||
|
tp.layout(maxWidth: size.maxWidth);
|
||||||
|
|
||||||
|
// whether the text overflowed or not
|
||||||
|
exceeded = tp.didExceedMaxLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Text(
|
||||||
|
(exceeded ? '...' : '') + substring(length - len),
|
||||||
|
overflow: overflow ?? TextOverflow.fade,
|
||||||
|
softWrap: false,
|
||||||
|
maxLines: maxLines ?? 1,
|
||||||
|
style: style,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
|
|
||||||
Future<Directory> get sftpDownloadDir async {
|
|
||||||
final docDir = await getApplicationDocumentsDirectory();
|
|
||||||
final dir = Directory('${docDir.path}/sftp');
|
|
||||||
if (!dir.existsSync()) {
|
|
||||||
dir.createSync();
|
|
||||||
}
|
|
||||||
return dir;
|
|
||||||
}
|
|
||||||
38
lib/data/model/app/path_with_prefix.dart
Normal file
38
lib/data/model/app/path_with_prefix.dart
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
class PathWithPrefix {
|
||||||
|
late String _prefixPath;
|
||||||
|
String _path = '/';
|
||||||
|
String? _prePath;
|
||||||
|
String get path => _prefixPath + _path;
|
||||||
|
|
||||||
|
PathWithPrefix(String prefixPath) {
|
||||||
|
if (prefixPath.endsWith('/')) {
|
||||||
|
_prefixPath = prefixPath.substring(0, prefixPath.length - 1);
|
||||||
|
} else {
|
||||||
|
_prefixPath = prefixPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(String newPath) {
|
||||||
|
_prePath = _path;
|
||||||
|
if (newPath == '..') {
|
||||||
|
_path = _path.substring(0, _path.lastIndexOf('/'));
|
||||||
|
if (_path == '') {
|
||||||
|
_path = '/';
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newPath == '/') {
|
||||||
|
_path = '/';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_path = _path + (_path.endsWith('/') ? '' : '/') + newPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool undo() {
|
||||||
|
if (_prePath == null || _path == _prePath) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_path = _prePath!;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ class AbsolutePath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool undo() {
|
bool undo() {
|
||||||
if (_prePath == null) {
|
if (_prePath == null || _prePath == path) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
path = _prePath!;
|
path = _prePath!;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'package:dartssh2/dartssh2.dart';
|
|||||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
import 'package:toolbox/data/model/server/server_private_info.dart';
|
||||||
import 'package:toolbox/data/model/sftp/absolute_path.dart';
|
import 'package:toolbox/data/model/sftp/absolute_path.dart';
|
||||||
|
|
||||||
class SFTPSideViewStatus {
|
class SftpBrowserStatus {
|
||||||
bool selected = false;
|
bool selected = false;
|
||||||
ServerPrivateInfo? spi;
|
ServerPrivateInfo? spi;
|
||||||
List<SftpName>? files;
|
List<SftpName>? files;
|
||||||
@@ -10,5 +10,5 @@ class SFTPSideViewStatus {
|
|||||||
SftpClient? client;
|
SftpClient? client;
|
||||||
bool isBusy = false;
|
bool isBusy = false;
|
||||||
|
|
||||||
SFTPSideViewStatus();
|
SftpBrowserStatus();
|
||||||
}
|
}
|
||||||
55
lib/data/model/sftp/download_status.dart
Normal file
55
lib/data/model/sftp/download_status.dart
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import 'package:toolbox/data/model/sftp/download_worker.dart';
|
||||||
|
|
||||||
|
class SftpDownloadStatus {
|
||||||
|
final int id;
|
||||||
|
final DownloadItem item;
|
||||||
|
final void Function() notifyListeners;
|
||||||
|
late SftpDownloadWorker worker;
|
||||||
|
|
||||||
|
String get fileName => item.localPath.split('/').last;
|
||||||
|
|
||||||
|
// status of the download
|
||||||
|
double? progress;
|
||||||
|
SftpWorkerStatus? status;
|
||||||
|
int? size;
|
||||||
|
Exception? error;
|
||||||
|
Duration? spentTime;
|
||||||
|
|
||||||
|
SftpDownloadStatus(this.item, this.notifyListeners, {String? key})
|
||||||
|
: id = DateTime.now().microsecondsSinceEpoch {
|
||||||
|
worker =
|
||||||
|
SftpDownloadWorker(onNotify: onNotify, item: item, privateKey: key);
|
||||||
|
worker.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
other is SftpDownloadStatus && id == other.id;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => id ^ super.hashCode;
|
||||||
|
|
||||||
|
void onNotify(dynamic event) {
|
||||||
|
switch (event.runtimeType) {
|
||||||
|
case SftpWorkerStatus:
|
||||||
|
status = event;
|
||||||
|
break;
|
||||||
|
case double:
|
||||||
|
progress = event;
|
||||||
|
break;
|
||||||
|
case int:
|
||||||
|
size = event;
|
||||||
|
break;
|
||||||
|
case Exception:
|
||||||
|
error = event;
|
||||||
|
break;
|
||||||
|
case Duration:
|
||||||
|
spentTime = event;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SftpWorkerStatus { preparing, sshConnectted, downloading, finished }
|
||||||
107
lib/data/model/sftp/download_worker.dart
Normal file
107
lib/data/model/sftp/download_worker.dart
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:isolate';
|
||||||
|
|
||||||
|
import 'package:dartssh2/dartssh2.dart';
|
||||||
|
import 'package:easy_isolate/easy_isolate.dart';
|
||||||
|
import 'package:toolbox/data/model/server/server_private_info.dart';
|
||||||
|
import 'package:toolbox/data/model/sftp/download_status.dart';
|
||||||
|
|
||||||
|
class DownloadItem {
|
||||||
|
DownloadItem(this.spi, this.remotePath, this.localPath);
|
||||||
|
|
||||||
|
final ServerPrivateInfo spi;
|
||||||
|
final String remotePath;
|
||||||
|
final String localPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SftpDownloadWorker {
|
||||||
|
SftpDownloadWorker(
|
||||||
|
{required this.onNotify, required this.item, this.privateKey});
|
||||||
|
|
||||||
|
final Function(Object event) onNotify;
|
||||||
|
final DownloadItem item;
|
||||||
|
final worker = Worker();
|
||||||
|
final String? privateKey;
|
||||||
|
|
||||||
|
/// Initiate the worker (new thread) and start listen from messages between
|
||||||
|
/// the threads
|
||||||
|
Future<void> init() async {
|
||||||
|
if (worker.isInitialized) worker.dispose();
|
||||||
|
await worker.init(
|
||||||
|
mainMessageHandler,
|
||||||
|
isolateMessageHandler,
|
||||||
|
errorHandler: print,
|
||||||
|
);
|
||||||
|
worker.sendMessage(DownloadItemEvent(item, privateKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle the messages coming from the isolate
|
||||||
|
void mainMessageHandler(dynamic data, SendPort isolateSendPort) {
|
||||||
|
onNotify(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle the messages coming from the main
|
||||||
|
static isolateMessageHandler(
|
||||||
|
dynamic data, SendPort mainSendPort, SendErrorFunction sendError) async {
|
||||||
|
if (data is DownloadItemEvent) {
|
||||||
|
try {
|
||||||
|
mainSendPort.send(SftpWorkerStatus.preparing);
|
||||||
|
final watch = Stopwatch()..start();
|
||||||
|
final item = data.item;
|
||||||
|
final spi = item.spi;
|
||||||
|
final socket = await SSHSocket.connect(spi.ip, spi.port);
|
||||||
|
SSHClient client;
|
||||||
|
if (spi.pubKeyId == null) {
|
||||||
|
client = SSHClient(socket,
|
||||||
|
username: spi.user,
|
||||||
|
onPasswordRequest: () => spi.authorization as String);
|
||||||
|
} else {
|
||||||
|
client = SSHClient(socket,
|
||||||
|
username: spi.user,
|
||||||
|
identities: SSHKeyPair.fromPem(data.privateKey!));
|
||||||
|
}
|
||||||
|
mainSendPort.send(SftpWorkerStatus.sshConnectted);
|
||||||
|
|
||||||
|
final remotePath = item.remotePath;
|
||||||
|
final localPath = item.localPath;
|
||||||
|
await Directory(localPath.substring(0, item.localPath.lastIndexOf('/')))
|
||||||
|
.create(recursive: true);
|
||||||
|
final local = File(localPath);
|
||||||
|
if (await local.exists()) {
|
||||||
|
await local.delete();
|
||||||
|
}
|
||||||
|
final localFile = local.openWrite(mode: FileMode.append);
|
||||||
|
final file = await (await client.sftp()).open(remotePath);
|
||||||
|
final size = (await file.stat()).size;
|
||||||
|
if (size == null) {
|
||||||
|
mainSendPort.send(Exception('can not get file size'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const defaultChunkSize = 1024 * 512;
|
||||||
|
final chunkSize = size > defaultChunkSize ? defaultChunkSize : size;
|
||||||
|
mainSendPort.send(size);
|
||||||
|
mainSendPort.send(SftpWorkerStatus.downloading);
|
||||||
|
for (var i = 0; i < size; i += chunkSize) {
|
||||||
|
final fileData = file.read(length: chunkSize);
|
||||||
|
await for (var form in fileData) {
|
||||||
|
localFile.add(form);
|
||||||
|
mainSendPort.send((i + form.length) / size * 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mainSendPort.send(SftpWorkerStatus.finished);
|
||||||
|
localFile.close();
|
||||||
|
mainSendPort.send(watch.elapsed);
|
||||||
|
} catch (e) {
|
||||||
|
mainSendPort.send(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DownloadItemEvent {
|
||||||
|
DownloadItemEvent(this.item, this.privateKey);
|
||||||
|
|
||||||
|
final DownloadItem item;
|
||||||
|
final String? privateKey;
|
||||||
|
}
|
||||||
31
lib/data/provider/sftp_download.dart
Normal file
31
lib/data/provider/sftp_download.dart
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import 'package:toolbox/core/provider_base.dart';
|
||||||
|
import 'package:toolbox/data/model/sftp/download_status.dart';
|
||||||
|
import 'package:toolbox/data/model/sftp/download_worker.dart';
|
||||||
|
|
||||||
|
class SftpDownloadProvider extends ProviderBase {
|
||||||
|
final List<SftpDownloadStatus> _status = [];
|
||||||
|
List<SftpDownloadStatus> get status => _status;
|
||||||
|
|
||||||
|
List<SftpDownloadStatus> gets({int? id, String? fileName}) {
|
||||||
|
var found = <SftpDownloadStatus>[];
|
||||||
|
if (id != null) {
|
||||||
|
found = _status.where((e) => e.id == id).toList();
|
||||||
|
}
|
||||||
|
if (fileName != null) {
|
||||||
|
found = found
|
||||||
|
.where((e) => e.item.localPath.split('/').last == fileName)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
SftpDownloadStatus? get({int? id, String? name}) {
|
||||||
|
final found = gets(id: id, fileName: name);
|
||||||
|
if (found.isEmpty) return null;
|
||||||
|
return found.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(DownloadItem item, {String? key}) {
|
||||||
|
_status.add(SftpDownloadStatus(item, notifyListeners, key: key));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
class BuildData {
|
class BuildData {
|
||||||
static const String name = "ServerBox";
|
static const String name = "ServerBox";
|
||||||
static const int build = 125;
|
static const int build = 126;
|
||||||
static const String engine =
|
static const String engine =
|
||||||
"Flutter 2.10.5 • channel stable • https://github.com/flutter/flutter.git\nFramework • revision 5464c5bac7 (2 weeks ago) • 2022-04-18 09:55:37 -0700\nEngine • revision 57d3bac3dd\nTools • Dart 2.16.2 • DevTools 2.9.2\n";
|
"Flutter 2.10.5 • channel stable • https://github.com/flutter/flutter.git\nFramework • revision 5464c5bac7 (3 weeks ago) • 2022-04-18 09:55:37 -0700\nEngine • revision 57d3bac3dd\nTools • Dart 2.16.2 • DevTools 2.9.2\n";
|
||||||
static const String buildAt = "2022-05-05 16:11:07.575227";
|
static const String buildAt = "2022-05-07 21:49:09.473227";
|
||||||
static const int modifications = 2;
|
static const int modifications = 11;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
const TextStyle size18 = TextStyle(fontSize: 18);
|
const TextStyle size18 = TextStyle(fontSize: 18);
|
||||||
|
const TextStyle grey = TextStyle(color: Colors.grey);
|
||||||
|
|||||||
10
lib/data/res/path.dart
Normal file
10
lib/data/res/path.dart
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
|
Future<Directory> get docDir async => await getApplicationDocumentsDirectory();
|
||||||
|
|
||||||
|
Future<Directory> get sftpDownloadDir async {
|
||||||
|
final dir = Directory('${(await docDir).path}/sftp');
|
||||||
|
return dir.create(recursive: true);
|
||||||
|
}
|
||||||
@@ -23,17 +23,30 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
static String m0(rainSunMeGithub) =>
|
static String m0(rainSunMeGithub) =>
|
||||||
"\nThanks ${rainSunMeGithub} for participating in the test.\n\nAll rights reserved.";
|
"\nThanks ${rainSunMeGithub} for participating in the test.\n\nAll rights reserved.";
|
||||||
|
|
||||||
static String m1(code) => "request failed, status code: ${code}";
|
static String m1(fileName) => "Download [${fileName}] to local?";
|
||||||
|
|
||||||
static String m2(myGithub) => "\nMade with ❤️ by ${myGithub}";
|
static String m2(runningCount, stoppedCount) =>
|
||||||
|
"${runningCount} running, ${stoppedCount} container stopped.";
|
||||||
|
|
||||||
static String m3(server) => "Are you sure to delete server [${server}]?";
|
static String m3(count) => "${count} container running.";
|
||||||
|
|
||||||
static String m4(build) => "Found: v1.0.${build}, click to update";
|
static String m4(percent, size) => "${percent}% of ${size}";
|
||||||
|
|
||||||
static String m5(build) => "Current: v1.0.${build}";
|
static String m5(code) => "request failed, status code: ${code}";
|
||||||
|
|
||||||
static String m6(build) => "Current: v1.0.${build}, is up to date";
|
static String m6(myGithub) => "\nMade with ❤️ by ${myGithub}";
|
||||||
|
|
||||||
|
static String m7(time) => "Spent time: ${time} seconds";
|
||||||
|
|
||||||
|
static String m8(name) => "Are you sure to delete [${name}]?";
|
||||||
|
|
||||||
|
static String m9(server) => "Are you sure to delete server [${server}]?";
|
||||||
|
|
||||||
|
static String m10(build) => "Found: v1.0.${build}, click to update";
|
||||||
|
|
||||||
|
static String m11(build) => "Current: v1.0.${build}";
|
||||||
|
|
||||||
|
static String m12(build) => "Current: v1.0.${build}, is up to date";
|
||||||
|
|
||||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||||
@@ -41,9 +54,12 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"addAServer": MessageLookupByLibrary.simpleMessage("add a server"),
|
"addAServer": MessageLookupByLibrary.simpleMessage("add a server"),
|
||||||
"addPrivateKey":
|
"addPrivateKey":
|
||||||
MessageLookupByLibrary.simpleMessage("Add private key"),
|
MessageLookupByLibrary.simpleMessage("Add private key"),
|
||||||
|
"alreadyLastDir":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Already in last directory."),
|
||||||
"appPrimaryColor":
|
"appPrimaryColor":
|
||||||
MessageLookupByLibrary.simpleMessage("App primary color"),
|
MessageLookupByLibrary.simpleMessage("App primary color"),
|
||||||
"attention": MessageLookupByLibrary.simpleMessage("Attention"),
|
"attention": MessageLookupByLibrary.simpleMessage("Attention"),
|
||||||
|
"backDir": MessageLookupByLibrary.simpleMessage("Back"),
|
||||||
"cancel": MessageLookupByLibrary.simpleMessage("Cancel"),
|
"cancel": MessageLookupByLibrary.simpleMessage("Cancel"),
|
||||||
"choose": MessageLookupByLibrary.simpleMessage("Choose"),
|
"choose": MessageLookupByLibrary.simpleMessage("Choose"),
|
||||||
"chooseDestination":
|
"chooseDestination":
|
||||||
@@ -52,33 +68,57 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
MessageLookupByLibrary.simpleMessage("Choose private key"),
|
MessageLookupByLibrary.simpleMessage("Choose private key"),
|
||||||
"clear": MessageLookupByLibrary.simpleMessage("Clear"),
|
"clear": MessageLookupByLibrary.simpleMessage("Clear"),
|
||||||
"close": MessageLookupByLibrary.simpleMessage("Close"),
|
"close": MessageLookupByLibrary.simpleMessage("Close"),
|
||||||
|
"containerStatus":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Container status"),
|
||||||
"convert": MessageLookupByLibrary.simpleMessage("Convert"),
|
"convert": MessageLookupByLibrary.simpleMessage("Convert"),
|
||||||
"copy": MessageLookupByLibrary.simpleMessage("Copy"),
|
"copy": MessageLookupByLibrary.simpleMessage("Copy"),
|
||||||
|
"copyPath": MessageLookupByLibrary.simpleMessage("Copy path"),
|
||||||
|
"createFolder": MessageLookupByLibrary.simpleMessage("Create folder"),
|
||||||
"currentMode": MessageLookupByLibrary.simpleMessage("Current Mode"),
|
"currentMode": MessageLookupByLibrary.simpleMessage("Current Mode"),
|
||||||
"debug": MessageLookupByLibrary.simpleMessage("Debug"),
|
"debug": MessageLookupByLibrary.simpleMessage("Debug"),
|
||||||
"decode": MessageLookupByLibrary.simpleMessage("Decode"),
|
"decode": MessageLookupByLibrary.simpleMessage("Decode"),
|
||||||
"delete": MessageLookupByLibrary.simpleMessage("Delete"),
|
"delete": MessageLookupByLibrary.simpleMessage("Delete"),
|
||||||
|
"dl2Local": m1,
|
||||||
|
"dockerStatusRunningAndStoppedFmt": m2,
|
||||||
|
"dockerStatusRunningFmt": m3,
|
||||||
|
"dockerWaitConnection": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Please wait for the connection to be established."),
|
||||||
|
"download": MessageLookupByLibrary.simpleMessage("Download"),
|
||||||
|
"downloadFinished":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Download finished"),
|
||||||
|
"downloadStatus": m4,
|
||||||
"edit": MessageLookupByLibrary.simpleMessage("Edit"),
|
"edit": MessageLookupByLibrary.simpleMessage("Edit"),
|
||||||
"encode": MessageLookupByLibrary.simpleMessage("Encode"),
|
"encode": MessageLookupByLibrary.simpleMessage("Encode"),
|
||||||
|
"error": MessageLookupByLibrary.simpleMessage("Error"),
|
||||||
"exampleName": MessageLookupByLibrary.simpleMessage("Example name"),
|
"exampleName": MessageLookupByLibrary.simpleMessage("Example name"),
|
||||||
"export": MessageLookupByLibrary.simpleMessage("Export"),
|
"export": MessageLookupByLibrary.simpleMessage("Export"),
|
||||||
"fieldMustNotEmpty": MessageLookupByLibrary.simpleMessage(
|
"fieldMustNotEmpty": MessageLookupByLibrary.simpleMessage(
|
||||||
"These fields must not be empty."),
|
"These fields must not be empty."),
|
||||||
"go": MessageLookupByLibrary.simpleMessage("Go"),
|
"go": MessageLookupByLibrary.simpleMessage("Go"),
|
||||||
|
"goSftpDlPage":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Go to SFTP download page?"),
|
||||||
"host": MessageLookupByLibrary.simpleMessage("Host"),
|
"host": MessageLookupByLibrary.simpleMessage("Host"),
|
||||||
"httpFailedWithCode": m1,
|
"httpFailedWithCode": m5,
|
||||||
"import": MessageLookupByLibrary.simpleMessage("Import"),
|
"import": MessageLookupByLibrary.simpleMessage("Import"),
|
||||||
"importAndExport":
|
"importAndExport":
|
||||||
MessageLookupByLibrary.simpleMessage("Import and Export"),
|
MessageLookupByLibrary.simpleMessage("Import and Export"),
|
||||||
|
"install": MessageLookupByLibrary.simpleMessage("install"),
|
||||||
|
"installDockerWithUrl": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Please https://docs.docker.com/engine/install docker first."),
|
||||||
|
"keepForeground":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Keep app foreground!"),
|
||||||
"keyAuth": MessageLookupByLibrary.simpleMessage("Key Auth"),
|
"keyAuth": MessageLookupByLibrary.simpleMessage("Key Auth"),
|
||||||
"launchPage": MessageLookupByLibrary.simpleMessage("Launch page"),
|
"launchPage": MessageLookupByLibrary.simpleMessage("Launch page"),
|
||||||
"license": MessageLookupByLibrary.simpleMessage("License"),
|
"license": MessageLookupByLibrary.simpleMessage("License"),
|
||||||
|
"loadingFiles":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Loading files..."),
|
||||||
"loss": MessageLookupByLibrary.simpleMessage("Loss"),
|
"loss": MessageLookupByLibrary.simpleMessage("Loss"),
|
||||||
"madeWithLove": m2,
|
"madeWithLove": m6,
|
||||||
"max": MessageLookupByLibrary.simpleMessage("max"),
|
"max": MessageLookupByLibrary.simpleMessage("max"),
|
||||||
"min": MessageLookupByLibrary.simpleMessage("min"),
|
"min": MessageLookupByLibrary.simpleMessage("min"),
|
||||||
"ms": MessageLookupByLibrary.simpleMessage("ms"),
|
"ms": MessageLookupByLibrary.simpleMessage("ms"),
|
||||||
"name": MessageLookupByLibrary.simpleMessage("Name"),
|
"name": MessageLookupByLibrary.simpleMessage("Name"),
|
||||||
|
"noClient": MessageLookupByLibrary.simpleMessage("No client"),
|
||||||
"noResult": MessageLookupByLibrary.simpleMessage("No result"),
|
"noResult": MessageLookupByLibrary.simpleMessage("No result"),
|
||||||
"noSavedPrivateKey":
|
"noSavedPrivateKey":
|
||||||
MessageLookupByLibrary.simpleMessage("No saved private keys."),
|
MessageLookupByLibrary.simpleMessage("No saved private keys."),
|
||||||
@@ -87,6 +127,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"noServerAvailable":
|
"noServerAvailable":
|
||||||
MessageLookupByLibrary.simpleMessage("No server available."),
|
MessageLookupByLibrary.simpleMessage("No server available."),
|
||||||
"ok": MessageLookupByLibrary.simpleMessage("OK"),
|
"ok": MessageLookupByLibrary.simpleMessage("OK"),
|
||||||
|
"open": MessageLookupByLibrary.simpleMessage("Open"),
|
||||||
"ping": MessageLookupByLibrary.simpleMessage("Ping"),
|
"ping": MessageLookupByLibrary.simpleMessage("Ping"),
|
||||||
"pingAvg": MessageLookupByLibrary.simpleMessage("Avg:"),
|
"pingAvg": MessageLookupByLibrary.simpleMessage("Avg:"),
|
||||||
"pingInputIP": MessageLookupByLibrary.simpleMessage(
|
"pingInputIP": MessageLookupByLibrary.simpleMessage(
|
||||||
@@ -100,6 +141,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"port": MessageLookupByLibrary.simpleMessage("Port"),
|
"port": MessageLookupByLibrary.simpleMessage("Port"),
|
||||||
"privateKey": MessageLookupByLibrary.simpleMessage("Private Key"),
|
"privateKey": MessageLookupByLibrary.simpleMessage("Private Key"),
|
||||||
"pwd": MessageLookupByLibrary.simpleMessage("Password"),
|
"pwd": MessageLookupByLibrary.simpleMessage("Password"),
|
||||||
|
"rename": MessageLookupByLibrary.simpleMessage("Rename"),
|
||||||
"result": MessageLookupByLibrary.simpleMessage("Result"),
|
"result": MessageLookupByLibrary.simpleMessage("Result"),
|
||||||
"run": MessageLookupByLibrary.simpleMessage("Run"),
|
"run": MessageLookupByLibrary.simpleMessage("Run"),
|
||||||
"save": MessageLookupByLibrary.simpleMessage("Save"),
|
"save": MessageLookupByLibrary.simpleMessage("Save"),
|
||||||
@@ -116,12 +158,21 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"serverTabUnkown":
|
"serverTabUnkown":
|
||||||
MessageLookupByLibrary.simpleMessage("Unknown state"),
|
MessageLookupByLibrary.simpleMessage("Unknown state"),
|
||||||
"setting": MessageLookupByLibrary.simpleMessage("Setting"),
|
"setting": MessageLookupByLibrary.simpleMessage("Setting"),
|
||||||
|
"sftpDlPrepare":
|
||||||
|
MessageLookupByLibrary.simpleMessage("Preparing to connect..."),
|
||||||
|
"sftpNoDownloadTask":
|
||||||
|
MessageLookupByLibrary.simpleMessage("No download task."),
|
||||||
|
"sftpSSHConnected":
|
||||||
|
MessageLookupByLibrary.simpleMessage("SFTP Connected"),
|
||||||
"snippet": MessageLookupByLibrary.simpleMessage("Snippet"),
|
"snippet": MessageLookupByLibrary.simpleMessage("Snippet"),
|
||||||
|
"spentTime": m7,
|
||||||
"start": MessageLookupByLibrary.simpleMessage("Start"),
|
"start": MessageLookupByLibrary.simpleMessage("Start"),
|
||||||
"stop": MessageLookupByLibrary.simpleMessage("Stop"),
|
"stop": MessageLookupByLibrary.simpleMessage("Stop"),
|
||||||
"sureToDeleteServer": m3,
|
"sureDelete": m8,
|
||||||
|
"sureToDeleteServer": m9,
|
||||||
"ttl": MessageLookupByLibrary.simpleMessage("TTL"),
|
"ttl": MessageLookupByLibrary.simpleMessage("TTL"),
|
||||||
"unknown": MessageLookupByLibrary.simpleMessage("unknown"),
|
"unknown": MessageLookupByLibrary.simpleMessage("unknown"),
|
||||||
|
"unknownError": MessageLookupByLibrary.simpleMessage("Unknown error"),
|
||||||
"unkownConvertMode":
|
"unkownConvertMode":
|
||||||
MessageLookupByLibrary.simpleMessage("Unknown convert mode"),
|
MessageLookupByLibrary.simpleMessage("Unknown convert mode"),
|
||||||
"updateIntervalEqual0": MessageLookupByLibrary.simpleMessage(
|
"updateIntervalEqual0": MessageLookupByLibrary.simpleMessage(
|
||||||
@@ -131,9 +182,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"upsideDown": MessageLookupByLibrary.simpleMessage("Upside Down"),
|
"upsideDown": MessageLookupByLibrary.simpleMessage("Upside Down"),
|
||||||
"urlOrJson": MessageLookupByLibrary.simpleMessage("URL or JSON"),
|
"urlOrJson": MessageLookupByLibrary.simpleMessage("URL or JSON"),
|
||||||
"user": MessageLookupByLibrary.simpleMessage("User"),
|
"user": MessageLookupByLibrary.simpleMessage("User"),
|
||||||
"versionHaveUpdate": m4,
|
"versionHaveUpdate": m10,
|
||||||
"versionUnknownUpdate": m5,
|
"versionUnknownUpdate": m11,
|
||||||
"versionUpdated": m6,
|
"versionUpdated": m12,
|
||||||
"willTakEeffectImmediately":
|
"willTakEeffectImmediately":
|
||||||
MessageLookupByLibrary.simpleMessage("Will take effect immediately")
|
MessageLookupByLibrary.simpleMessage("Will take effect immediately")
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,61 +23,95 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
static String m0(rainSunMeGithub) =>
|
static String m0(rainSunMeGithub) =>
|
||||||
"\n感谢 ${rainSunMeGithub} 参与软件测试。\n\n保留所有权利。";
|
"\n感谢 ${rainSunMeGithub} 参与软件测试。\n\n保留所有权利。";
|
||||||
|
|
||||||
static String m1(code) => "请求失败, 状态码: ${code}";
|
static String m1(fileName) => "下载 [${fileName}] 到本地?";
|
||||||
|
|
||||||
static String m2(myGithub) => "\n用❤️制作 by ${myGithub}";
|
static String m2(runningCount, stoppedCount) =>
|
||||||
|
"${runningCount}个正在运行, ${stoppedCount}个已停止";
|
||||||
|
|
||||||
static String m3(server) => "你确定要删除服务器 [${server}] 吗?";
|
static String m3(count) => "${count}个容器正在运行";
|
||||||
|
|
||||||
static String m4(build) => "找到新版本:v1.0.${build}, 点击更新";
|
static String m4(percent, size) => "${size} 的 ${percent}%";
|
||||||
|
|
||||||
static String m5(build) => "当前:v1.0.${build}";
|
static String m5(code) => "请求失败, 状态码: ${code}";
|
||||||
|
|
||||||
static String m6(build) => "当前:v1.0.${build}, 已是最新版本";
|
static String m6(myGithub) => "\n用❤️制作 by ${myGithub}";
|
||||||
|
|
||||||
|
static String m7(time) => "耗时: ${time} 秒";
|
||||||
|
|
||||||
|
static String m8(name) => "确定删除[${name}]?";
|
||||||
|
|
||||||
|
static String m9(server) => "你确定要删除服务器 [${server}] 吗?";
|
||||||
|
|
||||||
|
static String m10(build) => "找到新版本:v1.0.${build}, 点击更新";
|
||||||
|
|
||||||
|
static String m11(build) => "当前:v1.0.${build}";
|
||||||
|
|
||||||
|
static String m12(build) => "当前:v1.0.${build}, 已是最新版本";
|
||||||
|
|
||||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||||
"aboutThanks": m0,
|
"aboutThanks": m0,
|
||||||
"addAServer": MessageLookupByLibrary.simpleMessage("添加服务器"),
|
"addAServer": MessageLookupByLibrary.simpleMessage("添加服务器"),
|
||||||
"addPrivateKey": MessageLookupByLibrary.simpleMessage("添加一个私钥"),
|
"addPrivateKey": MessageLookupByLibrary.simpleMessage("添加一个私钥"),
|
||||||
|
"alreadyLastDir": MessageLookupByLibrary.simpleMessage("已经是最上层目录了"),
|
||||||
"appPrimaryColor": MessageLookupByLibrary.simpleMessage("App主要色"),
|
"appPrimaryColor": MessageLookupByLibrary.simpleMessage("App主要色"),
|
||||||
"attention": MessageLookupByLibrary.simpleMessage("注意"),
|
"attention": MessageLookupByLibrary.simpleMessage("注意"),
|
||||||
|
"backDir": MessageLookupByLibrary.simpleMessage("返回上一级"),
|
||||||
"cancel": MessageLookupByLibrary.simpleMessage("取消"),
|
"cancel": MessageLookupByLibrary.simpleMessage("取消"),
|
||||||
"choose": MessageLookupByLibrary.simpleMessage("选择"),
|
"choose": MessageLookupByLibrary.simpleMessage("选择"),
|
||||||
"chooseDestination": MessageLookupByLibrary.simpleMessage("选择目标"),
|
"chooseDestination": MessageLookupByLibrary.simpleMessage("选择目标"),
|
||||||
"choosePrivateKey": MessageLookupByLibrary.simpleMessage("选择私钥"),
|
"choosePrivateKey": MessageLookupByLibrary.simpleMessage("选择私钥"),
|
||||||
"clear": MessageLookupByLibrary.simpleMessage("清除"),
|
"clear": MessageLookupByLibrary.simpleMessage("清除"),
|
||||||
"close": MessageLookupByLibrary.simpleMessage("关闭"),
|
"close": MessageLookupByLibrary.simpleMessage("关闭"),
|
||||||
|
"containerStatus": MessageLookupByLibrary.simpleMessage("容器状态"),
|
||||||
"convert": MessageLookupByLibrary.simpleMessage("转换"),
|
"convert": MessageLookupByLibrary.simpleMessage("转换"),
|
||||||
"copy": MessageLookupByLibrary.simpleMessage("复制到剪切板"),
|
"copy": MessageLookupByLibrary.simpleMessage("复制到剪切板"),
|
||||||
|
"copyPath": MessageLookupByLibrary.simpleMessage("复制路径"),
|
||||||
|
"createFolder": MessageLookupByLibrary.simpleMessage("创建文件夹"),
|
||||||
"currentMode": MessageLookupByLibrary.simpleMessage("当前模式"),
|
"currentMode": MessageLookupByLibrary.simpleMessage("当前模式"),
|
||||||
"debug": MessageLookupByLibrary.simpleMessage("调试"),
|
"debug": MessageLookupByLibrary.simpleMessage("调试"),
|
||||||
"decode": MessageLookupByLibrary.simpleMessage("解码"),
|
"decode": MessageLookupByLibrary.simpleMessage("解码"),
|
||||||
"delete": MessageLookupByLibrary.simpleMessage("删除"),
|
"delete": MessageLookupByLibrary.simpleMessage("删除"),
|
||||||
|
"dl2Local": m1,
|
||||||
|
"dockerStatusRunningAndStoppedFmt": m2,
|
||||||
|
"dockerStatusRunningFmt": m3,
|
||||||
|
"dockerWaitConnection": MessageLookupByLibrary.simpleMessage("请等待连接建立"),
|
||||||
|
"download": MessageLookupByLibrary.simpleMessage("下载"),
|
||||||
|
"downloadFinished": MessageLookupByLibrary.simpleMessage("下载完成!"),
|
||||||
|
"downloadStatus": m4,
|
||||||
"edit": MessageLookupByLibrary.simpleMessage("编辑"),
|
"edit": MessageLookupByLibrary.simpleMessage("编辑"),
|
||||||
"encode": MessageLookupByLibrary.simpleMessage("编码"),
|
"encode": MessageLookupByLibrary.simpleMessage("编码"),
|
||||||
|
"error": MessageLookupByLibrary.simpleMessage("出错了"),
|
||||||
"exampleName": MessageLookupByLibrary.simpleMessage("名称示例"),
|
"exampleName": MessageLookupByLibrary.simpleMessage("名称示例"),
|
||||||
"export": MessageLookupByLibrary.simpleMessage("导出"),
|
"export": MessageLookupByLibrary.simpleMessage("导出"),
|
||||||
"fieldMustNotEmpty": MessageLookupByLibrary.simpleMessage("这些输入框不能为空。"),
|
"fieldMustNotEmpty": MessageLookupByLibrary.simpleMessage("这些输入框不能为空。"),
|
||||||
"go": MessageLookupByLibrary.simpleMessage("开始"),
|
"go": MessageLookupByLibrary.simpleMessage("开始"),
|
||||||
|
"goSftpDlPage": MessageLookupByLibrary.simpleMessage("前往下载页?"),
|
||||||
"host": MessageLookupByLibrary.simpleMessage("主机"),
|
"host": MessageLookupByLibrary.simpleMessage("主机"),
|
||||||
"httpFailedWithCode": m1,
|
"httpFailedWithCode": m5,
|
||||||
"import": MessageLookupByLibrary.simpleMessage("导入"),
|
"import": MessageLookupByLibrary.simpleMessage("导入"),
|
||||||
"importAndExport": MessageLookupByLibrary.simpleMessage("导入或导出"),
|
"importAndExport": MessageLookupByLibrary.simpleMessage("导入或导出"),
|
||||||
|
"install": MessageLookupByLibrary.simpleMessage("安装"),
|
||||||
|
"installDockerWithUrl": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"请先 https://docs.docker.com/engine/install docker"),
|
||||||
|
"keepForeground": MessageLookupByLibrary.simpleMessage("请保持应用处于前台!"),
|
||||||
"keyAuth": MessageLookupByLibrary.simpleMessage("公钥认证"),
|
"keyAuth": MessageLookupByLibrary.simpleMessage("公钥认证"),
|
||||||
"launchPage": MessageLookupByLibrary.simpleMessage("启动页"),
|
"launchPage": MessageLookupByLibrary.simpleMessage("启动页"),
|
||||||
"license": MessageLookupByLibrary.simpleMessage("开源证书"),
|
"license": MessageLookupByLibrary.simpleMessage("开源证书"),
|
||||||
|
"loadingFiles": MessageLookupByLibrary.simpleMessage("正在加载目录。。。"),
|
||||||
"loss": MessageLookupByLibrary.simpleMessage("丢包率"),
|
"loss": MessageLookupByLibrary.simpleMessage("丢包率"),
|
||||||
"madeWithLove": m2,
|
"madeWithLove": m6,
|
||||||
"max": MessageLookupByLibrary.simpleMessage("最大"),
|
"max": MessageLookupByLibrary.simpleMessage("最大"),
|
||||||
"min": MessageLookupByLibrary.simpleMessage("最小"),
|
"min": MessageLookupByLibrary.simpleMessage("最小"),
|
||||||
"ms": MessageLookupByLibrary.simpleMessage("毫秒"),
|
"ms": MessageLookupByLibrary.simpleMessage("毫秒"),
|
||||||
"name": MessageLookupByLibrary.simpleMessage("名称"),
|
"name": MessageLookupByLibrary.simpleMessage("名称"),
|
||||||
|
"noClient": MessageLookupByLibrary.simpleMessage("没有SSH连接"),
|
||||||
"noResult": MessageLookupByLibrary.simpleMessage("无结果"),
|
"noResult": MessageLookupByLibrary.simpleMessage("无结果"),
|
||||||
"noSavedPrivateKey": MessageLookupByLibrary.simpleMessage("没有已保存的私钥。"),
|
"noSavedPrivateKey": MessageLookupByLibrary.simpleMessage("没有已保存的私钥。"),
|
||||||
"noSavedSnippet": MessageLookupByLibrary.simpleMessage("没有已保存的代码片段。"),
|
"noSavedSnippet": MessageLookupByLibrary.simpleMessage("没有已保存的代码片段。"),
|
||||||
"noServerAvailable": MessageLookupByLibrary.simpleMessage("没有可用的服务器。"),
|
"noServerAvailable": MessageLookupByLibrary.simpleMessage("没有可用的服务器。"),
|
||||||
"ok": MessageLookupByLibrary.simpleMessage("好"),
|
"ok": MessageLookupByLibrary.simpleMessage("好"),
|
||||||
|
"open": MessageLookupByLibrary.simpleMessage("打开"),
|
||||||
"ping": MessageLookupByLibrary.simpleMessage("Ping"),
|
"ping": MessageLookupByLibrary.simpleMessage("Ping"),
|
||||||
"pingAvg": MessageLookupByLibrary.simpleMessage("平均:"),
|
"pingAvg": MessageLookupByLibrary.simpleMessage("平均:"),
|
||||||
"pingInputIP": MessageLookupByLibrary.simpleMessage("请输入目标IP或域名"),
|
"pingInputIP": MessageLookupByLibrary.simpleMessage("请输入目标IP或域名"),
|
||||||
@@ -87,6 +121,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"port": MessageLookupByLibrary.simpleMessage("端口"),
|
"port": MessageLookupByLibrary.simpleMessage("端口"),
|
||||||
"privateKey": MessageLookupByLibrary.simpleMessage("私钥"),
|
"privateKey": MessageLookupByLibrary.simpleMessage("私钥"),
|
||||||
"pwd": MessageLookupByLibrary.simpleMessage("密码"),
|
"pwd": MessageLookupByLibrary.simpleMessage("密码"),
|
||||||
|
"rename": MessageLookupByLibrary.simpleMessage("重命名"),
|
||||||
"result": MessageLookupByLibrary.simpleMessage("结果"),
|
"result": MessageLookupByLibrary.simpleMessage("结果"),
|
||||||
"run": MessageLookupByLibrary.simpleMessage("运行"),
|
"run": MessageLookupByLibrary.simpleMessage("运行"),
|
||||||
"save": MessageLookupByLibrary.simpleMessage("保存"),
|
"save": MessageLookupByLibrary.simpleMessage("保存"),
|
||||||
@@ -100,12 +135,19 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"serverTabPlzSave": MessageLookupByLibrary.simpleMessage("请再次保存该私钥"),
|
"serverTabPlzSave": MessageLookupByLibrary.simpleMessage("请再次保存该私钥"),
|
||||||
"serverTabUnkown": MessageLookupByLibrary.simpleMessage("未知状态"),
|
"serverTabUnkown": MessageLookupByLibrary.simpleMessage("未知状态"),
|
||||||
"setting": MessageLookupByLibrary.simpleMessage("设置"),
|
"setting": MessageLookupByLibrary.simpleMessage("设置"),
|
||||||
|
"sftpDlPrepare": MessageLookupByLibrary.simpleMessage("准备连接至服务器..."),
|
||||||
|
"sftpNoDownloadTask": MessageLookupByLibrary.simpleMessage("没有下载任务"),
|
||||||
|
"sftpSSHConnected":
|
||||||
|
MessageLookupByLibrary.simpleMessage("SFTP 已连接,即将开始下载..."),
|
||||||
"snippet": MessageLookupByLibrary.simpleMessage("代码片段"),
|
"snippet": MessageLookupByLibrary.simpleMessage("代码片段"),
|
||||||
|
"spentTime": m7,
|
||||||
"start": MessageLookupByLibrary.simpleMessage("开始"),
|
"start": MessageLookupByLibrary.simpleMessage("开始"),
|
||||||
"stop": MessageLookupByLibrary.simpleMessage("停止"),
|
"stop": MessageLookupByLibrary.simpleMessage("停止"),
|
||||||
"sureToDeleteServer": m3,
|
"sureDelete": m8,
|
||||||
|
"sureToDeleteServer": m9,
|
||||||
"ttl": MessageLookupByLibrary.simpleMessage("缓存时间"),
|
"ttl": MessageLookupByLibrary.simpleMessage("缓存时间"),
|
||||||
"unknown": MessageLookupByLibrary.simpleMessage("未知"),
|
"unknown": MessageLookupByLibrary.simpleMessage("未知"),
|
||||||
|
"unknownError": MessageLookupByLibrary.simpleMessage("未知错误"),
|
||||||
"unkownConvertMode": MessageLookupByLibrary.simpleMessage("未知转换模式"),
|
"unkownConvertMode": MessageLookupByLibrary.simpleMessage("未知转换模式"),
|
||||||
"updateIntervalEqual0": MessageLookupByLibrary.simpleMessage(
|
"updateIntervalEqual0": MessageLookupByLibrary.simpleMessage(
|
||||||
"你设置为0,服务器状态不会自动刷新。\n你可以手动下拉刷新。"),
|
"你设置为0,服务器状态不会自动刷新。\n你可以手动下拉刷新。"),
|
||||||
@@ -114,9 +156,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
|||||||
"upsideDown": MessageLookupByLibrary.simpleMessage("上下交换"),
|
"upsideDown": MessageLookupByLibrary.simpleMessage("上下交换"),
|
||||||
"urlOrJson": MessageLookupByLibrary.simpleMessage("链接或JSON"),
|
"urlOrJson": MessageLookupByLibrary.simpleMessage("链接或JSON"),
|
||||||
"user": MessageLookupByLibrary.simpleMessage("用户"),
|
"user": MessageLookupByLibrary.simpleMessage("用户"),
|
||||||
"versionHaveUpdate": m4,
|
"versionHaveUpdate": m10,
|
||||||
"versionUnknownUpdate": m5,
|
"versionUnknownUpdate": m11,
|
||||||
"versionUpdated": m6,
|
"versionUpdated": m12,
|
||||||
"willTakEeffectImmediately":
|
"willTakEeffectImmediately":
|
||||||
MessageLookupByLibrary.simpleMessage("更改将会立即生效")
|
MessageLookupByLibrary.simpleMessage("更改将会立即生效")
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -829,6 +829,277 @@ class S {
|
|||||||
args: [],
|
args: [],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `Download`
|
||||||
|
String get download {
|
||||||
|
return Intl.message(
|
||||||
|
'Download',
|
||||||
|
name: 'download',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Copy path`
|
||||||
|
String get copyPath {
|
||||||
|
return Intl.message(
|
||||||
|
'Copy path',
|
||||||
|
name: 'copyPath',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Keep app foreground!`
|
||||||
|
String get keepForeground {
|
||||||
|
return Intl.message(
|
||||||
|
'Keep app foreground!',
|
||||||
|
name: 'keepForeground',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Download finished`
|
||||||
|
String get downloadFinished {
|
||||||
|
return Intl.message(
|
||||||
|
'Download finished',
|
||||||
|
name: 'downloadFinished',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `{percent}% of {size}`
|
||||||
|
String downloadStatus(Object percent, Object size) {
|
||||||
|
return Intl.message(
|
||||||
|
'$percent% of $size',
|
||||||
|
name: 'downloadStatus',
|
||||||
|
desc: '',
|
||||||
|
args: [percent, size],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Preparing to connect...`
|
||||||
|
String get sftpDlPrepare {
|
||||||
|
return Intl.message(
|
||||||
|
'Preparing to connect...',
|
||||||
|
name: 'sftpDlPrepare',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `SFTP Connected`
|
||||||
|
String get sftpSSHConnected {
|
||||||
|
return Intl.message(
|
||||||
|
'SFTP Connected',
|
||||||
|
name: 'sftpSSHConnected',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Spent time: {time} seconds`
|
||||||
|
String spentTime(Object time) {
|
||||||
|
return Intl.message(
|
||||||
|
'Spent time: $time seconds',
|
||||||
|
name: 'spentTime',
|
||||||
|
desc: '',
|
||||||
|
args: [time],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Back`
|
||||||
|
String get backDir {
|
||||||
|
return Intl.message(
|
||||||
|
'Back',
|
||||||
|
name: 'backDir',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Already in last directory.`
|
||||||
|
String get alreadyLastDir {
|
||||||
|
return Intl.message(
|
||||||
|
'Already in last directory.',
|
||||||
|
name: 'alreadyLastDir',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Open`
|
||||||
|
String get open {
|
||||||
|
return Intl.message(
|
||||||
|
'Open',
|
||||||
|
name: 'open',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Are you sure to delete [{name}]?`
|
||||||
|
String sureDelete(Object name) {
|
||||||
|
return Intl.message(
|
||||||
|
'Are you sure to delete [$name]?',
|
||||||
|
name: 'sureDelete',
|
||||||
|
desc: '',
|
||||||
|
args: [name],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Container status`
|
||||||
|
String get containerStatus {
|
||||||
|
return Intl.message(
|
||||||
|
'Container status',
|
||||||
|
name: 'containerStatus',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `No client`
|
||||||
|
String get noClient {
|
||||||
|
return Intl.message(
|
||||||
|
'No client',
|
||||||
|
name: 'noClient',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Please https://docs.docker.com/engine/install docker first.`
|
||||||
|
String get installDockerWithUrl {
|
||||||
|
return Intl.message(
|
||||||
|
'Please https://docs.docker.com/engine/install docker first.',
|
||||||
|
name: 'installDockerWithUrl',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Please wait for the connection to be established.`
|
||||||
|
String get dockerWaitConnection {
|
||||||
|
return Intl.message(
|
||||||
|
'Please wait for the connection to be established.',
|
||||||
|
name: 'dockerWaitConnection',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Unknown error`
|
||||||
|
String get unknownError {
|
||||||
|
return Intl.message(
|
||||||
|
'Unknown error',
|
||||||
|
name: 'unknownError',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `{count} container running.`
|
||||||
|
String dockerStatusRunningFmt(Object count) {
|
||||||
|
return Intl.message(
|
||||||
|
'$count container running.',
|
||||||
|
name: 'dockerStatusRunningFmt',
|
||||||
|
desc: '',
|
||||||
|
args: [count],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `{runningCount} running, {stoppedCount} container stopped.`
|
||||||
|
String dockerStatusRunningAndStoppedFmt(
|
||||||
|
Object runningCount, Object stoppedCount) {
|
||||||
|
return Intl.message(
|
||||||
|
'$runningCount running, $stoppedCount container stopped.',
|
||||||
|
name: 'dockerStatusRunningAndStoppedFmt',
|
||||||
|
desc: '',
|
||||||
|
args: [runningCount, stoppedCount],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `install`
|
||||||
|
String get install {
|
||||||
|
return Intl.message(
|
||||||
|
'install',
|
||||||
|
name: 'install',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Loading files...`
|
||||||
|
String get loadingFiles {
|
||||||
|
return Intl.message(
|
||||||
|
'Loading files...',
|
||||||
|
name: 'loadingFiles',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `No download task.`
|
||||||
|
String get sftpNoDownloadTask {
|
||||||
|
return Intl.message(
|
||||||
|
'No download task.',
|
||||||
|
name: 'sftpNoDownloadTask',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Go to SFTP download page?`
|
||||||
|
String get goSftpDlPage {
|
||||||
|
return Intl.message(
|
||||||
|
'Go to SFTP download page?',
|
||||||
|
name: 'goSftpDlPage',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Create folder`
|
||||||
|
String get createFolder {
|
||||||
|
return Intl.message(
|
||||||
|
'Create folder',
|
||||||
|
name: 'createFolder',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Rename`
|
||||||
|
String get rename {
|
||||||
|
return Intl.message(
|
||||||
|
'Rename',
|
||||||
|
name: 'rename',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Download [{fileName}] to local?`
|
||||||
|
String dl2Local(Object fileName) {
|
||||||
|
return Intl.message(
|
||||||
|
'Download [$fileName] to local?',
|
||||||
|
name: 'dl2Local',
|
||||||
|
desc: '',
|
||||||
|
args: [fileName],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Error`
|
||||||
|
String get error {
|
||||||
|
return Intl.message(
|
||||||
|
'Error',
|
||||||
|
name: 'error',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
||||||
|
|||||||
@@ -76,5 +76,32 @@
|
|||||||
"plzEnterPwd": "Please enter password.",
|
"plzEnterPwd": "Please enter password.",
|
||||||
"plzSelectKey": "Please select a key.",
|
"plzSelectKey": "Please select a key.",
|
||||||
"exampleName": "Example name",
|
"exampleName": "Example name",
|
||||||
"stop": "Stop"
|
"stop": "Stop",
|
||||||
|
"download": "Download",
|
||||||
|
"copyPath": "Copy path",
|
||||||
|
"keepForeground": "Keep app foreground!",
|
||||||
|
"downloadFinished": "Download finished",
|
||||||
|
"downloadStatus": "{percent}% of {size}",
|
||||||
|
"sftpDlPrepare": "Preparing to connect...",
|
||||||
|
"sftpSSHConnected": "SFTP Connected",
|
||||||
|
"spentTime": "Spent time: {time} seconds",
|
||||||
|
"backDir": "Back",
|
||||||
|
"alreadyLastDir": "Already in last directory.",
|
||||||
|
"open": "Open",
|
||||||
|
"sureDelete": "Are you sure to delete [{name}]?",
|
||||||
|
"containerStatus": "Container status",
|
||||||
|
"noClient": "No client",
|
||||||
|
"installDockerWithUrl": "Please https://docs.docker.com/engine/install docker first.",
|
||||||
|
"dockerWaitConnection": "Please wait for the connection to be established.",
|
||||||
|
"unknownError": "Unknown error",
|
||||||
|
"dockerStatusRunningFmt": "{count} container running.",
|
||||||
|
"dockerStatusRunningAndStoppedFmt": "{runningCount} running, {stoppedCount} container stopped.",
|
||||||
|
"install": "install",
|
||||||
|
"loadingFiles": "Loading files...",
|
||||||
|
"sftpNoDownloadTask": "No download task.",
|
||||||
|
"goSftpDlPage": "Go to SFTP download page?",
|
||||||
|
"createFolder": "Create folder",
|
||||||
|
"rename": "Rename",
|
||||||
|
"dl2Local": "Download [{fileName}] to local?",
|
||||||
|
"error": "Error"
|
||||||
}
|
}
|
||||||
@@ -76,5 +76,32 @@
|
|||||||
"plzEnterPwd": "请输入密码",
|
"plzEnterPwd": "请输入密码",
|
||||||
"plzSelectKey": "请选择私钥",
|
"plzSelectKey": "请选择私钥",
|
||||||
"exampleName": "名称示例",
|
"exampleName": "名称示例",
|
||||||
"stop": "停止"
|
"stop": "停止",
|
||||||
|
"download": "下载",
|
||||||
|
"copyPath": "复制路径",
|
||||||
|
"keepForeground": "请保持应用处于前台!",
|
||||||
|
"downloadFinished": "下载完成!",
|
||||||
|
"downloadStatus": "{size} 的 {percent}%",
|
||||||
|
"sftpDlPrepare": "准备连接至服务器...",
|
||||||
|
"sftpSSHConnected": "SFTP 已连接,即将开始下载...",
|
||||||
|
"spentTime": "耗时: {time} 秒",
|
||||||
|
"backDir": "返回上一级",
|
||||||
|
"alreadyLastDir": "已经是最上层目录了",
|
||||||
|
"open": "打开",
|
||||||
|
"sureDelete": "确定删除[{name}]?",
|
||||||
|
"containerStatus": "容器状态",
|
||||||
|
"noClient": "没有SSH连接",
|
||||||
|
"installDockerWithUrl": "请先 https://docs.docker.com/engine/install docker",
|
||||||
|
"dockerWaitConnection": "请等待连接建立",
|
||||||
|
"unknownError": "未知错误",
|
||||||
|
"dockerStatusRunningFmt": "{count}个容器正在运行",
|
||||||
|
"dockerStatusRunningAndStoppedFmt": "{runningCount}个正在运行, {stoppedCount}个已停止",
|
||||||
|
"install": "安装",
|
||||||
|
"loadingFiles": "正在加载目录。。。",
|
||||||
|
"sftpNoDownloadTask": "没有下载任务",
|
||||||
|
"goSftpDlPage": "前往下载页?",
|
||||||
|
"createFolder": "创建文件夹",
|
||||||
|
"rename": "重命名",
|
||||||
|
"dl2Local": "下载 [{fileName}] 到本地?",
|
||||||
|
"error": "出错了"
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ import 'package:toolbox/data/provider/debug.dart';
|
|||||||
import 'package:toolbox/data/provider/docker.dart';
|
import 'package:toolbox/data/provider/docker.dart';
|
||||||
import 'package:toolbox/data/provider/private_key.dart';
|
import 'package:toolbox/data/provider/private_key.dart';
|
||||||
import 'package:toolbox/data/provider/server.dart';
|
import 'package:toolbox/data/provider/server.dart';
|
||||||
|
import 'package:toolbox/data/provider/sftp_download.dart';
|
||||||
import 'package:toolbox/data/provider/snippet.dart';
|
import 'package:toolbox/data/provider/snippet.dart';
|
||||||
import 'package:toolbox/data/service/app.dart';
|
import 'package:toolbox/data/service/app.dart';
|
||||||
import 'package:toolbox/data/store/private_key.dart';
|
import 'package:toolbox/data/store/private_key.dart';
|
||||||
@@ -26,6 +27,7 @@ void setupLocatorForProviders() {
|
|||||||
locator.registerSingleton(ServerProvider());
|
locator.registerSingleton(ServerProvider());
|
||||||
locator.registerSingleton(SnippetProvider());
|
locator.registerSingleton(SnippetProvider());
|
||||||
locator.registerSingleton(PrivateKeyProvider());
|
locator.registerSingleton(PrivateKeyProvider());
|
||||||
|
locator.registerSingleton(SftpDownloadProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setupLocatorForStores() async {
|
Future<void> setupLocatorForStores() async {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import 'package:toolbox/data/provider/debug.dart';
|
|||||||
import 'package:toolbox/data/provider/docker.dart';
|
import 'package:toolbox/data/provider/docker.dart';
|
||||||
import 'package:toolbox/data/provider/private_key.dart';
|
import 'package:toolbox/data/provider/private_key.dart';
|
||||||
import 'package:toolbox/data/provider/server.dart';
|
import 'package:toolbox/data/provider/server.dart';
|
||||||
|
import 'package:toolbox/data/provider/sftp_download.dart';
|
||||||
import 'package:toolbox/data/provider/snippet.dart';
|
import 'package:toolbox/data/provider/snippet.dart';
|
||||||
import 'package:toolbox/locator.dart';
|
import 'package:toolbox/locator.dart';
|
||||||
|
|
||||||
@@ -70,6 +71,8 @@ Future<void> main() async {
|
|||||||
ChangeNotifierProvider(create: (_) => locator<ServerProvider>()),
|
ChangeNotifierProvider(create: (_) => locator<ServerProvider>()),
|
||||||
ChangeNotifierProvider(create: (_) => locator<SnippetProvider>()),
|
ChangeNotifierProvider(create: (_) => locator<SnippetProvider>()),
|
||||||
ChangeNotifierProvider(create: (_) => locator<PrivateKeyProvider>()),
|
ChangeNotifierProvider(create: (_) => locator<PrivateKeyProvider>()),
|
||||||
|
ChangeNotifierProvider(
|
||||||
|
create: (_) => locator<SftpDownloadProvider>()),
|
||||||
],
|
],
|
||||||
child: const MyApp(),
|
child: const MyApp(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:toolbox/data/model/docker/ps.dart';
|
|||||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
import 'package:toolbox/data/model/server/server_private_info.dart';
|
||||||
import 'package:toolbox/data/provider/docker.dart';
|
import 'package:toolbox/data/provider/docker.dart';
|
||||||
import 'package:toolbox/data/provider/server.dart';
|
import 'package:toolbox/data/provider/server.dart';
|
||||||
|
import 'package:toolbox/generated/l10n.dart';
|
||||||
import 'package:toolbox/locator.dart';
|
import 'package:toolbox/locator.dart';
|
||||||
import 'package:toolbox/view/widget/center_loading.dart';
|
import 'package:toolbox/view/widget/center_loading.dart';
|
||||||
import 'package:toolbox/view/widget/two_line_text.dart';
|
import 'package:toolbox/view/widget/two_line_text.dart';
|
||||||
@@ -25,6 +26,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
|||||||
final _docker = locator<DockerProvider>();
|
final _docker = locator<DockerProvider>();
|
||||||
final greyTextStyle = const TextStyle(color: Colors.grey);
|
final greyTextStyle = const TextStyle(color: Colors.grey);
|
||||||
late MediaQueryData _media;
|
late MediaQueryData _media;
|
||||||
|
late S s;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@@ -36,6 +38,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
|||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
super.didChangeDependencies();
|
super.didChangeDependencies();
|
||||||
_media = MediaQuery.of(context);
|
_media = MediaQuery.of(context);
|
||||||
|
s = S.of(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -46,7 +49,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
|||||||
.firstWhere((element) => element.info == widget.spi)
|
.firstWhere((element) => element.info == widget.spi)
|
||||||
.client;
|
.client;
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
showSnackBar(context, const Text('No client found'));
|
showSnackBar(context, Text(s.noClient));
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -87,7 +90,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
|||||||
padding: const EdgeInsets.all(7),
|
padding: const EdgeInsets.all(7),
|
||||||
children: [
|
children: [
|
||||||
_buildVersion(
|
_buildVersion(
|
||||||
docker.edition ?? 'Unknown', docker.version ?? 'Unknown'),
|
docker.edition ?? s.unknown, docker.version ?? s.unknown),
|
||||||
_buildPsItems(running, docker)
|
_buildPsItems(running, docker)
|
||||||
].map((e) => RoundRectCard(e)).toList(),
|
].map((e) => RoundRectCard(e)).toList(),
|
||||||
);
|
);
|
||||||
@@ -97,14 +100,14 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
|||||||
Widget _buildSolution(String err) {
|
Widget _buildSolution(String err) {
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case 'docker not found':
|
case 'docker not found':
|
||||||
return const UrlText(
|
return UrlText(
|
||||||
text: 'Please https://docs.docker.com/engine/install docker first.',
|
text: s.installDockerWithUrl,
|
||||||
replace: 'install',
|
replace: s.install,
|
||||||
);
|
);
|
||||||
case 'no client':
|
case 'no client':
|
||||||
return const Text('Plz wait for the connection to be established.');
|
return Text(s.dockerWaitConnection);
|
||||||
default:
|
default:
|
||||||
return const Text('Unknown error');
|
return Text(s.unknownError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +123,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
|||||||
|
|
||||||
Widget _buildPsItems(List<DockerPsItem> running, DockerProvider docker) {
|
Widget _buildPsItems(List<DockerPsItem> running, DockerProvider docker) {
|
||||||
return ExpansionTile(
|
return ExpansionTile(
|
||||||
title: const Text('Container Status'),
|
title: Text(s.containerStatus),
|
||||||
subtitle: Text(_buildSubtitle(running), style: greyTextStyle),
|
subtitle: Text(_buildSubtitle(running), style: greyTextStyle),
|
||||||
children: running.map((item) {
|
children: running.map((item) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
@@ -186,8 +189,8 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
|||||||
final runningCount = running.where((element) => element.running).length;
|
final runningCount = running.where((element) => element.running).length;
|
||||||
final stoped = running.length - runningCount;
|
final stoped = running.length - runningCount;
|
||||||
if (stoped == 0) {
|
if (stoped == 0) {
|
||||||
return '$runningCount container running.';
|
return s.dockerStatusRunningFmt(runningCount);
|
||||||
}
|
}
|
||||||
return '$runningCount running, $stoped stoped.';
|
return s.dockerStatusRunningAndStoppedFmt(runningCount, stoped);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import 'package:toolbox/view/page/ping.dart';
|
|||||||
import 'package:toolbox/view/page/private_key/list.dart';
|
import 'package:toolbox/view/page/private_key/list.dart';
|
||||||
import 'package:toolbox/view/page/server/tab.dart';
|
import 'package:toolbox/view/page/server/tab.dart';
|
||||||
import 'package:toolbox/view/page/setting.dart';
|
import 'package:toolbox/view/page/setting.dart';
|
||||||
|
import 'package:toolbox/view/page/sftp/downloaded.dart';
|
||||||
import 'package:toolbox/view/page/snippet/list.dart';
|
import 'package:toolbox/view/page/snippet/list.dart';
|
||||||
import 'package:toolbox/view/widget/url_text.dart';
|
import 'package:toolbox/view/widget/url_text.dart';
|
||||||
|
|
||||||
@@ -233,6 +234,13 @@ class _MyHomePageState extends State<MyHomePage>
|
|||||||
const StoredPrivateKeysPage(), 'private key list')
|
const StoredPrivateKeysPage(), 'private key list')
|
||||||
.go(context),
|
.go(context),
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.download),
|
||||||
|
title: Text(s.download),
|
||||||
|
onTap: () =>
|
||||||
|
AppRoute(const SFTPDownloadedPage(), 'snippet list')
|
||||||
|
.go(context),
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.snippet_folder),
|
leading: const Icon(Icons.snippet_folder),
|
||||||
title: Text(s.snippet),
|
title: Text(s.snippet),
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import 'package:toolbox/view/page/apt.dart';
|
|||||||
import 'package:toolbox/view/page/docker.dart';
|
import 'package:toolbox/view/page/docker.dart';
|
||||||
import 'package:toolbox/view/page/server/detail.dart';
|
import 'package:toolbox/view/page/server/detail.dart';
|
||||||
import 'package:toolbox/view/page/server/edit.dart';
|
import 'package:toolbox/view/page/server/edit.dart';
|
||||||
import 'package:toolbox/view/page/sftp.dart';
|
import 'package:toolbox/view/page/sftp/view.dart';
|
||||||
import 'package:toolbox/view/page/snippet/list.dart';
|
import 'package:toolbox/view/page/snippet/list.dart';
|
||||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
import 'package:toolbox/view/widget/round_rect_card.dart';
|
||||||
|
|
||||||
|
|||||||
187
lib/view/page/sftp/downloaded.dart
Normal file
187
lib/view/page/sftp/downloaded.dart
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
import 'package:toolbox/core/extension/colorx.dart';
|
||||||
|
import 'package:toolbox/core/extension/numx.dart';
|
||||||
|
import 'package:toolbox/core/extension/stringx.dart';
|
||||||
|
import 'package:toolbox/core/route.dart';
|
||||||
|
import 'package:toolbox/core/utils.dart';
|
||||||
|
import 'package:toolbox/data/model/app/path_with_prefix.dart';
|
||||||
|
import 'package:toolbox/data/res/color.dart';
|
||||||
|
import 'package:toolbox/data/res/font_style.dart';
|
||||||
|
import 'package:toolbox/data/res/path.dart';
|
||||||
|
import 'package:toolbox/generated/l10n.dart';
|
||||||
|
import 'package:toolbox/view/page/sftp/downloading.dart';
|
||||||
|
import 'package:toolbox/view/widget/fade_in.dart';
|
||||||
|
|
||||||
|
class SFTPDownloadedPage extends StatefulWidget {
|
||||||
|
const SFTPDownloadedPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SFTPDownloadedPage> createState() => _SFTPDownloadedPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
|
||||||
|
PathWithPrefix? _path;
|
||||||
|
String? _prefixPath;
|
||||||
|
late S s;
|
||||||
|
late ThemeData _theme;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
sftpDownloadDir.then((dir) {
|
||||||
|
_path = PathWithPrefix(dir.path);
|
||||||
|
_prefixPath = dir.path + '/';
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
s = S.of(context);
|
||||||
|
_theme = Theme.of(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(s.download),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.downloading),
|
||||||
|
onPressed: () =>
|
||||||
|
AppRoute(const SFTPDownloadingPage(), 'sftp downloading')
|
||||||
|
.go(context),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: FadeIn(
|
||||||
|
child: _buildBody(),
|
||||||
|
key: UniqueKey(),
|
||||||
|
),
|
||||||
|
bottomNavigationBar: SafeArea(
|
||||||
|
child: _buildPath(),
|
||||||
|
),
|
||||||
|
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
onPressed: (() {
|
||||||
|
if (_path!.path == _prefixPath) {
|
||||||
|
showSnackBar(context, Text(s.alreadyLastDir));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_path!.update('..');
|
||||||
|
setState(() {});
|
||||||
|
}),
|
||||||
|
child: const Icon(Icons.keyboard_arrow_left),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPath() {
|
||||||
|
return Container(
|
||||||
|
color: _theme.appBarTheme.foregroundColor,
|
||||||
|
padding: const EdgeInsets.fromLTRB(11, 7, 11, 11),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Divider(),
|
||||||
|
(_path?.path ?? s.loadingFiles).omitStartStr(
|
||||||
|
style: TextStyle(
|
||||||
|
color:
|
||||||
|
primaryColor.isBrightColor ? Colors.black : Colors.white),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBody() {
|
||||||
|
if (_path == null) {
|
||||||
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final dir = Directory(_path!.path);
|
||||||
|
final files = dir.listSync();
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: files.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
var file = files[index];
|
||||||
|
var fileName = file.path.split('/').last;
|
||||||
|
var stat = file.statSync();
|
||||||
|
var isDir = stat.type == FileSystemEntityType.directory;
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
leading: isDir
|
||||||
|
? const Icon(Icons.folder)
|
||||||
|
: const Icon(Icons.insert_drive_file),
|
||||||
|
title: Text(fileName),
|
||||||
|
subtitle: isDir ? null : Text(stat.size.convertBytes),
|
||||||
|
trailing: Text(
|
||||||
|
stat.modified
|
||||||
|
.toString()
|
||||||
|
.substring(0, stat.modified.toString().length - 4),
|
||||||
|
style: grey,
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
if (!isDir) {
|
||||||
|
showFileActionDialog(file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_path!.update(fileName);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showFileActionDialog(FileSystemEntity file) {
|
||||||
|
final fileName = file.path.split('/').last;
|
||||||
|
showRoundDialog(
|
||||||
|
context,
|
||||||
|
s.choose,
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.delete),
|
||||||
|
title: Text(s.delete),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
showRoundDialog(
|
||||||
|
context, s.sureDelete(fileName), const SizedBox(), [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: Text(s.cancel)),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
file.deleteSync();
|
||||||
|
setState(() {});
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text(s.ok),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.open_in_new),
|
||||||
|
title: Text(s.open),
|
||||||
|
onTap: () {
|
||||||
|
Share.shareFiles([file.absolute.path],
|
||||||
|
text: '$fileName from ServerBox');
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
TextButton(
|
||||||
|
onPressed: (() => Navigator.of(context).pop()),
|
||||||
|
child: Text(s.close))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
105
lib/view/page/sftp/downloading.dart
Normal file
105
lib/view/page/sftp/downloading.dart
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
import 'package:toolbox/core/extension/numx.dart';
|
||||||
|
import 'package:toolbox/core/utils.dart';
|
||||||
|
import 'package:toolbox/data/model/sftp/download_status.dart';
|
||||||
|
import 'package:toolbox/data/provider/sftp_download.dart';
|
||||||
|
import 'package:toolbox/data/res/font_style.dart';
|
||||||
|
import 'package:toolbox/generated/l10n.dart';
|
||||||
|
import 'package:toolbox/view/widget/center_loading.dart';
|
||||||
|
import 'package:toolbox/view/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.download,
|
||||||
|
style: size18,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: _buildBody(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBody() {
|
||||||
|
return Consumer<SftpDownloadProvider>(builder: (__, pro, _) {
|
||||||
|
if (pro.status.isEmpty) {
|
||||||
|
return Center(
|
||||||
|
child: Text(s.sftpNoDownloadTask),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ListView.builder(
|
||||||
|
padding: const EdgeInsets.all(13),
|
||||||
|
itemCount: pro.status.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final status = pro.status[index];
|
||||||
|
return _buildItem(status);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _wrapInCard(SftpDownloadStatus status, String? subtitle,
|
||||||
|
{Widget? trailing}) {
|
||||||
|
return RoundRectCard(ListTile(
|
||||||
|
title: Text(status.fileName),
|
||||||
|
subtitle: subtitle == null
|
||||||
|
? null
|
||||||
|
: Text(
|
||||||
|
subtitle,
|
||||||
|
style: grey,
|
||||||
|
),
|
||||||
|
trailing: trailing,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildItem(SftpDownloadStatus status) {
|
||||||
|
if (status.error != null) {
|
||||||
|
showSnackBar(context, Text(status.error.toString()));
|
||||||
|
}
|
||||||
|
switch (status.status) {
|
||||||
|
case SftpWorkerStatus.finished:
|
||||||
|
return _wrapInCard(status,
|
||||||
|
'${s.downloadFinished}, ${s.spentTime(status.spentTime ?? s.unknown)}',
|
||||||
|
trailing: IconButton(
|
||||||
|
onPressed: () => Share.shareFiles([status.item.localPath],
|
||||||
|
text: '${status.fileName} from ServerBox'),
|
||||||
|
icon: const Icon(Icons.open_in_new)));
|
||||||
|
case SftpWorkerStatus.downloading:
|
||||||
|
return _wrapInCard(
|
||||||
|
status,
|
||||||
|
s.downloadStatus((status.progress ?? 0.0).toStringAsFixed(2),
|
||||||
|
(status.size ?? 0).convertBytes),
|
||||||
|
trailing:
|
||||||
|
CircularProgressIndicator(value: (status.progress ?? 0) / 100));
|
||||||
|
case SftpWorkerStatus.preparing:
|
||||||
|
return _wrapInCard(status, s.sftpDlPrepare, trailing: centerLoading);
|
||||||
|
case SftpWorkerStatus.sshConnectted:
|
||||||
|
return _wrapInCard(status, s.sftpSSHConnected, trailing: centerLoading);
|
||||||
|
default:
|
||||||
|
return _wrapInCard(status, s.unknown,
|
||||||
|
trailing: const Icon(
|
||||||
|
Icons.error,
|
||||||
|
size: 40,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,21 @@
|
|||||||
import 'package:dartssh2/dartssh2.dart';
|
import 'package:dartssh2/dartssh2.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:toolbox/core/extension/numx.dart';
|
import 'package:toolbox/core/extension/numx.dart';
|
||||||
|
import 'package:toolbox/core/extension/stringx.dart';
|
||||||
|
import 'package:toolbox/core/route.dart';
|
||||||
import 'package:toolbox/core/utils.dart';
|
import 'package:toolbox/core/utils.dart';
|
||||||
import 'package:toolbox/data/model/server/server_connection_state.dart';
|
import 'package:toolbox/data/model/server/server_connection_state.dart';
|
||||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
import 'package:toolbox/data/model/server/server_private_info.dart';
|
||||||
import 'package:toolbox/data/model/sftp/absolute_path.dart';
|
import 'package:toolbox/data/model/sftp/absolute_path.dart';
|
||||||
import 'package:toolbox/data/model/sftp/sftp_side_status.dart';
|
import 'package:toolbox/data/model/sftp/download_worker.dart';
|
||||||
|
import 'package:toolbox/data/model/sftp/browser_status.dart';
|
||||||
import 'package:toolbox/data/provider/server.dart';
|
import 'package:toolbox/data/provider/server.dart';
|
||||||
|
import 'package:toolbox/data/provider/sftp_download.dart';
|
||||||
|
import 'package:toolbox/data/res/path.dart';
|
||||||
|
import 'package:toolbox/data/store/private_key.dart';
|
||||||
|
import 'package:toolbox/generated/l10n.dart';
|
||||||
import 'package:toolbox/locator.dart';
|
import 'package:toolbox/locator.dart';
|
||||||
|
import 'package:toolbox/view/page/sftp/downloading.dart';
|
||||||
import 'package:toolbox/view/widget/fade_in.dart';
|
import 'package:toolbox/view/widget/fade_in.dart';
|
||||||
import 'package:toolbox/view/widget/two_line_text.dart';
|
import 'package:toolbox/view/widget/two_line_text.dart';
|
||||||
|
|
||||||
@@ -20,16 +28,18 @@ class SFTPPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SFTPPageState extends State<SFTPPage> {
|
class _SFTPPageState extends State<SFTPPage> {
|
||||||
final SFTPSideViewStatus _status = SFTPSideViewStatus();
|
final SftpBrowserStatus _status = SftpBrowserStatus();
|
||||||
|
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
late MediaQueryData _media;
|
late MediaQueryData _media;
|
||||||
|
late S s;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
super.didChangeDependencies();
|
super.didChangeDependencies();
|
||||||
_media = MediaQuery.of(context);
|
_media = MediaQuery.of(context);
|
||||||
|
s = S.of(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -133,94 +143,66 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.delete),
|
leading: const Icon(Icons.delete),
|
||||||
title: const Text('Delete'),
|
title: Text(s.delete),
|
||||||
onTap: () => delete(context, file),
|
onTap: () => delete(context, file),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.folder),
|
leading: const Icon(Icons.folder),
|
||||||
title: const Text('Create Folder'),
|
title: Text(s.createFolder),
|
||||||
onTap: () => mkdir(context)),
|
onTap: () => mkdir(context)),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.edit),
|
leading: const Icon(Icons.edit),
|
||||||
title: const Text('Rename'),
|
title: Text(s.rename),
|
||||||
onTap: () => rename(context, file),
|
onTap: () => rename(context, file),
|
||||||
),
|
),
|
||||||
// ListTile(
|
ListTile(
|
||||||
// leading: const Icon(Icons.download),
|
leading: const Icon(Icons.download),
|
||||||
// title: const Text('Download'),
|
title: Text(s.download),
|
||||||
// onTap: () => download(context, file),
|
onTap: () => download(context, file),
|
||||||
// )
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
child: const Text('Cancel'))
|
child: Text(s.cancel))
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// void download(BuildContext context, SftpName name) {
|
void download(BuildContext context, SftpName name) {
|
||||||
// showRoundDialog(
|
showRoundDialog(
|
||||||
// context, 'Download', Text('Download ${name.filename} to local?'), [
|
context, s.download, Text(s.dl2Local(name.filename)), [
|
||||||
// TextButton(
|
TextButton(
|
||||||
// onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
// child: const Text('Cancel')),
|
child: Text(s.cancel)),
|
||||||
// TextButton(
|
TextButton(
|
||||||
// onPressed: () async {
|
onPressed: () async {
|
||||||
// var result = '';
|
Navigator.of(context).pop();
|
||||||
// try {
|
final prePath = _status.path!.path;
|
||||||
// Navigator.of(context).pop();
|
final remotePath =
|
||||||
// showRoundDialog(
|
prePath + (prePath.endsWith('/') ? '' : '/') + name.filename;
|
||||||
// context,
|
final local = '${(await sftpDownloadDir).path}$remotePath';
|
||||||
// name.filename,
|
final pubKeyId = _status.spi!.pubKeyId;
|
||||||
// const Text('Downloading...\nKepp this app in the foreground.',
|
locator<SftpDownloadProvider>().add(
|
||||||
// textAlign: TextAlign.center),
|
DownloadItem(_status.spi!, remotePath, local),
|
||||||
// [],
|
key: pubKeyId == null
|
||||||
// barrierDismiss: false);
|
? null
|
||||||
// final path = await sftpDownloadDir;
|
: locator<PrivateKeyStore>().get(pubKeyId).privateKey);
|
||||||
// final local = File('${path.path}/${name.filename}');
|
showRoundDialog(context, s.goSftpDlPage, const SizedBox(), [
|
||||||
// if (await local.exists()) {
|
TextButton(
|
||||||
// await local.delete();
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
// }
|
child: Text(s.cancel)),
|
||||||
|
TextButton(onPressed: () => AppRoute(const SFTPDownloadingPage(), 'sftp downloading'), child: Text(s.ok))
|
||||||
// final localFile =
|
]);
|
||||||
// await local.open(mode: FileMode.writeOnlyAppend);
|
},
|
||||||
// final remotePath = _status.path!.path + '/' + name.filename;
|
child: Text(s.download))
|
||||||
// final file = await _status.client!.open(remotePath);
|
]);
|
||||||
// final size = (await file.stat()).size;
|
}
|
||||||
// if (size == null) {
|
|
||||||
// throw Exception('can not get file size');
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const chunkSize = 1024 * 128;
|
|
||||||
// for (var i = 0; i < size; i += chunkSize) {
|
|
||||||
// final data = file.read(length: chunkSize);
|
|
||||||
// await for (var item in data) {
|
|
||||||
// localFile.writeFrom(item);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } catch (e) {
|
|
||||||
// result = e.toString();
|
|
||||||
// } finally {
|
|
||||||
// Navigator.of(context).pop();
|
|
||||||
// if (result.isEmpty) {
|
|
||||||
// result = 'Donwloaded successfully.';
|
|
||||||
// }
|
|
||||||
// showRoundDialog(context, 'Result', Text(result), [
|
|
||||||
// TextButton(
|
|
||||||
// onPressed: () => Navigator.of(context).pop(),
|
|
||||||
// child: const Text('OK'))
|
|
||||||
// ]);
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// child: const Text('Download'))
|
|
||||||
// ]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
void delete(BuildContext context, SftpName file) {
|
void delete(BuildContext context, SftpName file) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
showRoundDialog(
|
showRoundDialog(
|
||||||
context, 'Confirm', Text('Are you sure to delete ${file.filename}?'), [
|
context, s.attention, Text(s.sureDelete(file.filename)), [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
child: const Text('Cancel')),
|
child: const Text('Cancel')),
|
||||||
@@ -230,9 +212,9 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
listDir();
|
listDir();
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: Text(
|
||||||
'Delete',
|
s.delete,
|
||||||
style: TextStyle(color: Colors.red),
|
style: const TextStyle(color: Colors.red),
|
||||||
)),
|
)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -242,25 +224,25 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
final textController = TextEditingController();
|
final textController = TextEditingController();
|
||||||
showRoundDialog(
|
showRoundDialog(
|
||||||
context,
|
context,
|
||||||
'Create Folder',
|
s.createFolder,
|
||||||
TextField(
|
TextField(
|
||||||
controller: textController,
|
controller: textController,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Folder Name',
|
labelText: s.name,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
child: const Text('Cancel')),
|
child: Text(s.cancel)),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (textController.text == '') {
|
if (textController.text == '') {
|
||||||
showRoundDialog(context, 'Attention',
|
showRoundDialog(context, s.attention,
|
||||||
const Text('You need input a name.'), [
|
Text(s.fieldMustNotEmpty), [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
child: const Text('OK')),
|
child: Text(s.ok)),
|
||||||
]);
|
]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -269,9 +251,9 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
listDir();
|
listDir();
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: Text(
|
||||||
'Create',
|
s.ok,
|
||||||
style: TextStyle(color: Colors.red),
|
style: const TextStyle(color: Colors.red),
|
||||||
)),
|
)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -281,25 +263,25 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
final textController = TextEditingController();
|
final textController = TextEditingController();
|
||||||
showRoundDialog(
|
showRoundDialog(
|
||||||
context,
|
context,
|
||||||
'Rename',
|
s.rename,
|
||||||
TextField(
|
TextField(
|
||||||
controller: textController,
|
controller: textController,
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'New Name',
|
labelText: s.name,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
child: const Text('Cancel')),
|
child: Text(s.cancel)),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (textController.text == '') {
|
if (textController.text == '') {
|
||||||
showRoundDialog(context, 'Attention',
|
showRoundDialog(context, s.attention,
|
||||||
const Text('You need input a name.'), [
|
Text(s.fieldMustNotEmpty), [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
child: const Text('OK')),
|
child: Text(s.ok)),
|
||||||
]);
|
]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -308,9 +290,9 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
listDir();
|
listDir();
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: Text(
|
||||||
'Rename',
|
s.rename,
|
||||||
style: TextStyle(color: Colors.red),
|
style: const TextStyle(color: Colors.red),
|
||||||
)),
|
)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -336,10 +318,10 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await showRoundDialog(context, 'Error', Text(e.toString()), [
|
await showRoundDialog(context, s.error, Text(e.toString()), [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
child: const Text('OK'))
|
child: Text(s.ok))
|
||||||
]);
|
]);
|
||||||
if (_status.path!.undo()) {
|
if (_status.path!.undo()) {
|
||||||
await listDir();
|
await listDir();
|
||||||
@@ -350,43 +332,9 @@ class _SFTPPageState extends State<SFTPPage> {
|
|||||||
Widget _buildDestSelector() {
|
Widget _buildDestSelector() {
|
||||||
final str = _status.path?.path;
|
final str = _status.path?.path;
|
||||||
return ExpansionTile(
|
return ExpansionTile(
|
||||||
title: Text(_status.spi?.name ?? 'Choose target'),
|
title: Text(_status.spi?.name ?? s.chooseDestination),
|
||||||
subtitle: _status.selected
|
subtitle: _status.selected
|
||||||
? LayoutBuilder(builder: (context, size) {
|
? str!.omitStartStr(style: const TextStyle(color: Colors.grey))
|
||||||
bool exceeded = false;
|
|
||||||
int len = 0;
|
|
||||||
for (; !exceeded && len < str!.length; len++) {
|
|
||||||
// Build the textspan
|
|
||||||
var span = TextSpan(
|
|
||||||
text: '...' + str.substring(str.length - len),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize:
|
|
||||||
Theme.of(context).textTheme.bodyText1?.fontSize ??
|
|
||||||
14),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Use a textpainter to determine if it will exceed max lines
|
|
||||||
var tp = TextPainter(
|
|
||||||
maxLines: 1,
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
text: span,
|
|
||||||
);
|
|
||||||
|
|
||||||
// trigger it to layout
|
|
||||||
tp.layout(maxWidth: size.maxWidth);
|
|
||||||
|
|
||||||
// whether the text overflowed or not
|
|
||||||
exceeded = tp.didExceedMaxLines;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Text(
|
|
||||||
(exceeded ? '...' : '') + str!.substring(str.length - len),
|
|
||||||
overflow: TextOverflow.clip,
|
|
||||||
maxLines: 1,
|
|
||||||
style: const TextStyle(color: Colors.grey),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
: null,
|
: null,
|
||||||
children: locator<ServerProvider>()
|
children: locator<ServerProvider>()
|
||||||
.servers
|
.servers
|
||||||
@@ -29,8 +29,8 @@ class _MyFadeInState extends State<FadeIn> with SingleTickerProviderStateMixin {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
super.dispose();
|
|
||||||
_controller.dispose();
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ import FlutterMacOS
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import path_provider_macos
|
import path_provider_macos
|
||||||
|
import share_plus_macos
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
|
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,15 @@ PODS:
|
|||||||
- FlutterMacOS (1.0.0)
|
- FlutterMacOS (1.0.0)
|
||||||
- path_provider_macos (0.0.1):
|
- path_provider_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- share_plus_macos (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
- url_launcher_macos (0.0.1):
|
- url_launcher_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||||
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
|
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
|
||||||
|
- share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`)
|
||||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
@@ -15,12 +18,15 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral
|
:path: Flutter/ephemeral
|
||||||
path_provider_macos:
|
path_provider_macos:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
|
||||||
|
share_plus_macos:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
|
FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
|
||||||
path_provider_macos: 160cab0d5461f0c0e02995469a98f24bdb9a3f1f
|
path_provider_macos: 160cab0d5461f0c0e02995469a98f24bdb9a3f1f
|
||||||
|
share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4
|
||||||
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
|
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
|
||||||
|
|
||||||
PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c
|
PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c
|
||||||
|
|||||||
@@ -73,8 +73,8 @@ Future<void> updateBuildData() async {
|
|||||||
await writeStaicConfigFile(data, 'BuildData', buildDataFilePath);
|
await writeStaicConfigFile(data, 'BuildData', buildDataFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dartFormat() {
|
Future<void> dartFormat() async {
|
||||||
final result = Process.runSync('dart', ['format', '.']);
|
final result = await Process.run('dart', ['format', '.']);
|
||||||
print('\n' + result.stdout);
|
print('\n' + result.stdout);
|
||||||
if (result.exitCode != 0) {
|
if (result.exitCode != 0) {
|
||||||
print(result.stderr);
|
print(result.stderr);
|
||||||
@@ -83,7 +83,7 @@ void dartFormat() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void flutterRun(String? mode) {
|
void flutterRun(String? mode) {
|
||||||
Process.start('flutter', ['run', mode == null ? '' : '--$mode'],
|
Process.start('flutter', mode == null ? ['run'] : ['run', '--$mode'],
|
||||||
mode: ProcessStartMode.inheritStdio, runInShell: true);
|
mode: ProcessStartMode.inheritStdio, runInShell: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@ void main(List<String> args) async {
|
|||||||
final buildFunc = [flutterBuildIOS, flutterBuildAndroid];
|
final buildFunc = [flutterBuildIOS, flutterBuildAndroid];
|
||||||
build = await getGitCommitCount();
|
build = await getGitCommitCount();
|
||||||
await updateBuildData();
|
await updateBuildData();
|
||||||
dartFormat();
|
await dartFormat();
|
||||||
if (args.length > 1) {
|
if (args.length > 1) {
|
||||||
final platform = args[1];
|
final platform = args[1];
|
||||||
switch (platform) {
|
switch (platform) {
|
||||||
|
|||||||
58
pubspec.lock
58
pubspec.lock
@@ -138,6 +138,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
|
easy_isolate:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: easy_isolate
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.0"
|
||||||
extended_image:
|
extended_image:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -347,6 +354,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.0"
|
version: "1.7.0"
|
||||||
|
mime:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: mime
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -362,7 +376,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0"
|
version: "1.8.0"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
@@ -480,6 +494,48 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.8+1"
|
version: "0.3.8+1"
|
||||||
|
share_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: share_plus
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.4"
|
||||||
|
share_plus_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: share_plus_linux
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
|
share_plus_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: share_plus_macos
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
|
share_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: share_plus_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.2"
|
||||||
|
share_plus_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: share_plus_web
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
|
share_plus_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: share_plus_windows
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ dependencies:
|
|||||||
marquee: ^2.2.0
|
marquee: ^2.2.0
|
||||||
dropdown_button2: ^1.1.1
|
dropdown_button2: ^1.1.1
|
||||||
flutter_advanced_drawer: ^1.3.0
|
flutter_advanced_drawer: ^1.3.0
|
||||||
|
path_provider: ^2.0.9
|
||||||
|
easy_isolate: ^1.3.0
|
||||||
|
share_plus: ^4.0.4
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_native_splash: ^2.1.6
|
flutter_native_splash: ^2.1.6
|
||||||
|
|||||||
Reference in New Issue
Block a user