mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
new: icloud manual
This commit is contained in:
@@ -1,14 +1,43 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:hive_flutter/hive_flutter.dart';
|
import 'package:hive_flutter/hive_flutter.dart';
|
||||||
|
import 'package:toolbox/data/res/path.dart';
|
||||||
|
|
||||||
class PersistentStore<E> {
|
class PersistentStore<E> {
|
||||||
late Box<E> box;
|
late final Box<E> box;
|
||||||
|
|
||||||
Future<PersistentStore<E>> init({String boxName = 'defaultBox'}) async {
|
final String boxName;
|
||||||
box = await Hive.openBox(boxName);
|
|
||||||
return this;
|
PersistentStore(this.boxName);
|
||||||
|
|
||||||
|
Future<void> init() async => box = await Hive.openBox(boxName);
|
||||||
|
|
||||||
|
/// Get all db filenames.
|
||||||
|
///
|
||||||
|
/// - [suffixs] defaults to ['.hive']
|
||||||
|
///
|
||||||
|
/// - If [hideSetting] is true, hide 'setting.hive'
|
||||||
|
static Future<List<String>> getFileNames({
|
||||||
|
bool hideSetting = false,
|
||||||
|
List<String>? suffixs,
|
||||||
|
}) async {
|
||||||
|
final docPath = await Paths.doc;
|
||||||
|
final dir = Directory(docPath);
|
||||||
|
final files = await dir.list().toList();
|
||||||
|
if (suffixs != null) {
|
||||||
|
files.removeWhere((e) => !suffixs.contains(e.path.split('.').last));
|
||||||
|
} else {
|
||||||
|
// filter out non-hive(db) files
|
||||||
|
files.removeWhere((e) => !e.path.endsWith('.hive'));
|
||||||
|
}
|
||||||
|
if (hideSetting) {
|
||||||
|
files.removeWhere((e) => e.path.endsWith('setting.hive'));
|
||||||
|
}
|
||||||
|
final paths =
|
||||||
|
files.map((e) => e.path.replaceFirst('$docPath/', '')).toList();
|
||||||
|
return paths;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,29 @@ import 'dart:async';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:icloud_storage/icloud_storage.dart';
|
import 'package:icloud_storage/icloud_storage.dart';
|
||||||
import 'package:toolbox/core/utils/platform/base.dart';
|
|
||||||
import 'package:toolbox/data/res/logger.dart';
|
import 'package:toolbox/data/res/logger.dart';
|
||||||
|
|
||||||
import '../../data/model/app/error.dart';
|
import '../../data/model/app/error.dart';
|
||||||
import '../../data/model/app/json.dart';
|
import '../../data/model/app/json.dart';
|
||||||
import '../../data/res/path.dart';
|
import '../../data/res/path.dart';
|
||||||
|
|
||||||
|
class SyncResult<T, E> {
|
||||||
|
final List<T> up;
|
||||||
|
final List<T> down;
|
||||||
|
final Map<T, E> err;
|
||||||
|
|
||||||
|
const SyncResult({
|
||||||
|
required this.up,
|
||||||
|
required this.down,
|
||||||
|
required this.err,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SyncResult{up: $up, down: $down, err: $err}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ICloud {
|
class ICloud {
|
||||||
static const _containerId = 'iCloud.tech.lolli.serverbox';
|
static const _containerId = 'iCloud.tech.lolli.serverbox';
|
||||||
|
|
||||||
@@ -91,41 +107,44 @@ class ICloud {
|
|||||||
///
|
///
|
||||||
/// - [relativePath] is the path relative to [docDir],
|
/// - [relativePath] is the path relative to [docDir],
|
||||||
/// must not starts with `/`
|
/// must not starts with `/`
|
||||||
|
/// - [bakSuffix] is the suffix of backup file, default to [null].
|
||||||
|
/// All files downloaded from cloud will be suffixed with [bakSuffix].
|
||||||
///
|
///
|
||||||
/// Return `null` if upload success, `ICloudErr` otherwise
|
/// Return `null` if upload success, `ICloudErr` otherwise
|
||||||
///
|
///
|
||||||
/// TODO: consider merge strategy, use [SyncAble] and [JsonSerializable]
|
/// TODO: consider merge strategy, use [SyncAble] and [JsonSerializable]
|
||||||
static Future<Iterable<ICloudErr>?> sync({
|
static Future<SyncResult<String, ICloudErr>> sync({
|
||||||
required Iterable<String> relativePaths,
|
required Iterable<String> relativePaths,
|
||||||
|
String? bakPrefix,
|
||||||
}) async {
|
}) async {
|
||||||
final uploadFiles = <String>[];
|
final uploadFiles = <String>[];
|
||||||
final downloadFiles = <String>[];
|
final downloadFiles = <String>[];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final errs = <ICloudErr>[];
|
final errs = <String, ICloudErr>{};
|
||||||
|
|
||||||
final allFiles = await getAll();
|
final allFiles = await getAll();
|
||||||
|
|
||||||
/// remove files not in relativePaths
|
/// remove files not in relativePaths
|
||||||
allFiles.removeWhere((e) => !relativePaths.contains(e.relativePath));
|
allFiles.removeWhere((e) => !relativePaths.contains(e.relativePath));
|
||||||
|
|
||||||
final mission = <Future<void>>[];
|
final missions = <Future<void>>[];
|
||||||
|
|
||||||
/// upload files not in iCloud
|
/// upload files not in iCloud
|
||||||
final missed = relativePaths.where((e) {
|
final missed = relativePaths.where((e) {
|
||||||
return !allFiles.any((f) => f.relativePath == e);
|
return !allFiles.any((f) => f.relativePath == e);
|
||||||
});
|
});
|
||||||
mission.addAll(missed.map((e) async {
|
missions.addAll(missed.map((e) async {
|
||||||
final err = await upload(relativePath: e);
|
final err = await upload(relativePath: e);
|
||||||
if (err != null) {
|
if (err != null) {
|
||||||
errs.add(err);
|
errs[e] = err;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
final docPath = await Paths.doc;
|
final docPath = await Paths.doc;
|
||||||
|
|
||||||
/// compare files in iCloud and local
|
/// compare files in iCloud and local
|
||||||
mission.addAll(allFiles.map((file) async {
|
missions.addAll(allFiles.map((file) async {
|
||||||
final relativePath = file.relativePath;
|
final relativePath = file.relativePath;
|
||||||
|
|
||||||
/// Check date
|
/// Check date
|
||||||
@@ -134,7 +153,7 @@ class ICloud {
|
|||||||
/// Local file not found, download remote file
|
/// Local file not found, download remote file
|
||||||
final err = await download(relativePath: relativePath);
|
final err = await download(relativePath: relativePath);
|
||||||
if (err != null) {
|
if (err != null) {
|
||||||
errs.add(err);
|
errs[relativePath] = err;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -149,39 +168,34 @@ class ICloud {
|
|||||||
await delete(relativePath);
|
await delete(relativePath);
|
||||||
final err = await upload(relativePath: relativePath);
|
final err = await upload(relativePath: relativePath);
|
||||||
if (err != null) {
|
if (err != null) {
|
||||||
errs.add(err);
|
errs[relativePath] = err;
|
||||||
}
|
}
|
||||||
uploadFiles.add(relativePath);
|
uploadFiles.add(relativePath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remote is newer than local, so download remote
|
/// Remote is newer than local, so download remote
|
||||||
final err = await download(relativePath: relativePath);
|
final localPath = '$docPath/${bakPrefix ?? ''}$relativePath';
|
||||||
|
final err = await download(
|
||||||
|
relativePath: relativePath,
|
||||||
|
localPath: localPath,
|
||||||
|
);
|
||||||
if (err != null) {
|
if (err != null) {
|
||||||
errs.add(err);
|
errs[relativePath] = err;
|
||||||
}
|
}
|
||||||
downloadFiles.add(relativePath);
|
downloadFiles.add(relativePath);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await Future.wait(mission);
|
await Future.wait(missions);
|
||||||
|
|
||||||
return errs.isEmpty ? null : errs;
|
return SyncResult(up: uploadFiles, down: downloadFiles, err: errs);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning('iCloud sync: $relativePaths failed', e, s);
|
Loggers.app.warning('iCloud sync: $relativePaths failed', e, s);
|
||||||
return [ICloudErr(type: ICloudErrType.generic, message: '$e')];
|
return SyncResult(up: uploadFiles, down: downloadFiles, err: {
|
||||||
|
'Generic': ICloudErr(type: ICloudErrType.generic, message: '$e')
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
Loggers.app.info('iCloud sync, up: $uploadFiles, down: $downloadFiles');
|
Loggers.app.info('iCloud sync, up: $uploadFiles, down: $downloadFiles');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> syncDb() async {
|
|
||||||
if (!isIOS && !isMacOS) return;
|
|
||||||
final docPath = await Paths.doc;
|
|
||||||
final dir = Directory(docPath);
|
|
||||||
final files = await dir.list().toList();
|
|
||||||
// filter out non-hive(db) files
|
|
||||||
files.removeWhere((e) => !e.path.endsWith('.hive'));
|
|
||||||
final paths = files.map((e) => e.path.replaceFirst('$docPath/', ''));
|
|
||||||
await ICloud.sync(relativePaths: paths);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
class BuildData {
|
class BuildData {
|
||||||
static const String name = "ServerBox";
|
static const String name = "ServerBox";
|
||||||
static const int build = 597;
|
static const int build = 599;
|
||||||
static const String engine = "3.13.7";
|
static const String engine = "3.13.7";
|
||||||
static const String buildAt = "2023-10-15 13:38:49";
|
static const String buildAt = "2023-10-15 21:24:51";
|
||||||
static const int modifications = 9;
|
static const int modifications = 4;
|
||||||
static const int script = 21;
|
static const int script = 21;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:toolbox/core/persistant_store.dart';
|
||||||
import 'package:toolbox/data/store/docker.dart';
|
import 'package:toolbox/data/store/docker.dart';
|
||||||
import 'package:toolbox/data/store/first.dart';
|
import 'package:toolbox/data/store/first.dart';
|
||||||
import 'package:toolbox/data/store/history.dart';
|
import 'package:toolbox/data/store/history.dart';
|
||||||
@@ -17,4 +18,14 @@ class Stores {
|
|||||||
static final key = locator<PrivateKeyStore>();
|
static final key = locator<PrivateKeyStore>();
|
||||||
static final snippet = locator<SnippetStore>();
|
static final snippet = locator<SnippetStore>();
|
||||||
static final first = locator<FirstStore>();
|
static final first = locator<FirstStore>();
|
||||||
|
|
||||||
|
static final List<PersistentStore> all = [
|
||||||
|
setting,
|
||||||
|
server,
|
||||||
|
docker,
|
||||||
|
history,
|
||||||
|
key,
|
||||||
|
snippet,
|
||||||
|
first,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import '../../core/persistant_store.dart';
|
import '../../core/persistant_store.dart';
|
||||||
|
|
||||||
class DockerStore extends PersistentStore<String> {
|
class DockerStore extends PersistentStore<String> {
|
||||||
|
DockerStore() : super('docker');
|
||||||
|
|
||||||
String? fetch(String id) {
|
String? fetch(String id) {
|
||||||
return box.get(id);
|
return box.get(id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import 'package:toolbox/core/persistant_store.dart';
|
|||||||
|
|
||||||
/// It stores whether is the first time of some.
|
/// It stores whether is the first time of some.
|
||||||
class FirstStore extends PersistentStore<bool> {
|
class FirstStore extends PersistentStore<bool> {
|
||||||
|
FirstStore() : super('first');
|
||||||
|
|
||||||
/// Add Snippet `Install ServerBoxMonitor`
|
/// Add Snippet `Install ServerBoxMonitor`
|
||||||
late final iSSBM = StoreProperty(box, 'installMonitorSnippet', true);
|
late final iSSBM = StoreProperty(box, 'installMonitorSnippet', true);
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ class _MapHistory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class HistoryStore extends PersistentStore {
|
class HistoryStore extends PersistentStore {
|
||||||
|
HistoryStore() : super('history');
|
||||||
|
|
||||||
/// Paths that user has visited by 'Locate' button
|
/// Paths that user has visited by 'Locate' button
|
||||||
late final sftpGoPath = _ListHistory(box: box, name: 'sftpPath');
|
late final sftpGoPath = _ListHistory(box: box, name: 'sftpPath');
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import '../../core/persistant_store.dart';
|
|||||||
import '../model/server/private_key_info.dart';
|
import '../model/server/private_key_info.dart';
|
||||||
|
|
||||||
class PrivateKeyStore extends PersistentStore<PrivateKeyInfo> {
|
class PrivateKeyStore extends PersistentStore<PrivateKeyInfo> {
|
||||||
|
PrivateKeyStore() : super('key');
|
||||||
|
|
||||||
void put(PrivateKeyInfo info) {
|
void put(PrivateKeyInfo info) {
|
||||||
box.put(info.id, info);
|
box.put(info.id, info);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import '../../core/persistant_store.dart';
|
|||||||
import '../model/server/server_private_info.dart';
|
import '../model/server/server_private_info.dart';
|
||||||
|
|
||||||
class ServerStore extends PersistentStore<ServerPrivateInfo> {
|
class ServerStore extends PersistentStore<ServerPrivateInfo> {
|
||||||
|
ServerStore() : super('server');
|
||||||
|
|
||||||
void put(ServerPrivateInfo info) {
|
void put(ServerPrivateInfo info) {
|
||||||
box.put(info.id, info);
|
box.put(info.id, info);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import '../model/app/net_view.dart';
|
|||||||
import '../res/default.dart';
|
import '../res/default.dart';
|
||||||
|
|
||||||
class SettingStore extends PersistentStore {
|
class SettingStore extends PersistentStore {
|
||||||
|
SettingStore() : super('setting');
|
||||||
|
|
||||||
/// Convert all settings into json
|
/// Convert all settings into json
|
||||||
Map<String, dynamic> toJson() => {for (var e in box.keys) e: box.get(e)};
|
Map<String, dynamic> toJson() => {for (var e in box.keys) e: box.get(e)};
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import '../../core/persistant_store.dart';
|
|||||||
import '../model/server/snippet.dart';
|
import '../model/server/snippet.dart';
|
||||||
|
|
||||||
class SnippetStore extends PersistentStore<Snippet> {
|
class SnippetStore extends PersistentStore<Snippet> {
|
||||||
|
SnippetStore() : super('snippet');
|
||||||
|
|
||||||
void put(Snippet snippet) {
|
void put(Snippet snippet) {
|
||||||
box.put(snippet.name, snippet);
|
box.put(snippet.name, snippet);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,31 +36,31 @@ void _setupLocatorForProviders() {
|
|||||||
|
|
||||||
Future<void> _setupLocatorForStores() async {
|
Future<void> _setupLocatorForStores() async {
|
||||||
final setting = SettingStore();
|
final setting = SettingStore();
|
||||||
await setting.init(boxName: 'setting');
|
await setting.init();
|
||||||
locator.registerSingleton(setting);
|
locator.registerSingleton(setting);
|
||||||
|
|
||||||
final server = ServerStore();
|
final server = ServerStore();
|
||||||
await server.init(boxName: 'server');
|
await server.init();
|
||||||
locator.registerSingleton(server);
|
locator.registerSingleton(server);
|
||||||
|
|
||||||
final key = PrivateKeyStore();
|
final key = PrivateKeyStore();
|
||||||
await key.init(boxName: 'key');
|
await key.init();
|
||||||
locator.registerSingleton(key);
|
locator.registerSingleton(key);
|
||||||
|
|
||||||
final snippet = SnippetStore();
|
final snippet = SnippetStore();
|
||||||
await snippet.init(boxName: 'snippet');
|
await snippet.init();
|
||||||
locator.registerSingleton(snippet);
|
locator.registerSingleton(snippet);
|
||||||
|
|
||||||
final docker = DockerStore();
|
final docker = DockerStore();
|
||||||
await docker.init(boxName: 'docker');
|
await docker.init();
|
||||||
locator.registerSingleton(docker);
|
locator.registerSingleton(docker);
|
||||||
|
|
||||||
final history = HistoryStore();
|
final history = HistoryStore();
|
||||||
await history.init(boxName: 'history');
|
await history.init();
|
||||||
locator.registerSingleton(history);
|
locator.registerSingleton(history);
|
||||||
|
|
||||||
final first = FirstStore();
|
final first = FirstStore();
|
||||||
await first.init(boxName: 'first');
|
await first.init();
|
||||||
locator.registerSingleton(first);
|
locator.registerSingleton(first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import 'package:toolbox/data/res/store.dart';
|
|||||||
|
|
||||||
import 'app.dart';
|
import 'app.dart';
|
||||||
import 'core/analysis.dart';
|
import 'core/analysis.dart';
|
||||||
import 'core/utils/icloud.dart';
|
|
||||||
import 'core/utils/ui.dart';
|
import 'core/utils/ui.dart';
|
||||||
import 'data/model/app/net_view.dart';
|
import 'data/model/app/net_view.dart';
|
||||||
import 'data/model/server/private_key_info.dart';
|
import 'data/model/server/private_key_info.dart';
|
||||||
@@ -78,9 +77,6 @@ Future<void> initApp() async {
|
|||||||
primaryColor = Color(Stores.setting.primaryColor.fetch());
|
primaryColor = Color(Stores.setting.primaryColor.fetch());
|
||||||
loadFontFile(Stores.setting.fontPath.fetch());
|
loadFontFile(Stores.setting.fontPath.fetch());
|
||||||
|
|
||||||
// Don't call it via `await`, it will block the main thread.
|
|
||||||
if (Stores.setting.icloudSync.fetch()) ICloud.syncDb();
|
|
||||||
|
|
||||||
if (isAndroid) {
|
if (isAndroid) {
|
||||||
// Only start service when [bgRun] is true.
|
// Only start service when [bgRun] is true.
|
||||||
if (Stores.setting.bgRun.fetch()) {
|
if (Stores.setting.bgRun.fetch()) {
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import 'package:toolbox/core/extension/context/common.dart';
|
|||||||
import 'package:toolbox/core/extension/context/dialog.dart';
|
import 'package:toolbox/core/extension/context/dialog.dart';
|
||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
import 'package:toolbox/core/extension/context/locale.dart';
|
||||||
import 'package:toolbox/core/extension/context/snackbar.dart';
|
import 'package:toolbox/core/extension/context/snackbar.dart';
|
||||||
|
import 'package:toolbox/core/persistant_store.dart';
|
||||||
|
import 'package:toolbox/core/utils/icloud.dart';
|
||||||
import 'package:toolbox/core/utils/platform/base.dart';
|
import 'package:toolbox/core/utils/platform/base.dart';
|
||||||
import 'package:toolbox/core/utils/rebuild.dart';
|
import 'package:toolbox/core/utils/rebuild.dart';
|
||||||
import 'package:toolbox/data/model/app/backup.dart';
|
import 'package:toolbox/data/model/app/backup.dart';
|
||||||
@@ -14,15 +16,19 @@ import 'package:toolbox/data/res/logger.dart';
|
|||||||
import 'package:toolbox/data/res/path.dart';
|
import 'package:toolbox/data/res/path.dart';
|
||||||
import 'package:toolbox/data/res/provider.dart';
|
import 'package:toolbox/data/res/provider.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:toolbox/data/res/store.dart';
|
||||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
import 'package:toolbox/view/widget/expand_tile.dart';
|
||||||
|
import 'package:toolbox/view/widget/cardx.dart';
|
||||||
|
import 'package:toolbox/view/widget/store_switch.dart';
|
||||||
|
import 'package:toolbox/view/widget/value_notifier.dart';
|
||||||
|
|
||||||
import '../../core/utils/misc.dart';
|
import '../../core/utils/misc.dart';
|
||||||
import '../../data/res/ui.dart';
|
import '../../data/res/ui.dart';
|
||||||
import '../widget/custom_appbar.dart';
|
import '../widget/custom_appbar.dart';
|
||||||
import '../widget/store_switch.dart';
|
|
||||||
|
|
||||||
class BackupPage extends StatelessWidget {
|
class BackupPage extends StatelessWidget {
|
||||||
const BackupPage({Key? key}) : super(key: key);
|
BackupPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
final icloudLoading = ValueNotifier(false);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -35,66 +41,38 @@ class BackupPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBody(BuildContext context) {
|
Widget _buildBody(BuildContext context) {
|
||||||
final tip = () {
|
return ListView(
|
||||||
if (isMacOS || isIOS) {
|
padding: const EdgeInsets.all(17),
|
||||||
return '${l10n.syncTip}\n${l10n.backupTip}';
|
|
||||||
}
|
|
||||||
return l10n.backupTip;
|
|
||||||
}();
|
|
||||||
return Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
children: [
|
||||||
if (isMacOS || isIOS) _buildIcloudSync(context),
|
if (isMacOS || isIOS) _buildIcloudSync(context),
|
||||||
UIs.height13,
|
_buildManual(context),
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(37),
|
|
||||||
child: Text(
|
|
||||||
tip,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
UIs.height77,
|
|
||||||
_buildCard(
|
|
||||||
l10n.restore,
|
|
||||||
Icons.download,
|
|
||||||
() => _onRestore(context),
|
|
||||||
),
|
|
||||||
UIs.height13,
|
|
||||||
const SizedBox(
|
|
||||||
width: 37,
|
|
||||||
child: Divider(),
|
|
||||||
),
|
|
||||||
UIs.height13,
|
|
||||||
_buildCard(
|
|
||||||
l10n.backup,
|
|
||||||
Icons.save,
|
|
||||||
() async {
|
|
||||||
await Backup.backup();
|
|
||||||
await shareFiles([await Paths.bak]);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCard(
|
Widget _buildManual(BuildContext context) {
|
||||||
String text,
|
return CardX(
|
||||||
IconData icon,
|
ListTile(
|
||||||
FutureOr Function() onTap,
|
title: Text(l10n.files),
|
||||||
) {
|
subtitle: Text(
|
||||||
return RoundRectCard(
|
l10n.backupTip,
|
||||||
InkWell(
|
style: UIs.textGrey,
|
||||||
onTap: onTap,
|
),
|
||||||
child: Padding(
|
trailing: SizedBox(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 7, horizontal: 17),
|
width: 120,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
children: [
|
||||||
Icon(icon, size: 20),
|
TextButton(
|
||||||
UIs.width7,
|
onPressed: () => _onRestore(context),
|
||||||
Text(text),
|
child: Text(l10n.restore),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await Backup.backup();
|
||||||
|
await shareFiles([await Paths.bak]);
|
||||||
|
},
|
||||||
|
child: Text(l10n.backup),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -103,27 +81,72 @@ class BackupPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildIcloudSync(BuildContext context) {
|
Widget _buildIcloudSync(BuildContext context) {
|
||||||
return Row(
|
return CardX(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
ExpandTile(
|
||||||
children: [
|
title: const Text('iCloud'),
|
||||||
const Text(
|
initiallyExpanded: true,
|
||||||
'iCloud',
|
subtitle: Text(
|
||||||
textAlign: TextAlign.center,
|
l10n.syncTip,
|
||||||
|
style: UIs.textGrey,
|
||||||
),
|
),
|
||||||
UIs.width13,
|
children: [
|
||||||
// Hive db only save data into local file after app exit,
|
ListTile(
|
||||||
// so this button is useless
|
title: Text(l10n.auto),
|
||||||
// IconButton(
|
subtitle: const Text(
|
||||||
// onPressed: () async {
|
'Please wait for optimization :)',
|
||||||
// showLoadingDialog(context);
|
style: UIs.textGrey,
|
||||||
// await ICloud.syncDb();
|
),
|
||||||
// context.pop();
|
trailing: StoreSwitch(
|
||||||
// showRestartSnackbar(context, btn: s.restart, msg: s.icloudSynced);
|
prop: Stores.setting.icloudSync,
|
||||||
// },
|
func: (val) async {
|
||||||
// icon: const Icon(Icons.sync)),
|
if (val) {
|
||||||
// width13,
|
final relativePaths = await PersistentStore.getFileNames();
|
||||||
StoreSwitch(prop: Stores.setting.icloudSync)
|
await ICloud.sync(relativePaths: relativePaths);
|
||||||
],
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text('Manual'),
|
||||||
|
trailing: ValueBuilder(
|
||||||
|
listenable: icloudLoading,
|
||||||
|
build: () {
|
||||||
|
if (icloudLoading.value) {
|
||||||
|
return UIs.centerSizedLoading;
|
||||||
|
}
|
||||||
|
return SizedBox(
|
||||||
|
width: 120,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
icloudLoading.value = true;
|
||||||
|
final files = await PersistentStore.getFileNames();
|
||||||
|
for (final file in files) {
|
||||||
|
await ICloud.download(relativePath: file);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(l10n.download),
|
||||||
|
),
|
||||||
|
UIs.width7,
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
icloudLoading.value = true;
|
||||||
|
final files = await PersistentStore.getFileNames();
|
||||||
|
for (final file in files) {
|
||||||
|
await ICloud.upload(relativePath: file);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(l10n.upload),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import '../../data/res/ui.dart';
|
|||||||
import '../../data/res/url.dart';
|
import '../../data/res/url.dart';
|
||||||
import '../widget/custom_appbar.dart';
|
import '../widget/custom_appbar.dart';
|
||||||
import '../widget/popup_menu.dart';
|
import '../widget/popup_menu.dart';
|
||||||
import '../widget/round_rect_card.dart';
|
import '../widget/cardx.dart';
|
||||||
import '../widget/two_line_text.dart';
|
import '../widget/two_line_text.dart';
|
||||||
import '../widget/url_text.dart';
|
import '../widget/url_text.dart';
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
|||||||
_buildPs(),
|
_buildPs(),
|
||||||
_buildImage(),
|
_buildImage(),
|
||||||
_buildEditHost(),
|
_buildEditHost(),
|
||||||
].map((e) => RoundRectCard(e));
|
].map((e) => CardX(e));
|
||||||
return ListView(
|
return ListView(
|
||||||
padding: const EdgeInsets.all(7),
|
padding: const EdgeInsets.all(7),
|
||||||
children: items.toList(),
|
children: items.toList(),
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import '../../data/res/misc.dart';
|
|||||||
import '../../data/res/ui.dart';
|
import '../../data/res/ui.dart';
|
||||||
import '../../data/res/url.dart';
|
import '../../data/res/url.dart';
|
||||||
import '../widget/custom_appbar.dart';
|
import '../widget/custom_appbar.dart';
|
||||||
import '../widget/round_rect_card.dart';
|
import '../widget/cardx.dart';
|
||||||
import '../widget/url_text.dart';
|
import '../widget/url_text.dart';
|
||||||
import '../widget/value_notifier.dart';
|
import '../widget/value_notifier.dart';
|
||||||
|
|
||||||
@@ -256,7 +256,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
title: Text('${l10n.about} & ${l10n.feedback}'),
|
title: Text('${l10n.about} & ${l10n.feedback}'),
|
||||||
onTap: _showAboutDialog,
|
onTap: _showAboutDialog,
|
||||||
)
|
)
|
||||||
].map((e) => RoundRectCard(e)).toList(),
|
].map((e) => CardX(e)).toList(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import '../../data/model/server/ping_result.dart';
|
|||||||
import '../../data/res/color.dart';
|
import '../../data/res/color.dart';
|
||||||
import '../../data/res/ui.dart';
|
import '../../data/res/ui.dart';
|
||||||
import '../widget/input_field.dart';
|
import '../widget/input_field.dart';
|
||||||
import '../widget/round_rect_card.dart';
|
import '../widget/cardx.dart';
|
||||||
|
|
||||||
/// Only permit ipv4 / ipv6 / domain chars
|
/// Only permit ipv4 / ipv6 / domain chars
|
||||||
final targetReg = RegExp(r'[a-zA-Z0-9\.-_:]+');
|
final targetReg = RegExp(r'[a-zA-Z0-9\.-_:]+');
|
||||||
@@ -103,7 +103,7 @@ class _PingPageState extends State<PingPage>
|
|||||||
return Center(
|
return Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
l10n.noResult,
|
l10n.noResult,
|
||||||
style: const TextStyle(fontSize: 18),
|
style: const TextStyle(fontSize: 15),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -118,7 +118,7 @@ class _PingPageState extends State<PingPage>
|
|||||||
Widget _buildResultItem(PingResult result) {
|
Widget _buildResultItem(PingResult result) {
|
||||||
final unknown = l10n.unknown;
|
final unknown = l10n.unknown;
|
||||||
final ms = l10n.ms;
|
final ms = l10n.ms;
|
||||||
return RoundRectCard(
|
return CardX(
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding: const EdgeInsets.symmetric(vertical: 7, horizontal: 17),
|
contentPadding: const EdgeInsets.symmetric(vertical: 7, horizontal: 17),
|
||||||
title: Text(
|
title: Text(
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import '../../../data/model/server/private_key_info.dart';
|
|||||||
import '../../../data/provider/private_key.dart';
|
import '../../../data/provider/private_key.dart';
|
||||||
import '../../../data/res/ui.dart';
|
import '../../../data/res/ui.dart';
|
||||||
import '../../widget/custom_appbar.dart';
|
import '../../widget/custom_appbar.dart';
|
||||||
import '../../../view/widget/round_rect_card.dart';
|
import '../../widget/cardx.dart';
|
||||||
|
|
||||||
class PrivateKeysListPage extends StatefulWidget {
|
class PrivateKeysListPage extends StatefulWidget {
|
||||||
const PrivateKeysListPage({Key? key}) : super(key: key);
|
const PrivateKeysListPage({Key? key}) : super(key: key);
|
||||||
@@ -53,7 +53,7 @@ class _PrivateKeyListState extends State<PrivateKeysListPage>
|
|||||||
itemCount: key.pkis.length,
|
itemCount: key.pkis.length,
|
||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
final item = key.pkis[idx];
|
final item = key.pkis[idx];
|
||||||
return RoundRectCard(
|
return CardX(
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Text(
|
leading: Text(
|
||||||
'#$idx',
|
'#$idx',
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import '../../data/model/server/proc.dart';
|
|||||||
import '../../data/model/server/server_private_info.dart';
|
import '../../data/model/server/server_private_info.dart';
|
||||||
import '../../data/res/ui.dart';
|
import '../../data/res/ui.dart';
|
||||||
import '../widget/custom_appbar.dart';
|
import '../widget/custom_appbar.dart';
|
||||||
import '../widget/round_rect_card.dart';
|
import '../widget/cardx.dart';
|
||||||
import '../widget/two_line_text.dart';
|
import '../widget/two_line_text.dart';
|
||||||
|
|
||||||
class ProcessPage extends StatefulWidget {
|
class ProcessPage extends StatefulWidget {
|
||||||
@@ -142,7 +142,7 @@ class _ProcessPageState extends State<ProcessPage> {
|
|||||||
final leading = proc.user == null
|
final leading = proc.user == null
|
||||||
? Text(proc.pid.toString())
|
? Text(proc.pid.toString())
|
||||||
: TwoLineText(up: proc.pid.toString(), down: proc.user!);
|
: TwoLineText(up: proc.pid.toString(), down: proc.user!);
|
||||||
return RoundRectCard(
|
return CardX(
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: SizedBox(
|
leading: SizedBox(
|
||||||
width: _media.size.width / 6,
|
width: _media.size.width / 6,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import '../../../data/res/color.dart';
|
|||||||
import '../../../data/res/default.dart';
|
import '../../../data/res/default.dart';
|
||||||
import '../../../data/res/ui.dart';
|
import '../../../data/res/ui.dart';
|
||||||
import '../../widget/custom_appbar.dart';
|
import '../../widget/custom_appbar.dart';
|
||||||
import '../../widget/round_rect_card.dart';
|
import '../../widget/cardx.dart';
|
||||||
|
|
||||||
class ServerDetailPage extends StatefulWidget {
|
class ServerDetailPage extends StatefulWidget {
|
||||||
const ServerDetailPage({Key? key, required this.spi}) : super(key: key);
|
const ServerDetailPage({Key? key, required this.spi}) : super(key: key);
|
||||||
@@ -131,7 +131,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return RoundRectCard(
|
return CardX(
|
||||||
Padding(
|
Padding(
|
||||||
padding: UIs.roundRectCardPadding,
|
padding: UIs.roundRectCardPadding,
|
||||||
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
|
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||||
@@ -201,7 +201,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildUpTimeAndSys(ServerStatus ss) {
|
Widget _buildUpTimeAndSys(ServerStatus ss) {
|
||||||
return RoundRectCard(
|
return CardX(
|
||||||
Padding(
|
Padding(
|
||||||
padding: UIs.roundRectCardPadding,
|
padding: UIs.roundRectCardPadding,
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -229,7 +229,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
final used = ss.mem.usedPercent * 100;
|
final used = ss.mem.usedPercent * 100;
|
||||||
final usedStr = used.toStringAsFixed(0);
|
final usedStr = used.toStringAsFixed(0);
|
||||||
|
|
||||||
return RoundRectCard(
|
return CardX(
|
||||||
Padding(
|
Padding(
|
||||||
padding: UIs.roundRectCardPadding,
|
padding: UIs.roundRectCardPadding,
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -274,7 +274,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
if (ss.swap.total == 0) return UIs.placeholder;
|
if (ss.swap.total == 0) return UIs.placeholder;
|
||||||
final used = ss.swap.usedPercent * 100;
|
final used = ss.swap.usedPercent * 100;
|
||||||
final cached = ss.swap.cached / ss.swap.total * 100;
|
final cached = ss.swap.cached / ss.swap.total * 100;
|
||||||
return RoundRectCard(
|
return CardX(
|
||||||
Padding(
|
Padding(
|
||||||
padding: UIs.roundRectCardPadding,
|
padding: UIs.roundRectCardPadding,
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -339,7 +339,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
),
|
),
|
||||||
))
|
))
|
||||||
.toList();
|
.toList();
|
||||||
return RoundRectCard(
|
return CardX(
|
||||||
Padding(
|
Padding(
|
||||||
padding: UIs.roundRectCardPadding,
|
padding: UIs.roundRectCardPadding,
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -351,7 +351,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildNetView(ServerStatus ss) {
|
Widget _buildNetView(ServerStatus ss) {
|
||||||
return RoundRectCard(
|
return CardX(
|
||||||
Padding(
|
Padding(
|
||||||
padding: UIs.roundRectCardPadding,
|
padding: UIs.roundRectCardPadding,
|
||||||
child: ValueBuilder(
|
child: ValueBuilder(
|
||||||
@@ -503,7 +503,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
)));
|
)));
|
||||||
return RoundRectCard(
|
return CardX(
|
||||||
Padding(
|
Padding(
|
||||||
padding: UIs.roundRectCardPadding,
|
padding: UIs.roundRectCardPadding,
|
||||||
child: Column(children: children),
|
child: Column(children: children),
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import '../../../data/provider/private_key.dart';
|
|||||||
import '../../../data/res/ui.dart';
|
import '../../../data/res/ui.dart';
|
||||||
import '../../widget/custom_appbar.dart';
|
import '../../widget/custom_appbar.dart';
|
||||||
import '../../widget/input_field.dart';
|
import '../../widget/input_field.dart';
|
||||||
import '../../widget/round_rect_card.dart';
|
import '../../widget/cardx.dart';
|
||||||
import '../../widget/tag.dart';
|
import '../../widget/tag.dart';
|
||||||
import '../../widget/value_notifier.dart';
|
import '../../widget/value_notifier.dart';
|
||||||
|
|
||||||
@@ -283,7 +283,7 @@ class _ServerEditPageState extends State<ServerEditPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return RoundRectCard(
|
return CardX(
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 17),
|
padding: const EdgeInsets.symmetric(horizontal: 17),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import '../../../data/model/server/server_status.dart';
|
|||||||
import '../../../data/provider/server.dart';
|
import '../../../data/provider/server.dart';
|
||||||
import '../../../data/res/color.dart';
|
import '../../../data/res/color.dart';
|
||||||
import '../../../data/res/ui.dart';
|
import '../../../data/res/ui.dart';
|
||||||
import '../../widget/round_rect_card.dart';
|
import '../../widget/cardx.dart';
|
||||||
import '../../widget/server_func_btns.dart';
|
import '../../widget/server_func_btns.dart';
|
||||||
import '../../widget/tag.dart';
|
import '../../widget/tag.dart';
|
||||||
|
|
||||||
@@ -173,7 +173,7 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
return UIs.placeholder;
|
return UIs.placeholder;
|
||||||
}
|
}
|
||||||
|
|
||||||
return RoundRectCard(
|
return CardX(
|
||||||
key: Key(si.spi.id + (_tag ?? '')),
|
key: Key(si.spi.id + (_tag ?? '')),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import 'package:toolbox/data/res/store.dart';
|
|||||||
import 'package:toolbox/view/page/setting/platform_pub.dart';
|
import 'package:toolbox/view/page/setting/platform_pub.dart';
|
||||||
import 'package:toolbox/view/widget/custom_appbar.dart';
|
import 'package:toolbox/view/widget/custom_appbar.dart';
|
||||||
import 'package:toolbox/view/widget/input_field.dart';
|
import 'package:toolbox/view/widget/input_field.dart';
|
||||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
import 'package:toolbox/view/widget/cardx.dart';
|
||||||
import 'package:toolbox/view/widget/store_switch.dart';
|
import 'package:toolbox/view/widget/store_switch.dart';
|
||||||
|
|
||||||
class AndroidSettingsPage extends StatefulWidget {
|
class AndroidSettingsPage extends StatefulWidget {
|
||||||
@@ -43,7 +43,7 @@ class _AndroidSettingsPageState extends State<AndroidSettingsPage> {
|
|||||||
_buildAndroidWidgetSharedPreference(),
|
_buildAndroidWidgetSharedPreference(),
|
||||||
if (BioAuth.isPlatformSupported)
|
if (BioAuth.isPlatformSupported)
|
||||||
PlatformPublicSettings.buildBioAuth(),
|
PlatformPublicSettings.buildBioAuth(),
|
||||||
].map((e) => RoundRectCard(e)).toList(),
|
].map((e) => CardX(e)).toList(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import '../../../data/res/ui.dart';
|
|||||||
import '../../widget/color_picker.dart';
|
import '../../widget/color_picker.dart';
|
||||||
import '../../widget/custom_appbar.dart';
|
import '../../widget/custom_appbar.dart';
|
||||||
import '../../widget/input_field.dart';
|
import '../../widget/input_field.dart';
|
||||||
import '../../widget/round_rect_card.dart';
|
import '../../widget/cardx.dart';
|
||||||
import '../../widget/store_switch.dart';
|
import '../../widget/store_switch.dart';
|
||||||
import '../../widget/value_notifier.dart';
|
import '../../widget/value_notifier.dart';
|
||||||
|
|
||||||
@@ -194,7 +194,7 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
children.add(_buildPlatformSetting());
|
children.add(_buildPlatformSetting());
|
||||||
}
|
}
|
||||||
return Column(
|
return Column(
|
||||||
children: children.map((e) => RoundRectCard(e)).toList(),
|
children: children.map((e) => CardX(e)).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +204,7 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
_buildFullScreenSwitch(),
|
_buildFullScreenSwitch(),
|
||||||
_buildFullScreenJitter(),
|
_buildFullScreenJitter(),
|
||||||
_buildFulScreenRotateQuarter(),
|
_buildFulScreenRotateQuarter(),
|
||||||
].map((e) => RoundRectCard(e)).toList(),
|
].map((e) => CardX(e)).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +219,7 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
//_buildDiskIgnorePath(),
|
//_buildDiskIgnorePath(),
|
||||||
_buildDeleteServers(),
|
_buildDeleteServers(),
|
||||||
//if (isDesktop) _buildDoubleColumnServersPage(),
|
//if (isDesktop) _buildDoubleColumnServersPage(),
|
||||||
].map((e) => RoundRectCard(e)).toList(),
|
].map((e) => CardX(e)).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +232,7 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
// Use hardware keyboard on desktop, so there is no need to set it
|
// Use hardware keyboard on desktop, so there is no need to set it
|
||||||
if (isMobile) _buildKeyboardType(),
|
if (isMobile) _buildKeyboardType(),
|
||||||
_buildSSHVirtKeys(),
|
_buildSSHVirtKeys(),
|
||||||
].map((e) => RoundRectCard(e)).toList(),
|
].map((e) => CardX(e)).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +243,7 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
_buildEditorTheme(),
|
_buildEditorTheme(),
|
||||||
_buildEditorDarkTheme(),
|
_buildEditorDarkTheme(),
|
||||||
_buildEditorHighlight(),
|
_buildEditorHighlight(),
|
||||||
].map((e) => RoundRectCard(e)).toList(),
|
].map((e) => CardX(e)).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -848,7 +848,7 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
children: [
|
children: [
|
||||||
_buildSftpRmrDir(),
|
_buildSftpRmrDir(),
|
||||||
_buildSftpOpenLastPath(),
|
_buildSftpOpenLastPath(),
|
||||||
].map((e) => RoundRectCard(e)).toList(),
|
].map((e) => CardX(e)).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import 'package:toolbox/data/res/ui.dart';
|
|||||||
import 'package:toolbox/view/page/setting/platform_pub.dart';
|
import 'package:toolbox/view/page/setting/platform_pub.dart';
|
||||||
import 'package:toolbox/view/widget/custom_appbar.dart';
|
import 'package:toolbox/view/widget/custom_appbar.dart';
|
||||||
import 'package:toolbox/view/widget/future_widget.dart';
|
import 'package:toolbox/view/widget/future_widget.dart';
|
||||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
import 'package:toolbox/view/widget/cardx.dart';
|
||||||
import 'package:toolbox/view/widget/store_switch.dart';
|
import 'package:toolbox/view/widget/store_switch.dart';
|
||||||
import 'package:watch_connectivity/watch_connectivity.dart';
|
import 'package:watch_connectivity/watch_connectivity.dart';
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ class _IOSSettingsPageState extends State<IOSSettingsPage> {
|
|||||||
_buildWatchApp(),
|
_buildWatchApp(),
|
||||||
if (BioAuth.isPlatformSupported)
|
if (BioAuth.isPlatformSupported)
|
||||||
PlatformPublicSettings.buildBioAuth(),
|
PlatformPublicSettings.buildBioAuth(),
|
||||||
].map((e) => RoundRectCard(e)).toList(),
|
].map((e) => CardX(e)).toList(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import 'package:toolbox/data/res/store.dart';
|
|||||||
|
|
||||||
import '../../../core/extension/order.dart';
|
import '../../../core/extension/order.dart';
|
||||||
import '../../widget/custom_appbar.dart';
|
import '../../widget/custom_appbar.dart';
|
||||||
import '../../widget/round_rect_card.dart';
|
import '../../widget/cardx.dart';
|
||||||
|
|
||||||
class ServerDetailOrderPage extends StatefulWidget {
|
class ServerDetailOrderPage extends StatefulWidget {
|
||||||
const ServerDetailOrderPage({super.key});
|
const ServerDetailOrderPage({super.key});
|
||||||
@@ -53,7 +53,7 @@ class _ServerDetailOrderPageState extends State<ServerDetailOrderPage> {
|
|||||||
return ReorderableDelayedDragStartListener(
|
return ReorderableDelayedDragStartListener(
|
||||||
key: ValueKey('$index'),
|
key: ValueKey('$index'),
|
||||||
index: index,
|
index: index,
|
||||||
child: RoundRectCard(ListTile(
|
child: CardX(ListTile(
|
||||||
title: Text(id),
|
title: Text(id),
|
||||||
trailing: const Icon(Icons.drag_handle),
|
trailing: const Icon(Icons.drag_handle),
|
||||||
)),
|
)),
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import 'package:toolbox/core/extension/context/locale.dart';
|
|||||||
import 'package:toolbox/core/extension/order.dart';
|
import 'package:toolbox/core/extension/order.dart';
|
||||||
import 'package:toolbox/data/res/provider.dart';
|
import 'package:toolbox/data/res/provider.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:toolbox/data/res/store.dart';
|
||||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
import 'package:toolbox/view/widget/cardx.dart';
|
||||||
|
|
||||||
import '../../widget/custom_appbar.dart';
|
import '../../widget/custom_appbar.dart';
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ class _ServerOrderPageState extends State<ServerOrderPage> {
|
|||||||
return ReorderableDelayedDragStartListener(
|
return ReorderableDelayedDragStartListener(
|
||||||
key: ValueKey('$index'),
|
key: ValueKey('$index'),
|
||||||
index: index,
|
index: index,
|
||||||
child: RoundRectCard(ListTile(
|
child: CardX(ListTile(
|
||||||
title: Text(spi.name),
|
title: Text(spi.name),
|
||||||
subtitle: Text(spi.id),
|
subtitle: Text(spi.id),
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import 'package:toolbox/core/utils/platform/base.dart';
|
|||||||
import 'package:toolbox/data/model/ssh/virtual_key.dart';
|
import 'package:toolbox/data/model/ssh/virtual_key.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:toolbox/data/res/store.dart';
|
||||||
import 'package:toolbox/data/res/ui.dart';
|
import 'package:toolbox/data/res/ui.dart';
|
||||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
import 'package:toolbox/view/widget/cardx.dart';
|
||||||
|
|
||||||
import '../../widget/custom_appbar.dart';
|
import '../../widget/custom_appbar.dart';
|
||||||
|
|
||||||
@@ -41,7 +41,7 @@ class _SSHVirtKeySettingPageState extends State<SSHVirtKeySettingPage> {
|
|||||||
itemBuilder: (_, idx) {
|
itemBuilder: (_, idx) {
|
||||||
final key = allKeys[idx];
|
final key = allKeys[idx];
|
||||||
final help = key.help;
|
final help = key.help;
|
||||||
return RoundRectCard(
|
return CardX(
|
||||||
key: ValueKey(idx),
|
key: ValueKey(idx),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: _buildTitle(key),
|
title: _buildTitle(key),
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import '../../../data/res/ui.dart';
|
|||||||
import '../../widget/tag.dart';
|
import '../../widget/tag.dart';
|
||||||
import '/core/route.dart';
|
import '/core/route.dart';
|
||||||
import '/data/provider/snippet.dart';
|
import '/data/provider/snippet.dart';
|
||||||
import '/view/widget/round_rect_card.dart';
|
import '../../widget/cardx.dart';
|
||||||
|
|
||||||
class SnippetListPage extends StatefulWidget {
|
class SnippetListPage extends StatefulWidget {
|
||||||
const SnippetListPage({Key? key}) : super(key: key);
|
const SnippetListPage({Key? key}) : super(key: key);
|
||||||
@@ -94,7 +94,7 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSnippetItem(Snippet snippet) {
|
Widget _buildSnippetItem(Snippet snippet) {
|
||||||
return RoundRectCard(
|
return CardX(
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding: const EdgeInsets.only(left: 23, right: 17),
|
contentPadding: const EdgeInsets.only(left: 23, right: 17),
|
||||||
title: Text(
|
title: Text(
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import 'package:toolbox/data/res/misc.dart';
|
|||||||
import 'package:toolbox/data/res/provider.dart';
|
import 'package:toolbox/data/res/provider.dart';
|
||||||
import 'package:toolbox/view/widget/input_field.dart';
|
import 'package:toolbox/view/widget/input_field.dart';
|
||||||
import 'package:toolbox/view/widget/omit_start_text.dart';
|
import 'package:toolbox/view/widget/omit_start_text.dart';
|
||||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
import 'package:toolbox/view/widget/cardx.dart';
|
||||||
|
|
||||||
import '../../../core/extension/numx.dart';
|
import '../../../core/extension/numx.dart';
|
||||||
import '../../../core/route.dart';
|
import '../../../core/route.dart';
|
||||||
@@ -153,7 +153,7 @@ class _LocalStoragePageState extends State<LocalStoragePage> {
|
|||||||
var stat = file.statSync();
|
var stat = file.statSync();
|
||||||
var isDir = stat.type == FileSystemEntityType.directory;
|
var isDir = stat.type == FileSystemEntityType.directory;
|
||||||
|
|
||||||
return RoundRectCard(ListTile(
|
return CardX(ListTile(
|
||||||
leading: isDir
|
leading: isDir
|
||||||
? const Icon(Icons.folder)
|
? const Icon(Icons.folder)
|
||||||
: const Icon(Icons.insert_drive_file),
|
: const Icon(Icons.insert_drive_file),
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import 'package:toolbox/data/res/misc.dart';
|
|||||||
import 'package:toolbox/data/res/provider.dart';
|
import 'package:toolbox/data/res/provider.dart';
|
||||||
import 'package:toolbox/data/res/store.dart';
|
import 'package:toolbox/data/res/store.dart';
|
||||||
import 'package:toolbox/view/widget/omit_start_text.dart';
|
import 'package:toolbox/view/widget/omit_start_text.dart';
|
||||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
import 'package:toolbox/view/widget/cardx.dart';
|
||||||
|
|
||||||
import '../../../core/extension/numx.dart';
|
import '../../../core/extension/numx.dart';
|
||||||
import '../../../core/route.dart';
|
import '../../../core/route.dart';
|
||||||
@@ -277,7 +277,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
|||||||
style: UIs.textGrey,
|
style: UIs.textGrey,
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
);
|
);
|
||||||
return RoundRectCard(ListTile(
|
return CardX(ListTile(
|
||||||
leading: Icon(isDir ? Icons.folder : Icons.insert_drive_file),
|
leading: Icon(isDir ? Icons.folder : Icons.insert_drive_file),
|
||||||
title: Text(file.filename),
|
title: Text(file.filename),
|
||||||
trailing: trailing,
|
trailing: trailing,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import '../../../data/model/sftp/req.dart';
|
|||||||
import '../../../data/provider/sftp.dart';
|
import '../../../data/provider/sftp.dart';
|
||||||
import '../../../data/res/ui.dart';
|
import '../../../data/res/ui.dart';
|
||||||
import '../../widget/custom_appbar.dart';
|
import '../../widget/custom_appbar.dart';
|
||||||
import '../../widget/round_rect_card.dart';
|
import '../../widget/cardx.dart';
|
||||||
|
|
||||||
class SftpMissionPage extends StatefulWidget {
|
class SftpMissionPage extends StatefulWidget {
|
||||||
const SftpMissionPage({Key? key}) : super(key: key);
|
const SftpMissionPage({Key? key}) : super(key: key);
|
||||||
@@ -119,7 +119,7 @@ class _SftpMissionPageState extends State<SftpMissionPage> {
|
|||||||
Widget? trailing,
|
Widget? trailing,
|
||||||
}) {
|
}) {
|
||||||
final time = DateTime.fromMicrosecondsSinceEpoch(status.id);
|
final time = DateTime.fromMicrosecondsSinceEpoch(status.id);
|
||||||
return RoundRectCard(
|
return CardX(
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Text(time.hourMinute),
|
leading: Text(time.hourMinute),
|
||||||
title: Text(
|
title: Text(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class RoundRectCard extends StatelessWidget {
|
class CardX extends StatelessWidget {
|
||||||
const RoundRectCard(this.child, {Key? key, this.color}) : super(key: key);
|
const CardX(this.child, {Key? key, this.color}) : super(key: key);
|
||||||
|
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final Color? color;
|
final Color? color;
|
||||||
@@ -8,5 +8,6 @@ class ExpandTile extends ExpansionTile {
|
|||||||
required super.title,
|
required super.title,
|
||||||
super.children,
|
super.children,
|
||||||
super.subtitle,
|
super.subtitle,
|
||||||
|
super.initiallyExpanded,
|
||||||
}) : super(shape: _shape, collapsedShape: _shape);
|
}) : super(shape: _shape, collapsedShape: _shape);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'round_rect_card.dart';
|
import 'cardx.dart';
|
||||||
|
|
||||||
class Input extends StatelessWidget {
|
class Input extends StatelessWidget {
|
||||||
final TextEditingController? controller;
|
final TextEditingController? controller;
|
||||||
@@ -41,7 +41,7 @@ class Input extends StatelessWidget {
|
|||||||
});
|
});
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return RoundRectCard(
|
return CardX(
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 17),
|
padding: const EdgeInsets.symmetric(horizontal: 17),
|
||||||
child: TextField(
|
child: TextField(
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import 'package:toolbox/core/extension/context/dialog.dart';
|
|||||||
import 'package:toolbox/core/extension/context/locale.dart';
|
import 'package:toolbox/core/extension/context/locale.dart';
|
||||||
import 'package:toolbox/data/res/ui.dart';
|
import 'package:toolbox/data/res/ui.dart';
|
||||||
import 'package:toolbox/view/widget/input_field.dart';
|
import 'package:toolbox/view/widget/input_field.dart';
|
||||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
import 'package:toolbox/view/widget/cardx.dart';
|
||||||
|
|
||||||
import '../../data/res/color.dart';
|
import '../../data/res/color.dart';
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ class TagEditor extends StatefulWidget {
|
|||||||
class _TagEditorState extends State<TagEditor> {
|
class _TagEditorState extends State<TagEditor> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return RoundRectCard(ListTile(
|
return CardX(ListTile(
|
||||||
leading: const Icon(Icons.tag),
|
leading: const Icon(Icons.tag),
|
||||||
title: _buildTags(widget.tags),
|
title: _buildTags(widget.tags),
|
||||||
trailing: InkWell(
|
trailing: InkWell(
|
||||||
|
|||||||
Reference in New Issue
Block a user