opt.: tag switcher

This commit is contained in:
lollipopkit
2023-12-15 12:01:55 +08:00
parent f10c5b9ea8
commit ee18b85108
30 changed files with 316 additions and 281 deletions

View File

@@ -586,7 +586,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 672;
CURRENT_PROJECT_VERSION = 674;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -596,7 +596,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.672;
MARKETING_VERSION = 1.0.674;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -720,7 +720,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 672;
CURRENT_PROJECT_VERSION = 674;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -730,7 +730,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.672;
MARKETING_VERSION = 1.0.674;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -748,7 +748,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 672;
CURRENT_PROJECT_VERSION = 674;
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -758,7 +758,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.672;
MARKETING_VERSION = 1.0.674;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -779,7 +779,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 672;
CURRENT_PROJECT_VERSION = 674;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -792,7 +792,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.672;
MARKETING_VERSION = 1.0.674;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
@@ -818,7 +818,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 672;
CURRENT_PROJECT_VERSION = 674;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -831,7 +831,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.672;
MARKETING_VERSION = 1.0.674;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -854,7 +854,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 672;
CURRENT_PROJECT_VERSION = 674;
DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES;
@@ -867,7 +867,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0.672;
MARKETING_VERSION = 1.0.674;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -890,7 +890,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 672;
CURRENT_PROJECT_VERSION = 674;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -902,7 +902,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.672;
MARKETING_VERSION = 1.0.674;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
@@ -931,7 +931,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 672;
CURRENT_PROJECT_VERSION = 674;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -943,7 +943,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.672;
MARKETING_VERSION = 1.0.674;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox;
@@ -969,7 +969,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 672;
CURRENT_PROJECT_VERSION = 674;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES;
@@ -981,7 +981,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.672;
MARKETING_VERSION = 1.0.674;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox;

View File

@@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'package:toolbox/core/utils/function.dart';
extension WidgetX on Widget {
Widget padding(EdgeInsetsGeometry padding) {
return Padding(padding: padding, child: this);
}
Widget expanded({int flex = 1}) {
return Expanded(flex: flex, child: this);
}
Widget center() {
return Center(child: this);
}
Widget tap({
VoidCallback? onTap,
bool disable = false,
VoidCallback? onLongTap,
VoidCallback? onDoubleTap,
}) {
if (disable) return this;
return InkWell(
onTap: () => Funcs.throttle(onTap),
onLongPress: onLongTap,
onDoubleTap: onDoubleTap,
child: this,
);
}
}

View File

@@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
abstract final class Funcs {
static const int _defaultDurationTime = 377;
static const String _defaultThrottleId = 'default';
static final Map<String, int> startTimeMap = <String, int>{
_defaultThrottleId: 0
};
static void throttle(
VoidCallback? func, {
String id = _defaultThrottleId,
int duration = _defaultDurationTime,
Function? continueClick,
}) {
final currentTime = DateTime.now().millisecondsSinceEpoch;
if (currentTime - (startTimeMap[id] ?? 0) > duration) {
func?.call();
startTimeMap[id] = DateTime.now().millisecondsSinceEpoch;
} else {
continueClick?.call();
}
}
}

View File

@@ -2,9 +2,9 @@
class BuildData {
static const String name = "ServerBox";
static const int build = 672;
static const String engine = "3.16.2";
static const String buildAt = "2023-12-11 11:40:12";
static const int modifications = 3;
static const int build = 674;
static const String engine = "3.16.3";
static const String buildAt = "2023-12-12 18:14:36";
static const int modifications = 29;
static const int script = 31;
}

View File

@@ -51,10 +51,10 @@ class SettingStore extends PersistentStore {
property('diskIgnorePath', Defaults.diskIgnorePath);
/// Use double column servers page on Desktop
late final doubleColumnServersPage = property(
'doubleColumnServersPage',
isDesktop,
);
// late final doubleColumnServersPage = property(
// 'doubleColumnServersPage',
// isDesktop,
// );
/// Disk view: amount / IO
late final serverTabPreferDiskAmount = property(

View File

@@ -53,7 +53,7 @@ class BackupPage extends StatelessWidget {
Widget _buildTip() {
return CardX(
ListTile(
child: ListTile(
leading: const Icon(Icons.warning),
title: Text(l10n.attention),
subtitle: Text(l10n.backupTip, style: UIs.textGrey),
@@ -63,7 +63,7 @@ class BackupPage extends StatelessWidget {
Widget _buildFile(BuildContext context) {
return CardX(
ExpandTile(
child: ExpandTile(
leading: const Icon(Icons.file_open),
title: Text(l10n.files),
initiallyExpanded: true,
@@ -94,7 +94,8 @@ class BackupPage extends StatelessWidget {
Widget _buildIcloud(BuildContext context) {
return CardX(
ListTile(
child: ListTile(
leading: const Icon(Icons.cloud),
title: const Text('iCloud'),
trailing: StoreSwitch(
prop: Stores.setting.icloudSync,
@@ -119,7 +120,7 @@ class BackupPage extends StatelessWidget {
Widget _buildWebdav(BuildContext context) {
return CardX(
ExpandTile(
child: ExpandTile(
leading: const Icon(Icons.storage),
title: const Text('WebDAV'),
initiallyExpanded: !(isIOS || isMacOS),

View File

@@ -214,7 +214,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
_buildPs(),
_buildImage(),
_buildEditHost(),
].map((e) => CardX(e));
].map((e) => CardX(child: e));
return ListView(
padding: const EdgeInsets.all(7),
children: items.toList(),

View File

@@ -241,7 +241,7 @@ class _HomePageState extends State<HomePage>
title: Text('${l10n.about} & ${l10n.feedback}'),
onTap: _showAboutDialog,
)
].map((e) => CardX(e)).toList(),
].map((e) => CardX(child: e)).toList(),
),
);
}

View File

@@ -119,7 +119,7 @@ class _PingPageState extends State<PingPage>
final unknown = l10n.unknown;
final ms = l10n.ms;
return CardX(
ListTile(
child: ListTile(
contentPadding: const EdgeInsets.symmetric(vertical: 7, horizontal: 17),
title: Text(
result.serverName,

View File

@@ -54,7 +54,7 @@ class _PrivateKeyListState extends State<PrivateKeysListPage>
itemBuilder: (context, idx) {
final item = key.pkis[idx];
return CardX(
ListTile(
child: ListTile(
leading: Text(
'#$idx',
style: const TextStyle(

View File

@@ -143,7 +143,8 @@ class _ProcessPageState extends State<ProcessPage> {
? Text(proc.pid.toString())
: TwoLineText(up: proc.pid.toString(), down: proc.user!);
return CardX(
ListTile(
key: ValueKey(proc.pid),
child: ListTile(
leading: SizedBox(
width: _media.size.width / 6,
child: leading,
@@ -178,7 +179,6 @@ class _ProcessPageState extends State<ProcessPage> {
selected: _lastFocusId == proc.pid,
autofocus: _lastFocusId == proc.pid,
),
key: ValueKey(proc.pid),
);
}

View File

@@ -4,6 +4,7 @@ import 'package:toolbox/core/extension/context/common.dart';
import 'package:toolbox/core/extension/context/dialog.dart';
import 'package:toolbox/core/extension/context/locale.dart';
import 'package:toolbox/core/extension/order.dart';
import 'package:toolbox/core/extension/widget.dart';
import 'package:toolbox/data/model/server/cpu.dart';
import 'package:toolbox/data/model/server/disk.dart';
import 'package:toolbox/data/model/server/net_speed.dart';
@@ -135,7 +136,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
}
return CardX(
ExpandTile(
child: ExpandTile(
title: Align(
alignment: Alignment.centerLeft,
child: _buildAnimatedText(
@@ -202,7 +203,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
Widget _buildUpTimeAndSys(ServerStatus ss) {
return CardX(
Padding(
child: Padding(
padding: UIs.roundRectCardPadding,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -230,7 +231,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
final usedStr = used.toStringAsFixed(0);
return CardX(
Padding(
child: Padding(
padding: UIs.roundRectCardPadding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
@@ -275,7 +276,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
final used = ss.swap.usedPercent * 100;
final cached = ss.swap.cached / ss.swap.total * 100;
return CardX(
Padding(
child: Padding(
padding: UIs.roundRectCardPadding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
@@ -309,7 +310,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
if (ss.nvdia == null) return UIs.placeholder;
final children = ss.nvdia!.map((e) => _buildGpuItem(e)).toList();
return CardX(
ExpandTile(
child: ExpandTile(
title: const Text('GPU'),
leading: const Icon(Icons.memory, size: 17),
initiallyExpanded: children.length <= 3,
@@ -392,7 +393,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
style: UIs.textSize11Grey,
textScaler: _textFactor,
),
trailing: InkWell(
trailing: const Icon(Icons.info_outline, size: 17).tap(
onTap: () {
context.showRoundDialog(
title: SizedBox(
@@ -417,7 +418,6 @@ class _ServerDetailPageState extends State<ServerDetailPage>
],
);
},
child: const Icon(Icons.info_outline, size: 17),
),
);
}
@@ -433,7 +433,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
final children =
List.generate(disks.length, (idx) => _buildDiskItem(disks[idx], ss));
return CardX(
ExpandTile(
child: ExpandTile(
title: Text(l10n.disk),
childrenPadding: const EdgeInsets.only(bottom: 7),
leading: const Icon(Icons.storage, size: 17),
@@ -497,7 +497,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
children.addAll(devices.map((e) => _buildNetSpeedItem(ns, e)));
}
return CardX(
ExpandTile(
child: ExpandTile(
title: Row(
children: [
Text(l10n.net),
@@ -579,10 +579,11 @@ class _ServerDetailPageState extends State<ServerDetailPage>
return UIs.placeholder;
}
return CardX(
ExpandTile(
child: ExpandTile(
title: Text(l10n.temperature),
leading: const Icon(Icons.ac_unit, size: 17),
initiallyExpanded: ss.temps.devices.length <= 7,
childrenPadding: EdgeInsets.zero,
children: ss.temps.devices
.map((key) => _buildTemperatureItem(key, ss.temps.get(key)))
.toList(),

View File

@@ -127,16 +127,15 @@ class _ServerEditPageState extends State<ServerEditPage> {
)),
UIs.height13,
if (widget.spi?.server?.canViewDetails ?? false)
Row(
children: [
Checkbox(
value: delScripts,
onChanged: (_) => setState(
() => delScripts = !delScripts,
),
),
Text(l10n.deleteScripts),
],
CheckboxListTile(
value: delScripts,
onChanged: (_) => setState(
() => delScripts = !delScripts,
),
controlAffinity: ListTileControlAffinity.leading,
subtitle: Text(l10n.deleteScripts),
tileColor: Colors.transparent,
contentPadding: EdgeInsets.zero,
)
],
);
@@ -315,7 +314,7 @@ class _ServerEditPageState extends State<ServerEditPage> {
),
);
return CardX(
Padding(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 17),
child: Column(
children: tiles,
@@ -375,7 +374,7 @@ class _ServerEditPageState extends State<ServerEditPage> {
onTap: () => _jumpServer.value = null,
));
return CardX(
ExpandTile(
child: ExpandTile(
leading: const Icon(Icons.map),
initiallyExpanded: _jumpServer.value != null,
title: Text(l10n.jumpServer),

View File

@@ -8,6 +8,7 @@ import 'package:toolbox/core/extension/context/dialog.dart';
import 'package:toolbox/core/extension/context/locale.dart';
import 'package:toolbox/core/extension/media_queryx.dart';
import 'package:toolbox/core/extension/ssh_client.dart';
import 'package:toolbox/core/extension/widget.dart';
import 'package:toolbox/core/utils/platform/base.dart';
import 'package:toolbox/core/utils/share.dart';
import 'package:toolbox/data/model/app/shell_func.dart';
@@ -55,6 +56,7 @@ class _ServerPageState extends State<ServerPage>
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
appBar: _buildTagsSwitcher(Pros.server),
body: _buildBody(),
floatingActionButton: FloatingActionButton(
onPressed: () => AppRoute.serverEdit().go(context),
@@ -81,10 +83,10 @@ class _ServerPageState extends State<ServerPage>
}
final filtered = _filterServers(pro);
if (_useDoubleColumn &&
Stores.setting.doubleColumnServersPage.fetch()) {
return _buildBodyMedium(pro: pro, filtered: filtered);
}
// if (_useDoubleColumn &&
// Stores.setting.doubleColumnServersPage.fetch()) {
// return _buildBodyMedium(pro: pro, filtered: filtered);
// }
return _buildBodySmall(provider: pro, filtered: filtered);
},
);
@@ -99,7 +101,7 @@ class _ServerPageState extends State<ServerPage>
);
}
Widget _buildTagsSwitcher(ServerProvider provider) {
TagSwitcher _buildTagsSwitcher(ServerProvider provider) {
return TagSwitcher(
tags: provider.tags,
width: _media.size.width,
@@ -115,61 +117,33 @@ class _ServerPageState extends State<ServerPage>
required ServerProvider provider,
required List<String> filtered,
EdgeInsets? padding = const EdgeInsets.fromLTRB(7, 0, 7, 7),
bool buildTags = true,
}) {
final count = buildTags ? filtered.length + 2 : filtered.length + 1;
final count = filtered.length + 1;
return ListView.builder(
padding: padding,
itemCount: count,
itemBuilder: (_, index) {
if (index == 0 && buildTags) return _buildTagsSwitcher(provider);
// Issue #130
if (index == count - 1) return UIs.height77;
if (buildTags) index--;
return _buildEachServerCard(provider.pick(id: filtered[index]));
},
);
}
Widget _buildBodyMedium({
required ServerProvider pro,
required List<String> filtered,
}) {
final left = filtered.where((e) => filtered.indexOf(e) % 2 == 0).toList();
final right = filtered.where((e) => filtered.indexOf(e) % 2 == 1).toList();
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 7),
child: _buildTagsSwitcher(pro),
),
Expanded(
child: Row(
children: [
Expanded(
child: _buildBodySmall(
provider: pro,
filtered: left,
padding: const EdgeInsets.fromLTRB(7, 0, 0, 7),
buildTags: false,
),
),
Expanded(
child: _buildBodySmall(
provider: pro,
filtered: right,
padding: const EdgeInsets.fromLTRB(0, 0, 7, 7),
buildTags: false,
),
),
],
))
],
);
}
// Widget _buildBodyMedium({
// required ServerProvider pro,
// required List<String> filtered,
// }) {
// return GridView.builder(
// gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
// crossAxisCount: 2,
// ),
// itemCount: filtered.length,
// itemBuilder: (context, index) {
// return _buildEachServerCard(pro.pick(id: filtered[index]));
// },
// );
// }
Widget _buildEachServerCard(Server? srv) {
if (srv == null) {
@@ -178,7 +152,7 @@ class _ServerPageState extends State<ServerPage>
return CardX(
key: Key(srv.spi.id + (_tag ?? '')),
InkWell(
child: _buildRealServerCard(srv).padding(const EdgeInsets.all(13)).tap(
onTap: () {
if (srv.canViewDetails) {
AppRoute.serverDetail(spi: srv.spi).go(context);
@@ -186,7 +160,7 @@ class _ServerPageState extends State<ServerPage>
_showFailReason(srv.status);
}
},
onLongPress: () {
onLongTap: () {
if (srv.state == ServerState.finished) {
final id = srv.spi.id;
final cardStatus = getCardNoti(id);
@@ -197,10 +171,6 @@ class _ServerPageState extends State<ServerPage>
AppRoute.serverEdit(spi: srv.spi).go(context);
}
},
child: Padding(
padding: const EdgeInsets.all(13),
child: _buildRealServerCard(srv),
),
),
);
}

View File

@@ -43,7 +43,7 @@ class _AndroidSettingsPageState extends State<AndroidSettingsPage> {
_buildAndroidWidgetSharedPreference(),
if (BioAuth.isPlatformSupported)
PlatformPublicSettings.buildBioAuth(),
].map((e) => CardX(e)).toList(),
].map((e) => CardX(child: e)).toList(),
),
);
}

View File

@@ -12,6 +12,8 @@ import 'package:toolbox/core/extension/context/snackbar.dart';
import 'package:toolbox/core/extension/locale.dart';
import 'package:toolbox/core/extension/context/dialog.dart';
import 'package:toolbox/core/extension/stringx.dart';
import 'package:toolbox/core/extension/widget.dart';
import 'package:toolbox/core/utils/function.dart';
import 'package:toolbox/core/utils/platform/base.dart';
import 'package:toolbox/data/res/provider.dart';
import 'package:toolbox/data/res/rebuild.dart';
@@ -100,55 +102,53 @@ class _SettingPageState extends State<SettingPage> {
appBar: CustomAppBar(
title: Text(l10n.setting),
actions: [
Padding(
padding: const EdgeInsets.only(right: 17),
child: InkWell(
onTap: () => context.showRoundDialog(
title: Text(l10n.attention),
child: Text(l10n.askContinue(
'${l10n.delete}: **${l10n.all}** ${l10n.setting}',
)),
actions: [
TextButton(
onPressed: () {
_setting.box.deleteAll(_setting.box.keys);
context.pop();
context.showSnackBar(l10n.success);
},
child: Text(
l10n.ok,
style: const TextStyle(color: Colors.red),
const Icon(Icons.delete)
.tap(
onTap: () => context.showRoundDialog(
title: Text(l10n.attention),
child: Text(l10n.askContinue(
'${l10n.delete}: **${l10n.all}** ${l10n.setting}',
)),
actions: [
TextButton(
onPressed: () {
_setting.box.deleteAll(_setting.box.keys);
context.pop();
context.showSnackBar(l10n.success);
},
child: Text(
l10n.ok,
style: const TextStyle(color: Colors.red),
),
),
),
],
),
],
),
/// Only for debug, this will cause the app to crash
onDoubleTap: () => context.showRoundDialog(
title: Text(l10n.attention),
child: Text(l10n.askContinue(
'Delete all data from disk, and exit the app?',
)),
actions: [
TextButton(
onPressed: () {
if (!BuildMode.isDebug) return;
Stores.docker.box.deleteFromDisk();
Stores.server.box.deleteFromDisk();
Stores.setting.box.deleteFromDisk();
Stores.history.box.deleteFromDisk();
Stores.snippet.box.deleteFromDisk();
Stores.key.box.deleteFromDisk();
exit(0);
},
child: Text(l10n.ok,
style: const TextStyle(color: Colors.red)),
),
],
),
child: const Icon(Icons.delete),
),
),
/// Only for debug, this will cause the app to crash
onDoubleTap: () => context.showRoundDialog(
title: Text(l10n.attention),
child: Text(l10n.askContinue(
'Delete all data from disk, and exit the app?',
)),
actions: [
TextButton(
onPressed: () {
if (!BuildMode.isDebug) return;
Stores.docker.box.deleteFromDisk();
Stores.server.box.deleteFromDisk();
Stores.setting.box.deleteFromDisk();
Stores.history.box.deleteFromDisk();
Stores.snippet.box.deleteFromDisk();
Stores.key.box.deleteFromDisk();
exit(0);
},
child: Text(l10n.ok,
style: const TextStyle(color: Colors.red)),
),
],
),
)
.padding(const EdgeInsets.only(right: 17)),
],
),
body: ListView(
@@ -198,7 +198,7 @@ class _SettingPageState extends State<SettingPage> {
children.add(_buildPlatformSetting());
}
return Column(
children: children.map((e) => CardX(e)).toList(),
children: children.map((e) => CardX(child: e)).toList(),
);
}
@@ -208,7 +208,7 @@ class _SettingPageState extends State<SettingPage> {
_buildFullScreenSwitch(),
_buildFullScreenJitter(),
_buildFulScreenRotateQuarter(),
].map((e) => CardX(e)).toList(),
].map((e) => CardX(child: e)).toList(),
);
}
@@ -223,7 +223,7 @@ class _SettingPageState extends State<SettingPage> {
//_buildDiskIgnorePath(),
_buildDeleteServers(),
//if (isDesktop) _buildDoubleColumnServersPage(),
].map((e) => CardX(e)).toList(),
].map((e) => CardX(child: e)).toList(),
);
}
@@ -236,7 +236,7 @@ class _SettingPageState extends State<SettingPage> {
// Use hardware keyboard on desktop, so there is no need to set it
if (isMobile) _buildKeyboardType(),
_buildSSHVirtKeys(),
].map((e) => CardX(e)).toList(),
].map((e) => CardX(child: e)).toList(),
);
}
@@ -247,7 +247,7 @@ class _SettingPageState extends State<SettingPage> {
_buildEditorTheme(),
_buildEditorDarkTheme(),
_buildEditorHighlight(),
].map((e) => CardX(e)).toList(),
].map((e) => CardX(child: e)).toList(),
);
}
@@ -267,7 +267,7 @@ class _SettingPageState extends State<SettingPage> {
return ListTile(
title: Text(l10n.autoCheckUpdate),
subtitle: Text(display, style: UIs.textGrey),
onTap: () => doUpdate(ctx),
onTap: () => Funcs.throttle(() => doUpdate(ctx)),
trailing: StoreSwitch(prop: _setting.autoCheckAppUpdate),
);
},
@@ -861,7 +861,7 @@ class _SettingPageState extends State<SettingPage> {
children: [
_buildSftpRmrDir(),
_buildSftpOpenLastPath(),
].map((e) => CardX(e)).toList(),
].map((e) => CardX(child: e)).toList(),
);
}

View File

@@ -45,7 +45,7 @@ class _IOSSettingsPageState extends State<IOSSettingsPage> {
_buildWatchApp(),
if (BioAuth.isPlatformSupported)
PlatformPublicSettings.buildBioAuth(),
].map((e) => CardX(e)).toList(),
].map((e) => CardX(child: e)).toList(),
),
);
}

View File

@@ -53,10 +53,12 @@ class _ServerDetailOrderPageState extends State<ServerDetailOrderPage> {
return ReorderableDelayedDragStartListener(
key: ValueKey('$index'),
index: index,
child: CardX(ListTile(
title: Text(id),
trailing: const Icon(Icons.drag_handle),
)),
child: CardX(
child: ListTile(
title: Text(id),
trailing: const Icon(Icons.drag_handle),
),
),
);
}
}

View File

@@ -54,14 +54,16 @@ class _ServerOrderPageState extends State<ServerOrderPage> {
return ReorderableDelayedDragStartListener(
key: ValueKey('$index'),
index: index,
child: CardX(ListTile(
title: Text(spi.name),
subtitle: Text(spi.id, style: UIs.textGrey),
leading: CircleAvatar(
child: Text(spi.name[0]),
child: CardX(
child: ListTile(
title: Text(spi.name),
subtitle: Text(spi.id, style: UIs.textGrey),
leading: CircleAvatar(
child: Text(spi.name[0]),
),
trailing: const Icon(Icons.drag_handle),
),
trailing: const Icon(Icons.drag_handle),
)),
),
);
}
}

View File

@@ -43,7 +43,7 @@ class _SSHVirtKeySettingPageState extends State<SSHVirtKeySettingPage> {
final help = key.help;
return CardX(
key: ValueKey(idx),
ListTile(
child: ListTile(
title: _buildTitle(key),
subtitle: help == null ? null : Text(help, style: UIs.textGrey),
leading: _buildCheckBox(keys, key, idx, idx < keys.length),

View File

@@ -94,7 +94,7 @@ class _SnippetListPageState extends State<SnippetListPage> {
Widget _buildSnippetItem(Snippet snippet) {
return CardX(
ListTile(
child: ListTile(
contentPadding: const EdgeInsets.only(left: 23, right: 17),
title: Text(
snippet.name,

View File

@@ -29,7 +29,7 @@ class SnippetResultPage extends StatelessWidget {
final item = results[index];
if (item == null) return UIs.placeholder;
return CardX(
ExpandTile(
child: ExpandTile(
initiallyExpanded: results.length == 1,
title: Text(item.dest ?? ''),
subtitle: Text(item.time.toString(), style: UIs.textGrey),

View File

@@ -9,6 +9,7 @@ import 'package:toolbox/core/extension/context/common.dart';
import 'package:toolbox/core/extension/context/dialog.dart';
import 'package:toolbox/core/extension/context/locale.dart';
import 'package:toolbox/core/utils/platform/base.dart';
import 'package:toolbox/core/utils/server.dart';
import 'package:toolbox/core/utils/share.dart';
import 'package:toolbox/data/model/server/server.dart';
import 'package:toolbox/data/model/server/snippet.dart';
@@ -58,7 +59,7 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
bool _isDark = false;
Timer? _virtKeyLongPressTimer;
late final Server? _server = widget.spi.server;
late final SSHClient? _client = _server?.client;
late SSHClient? _client = _server?.client;
Timer? _discontinuityTimer;
@override
@@ -329,9 +330,7 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
Future<void> _initTerminal() async {
_writeLn('Connecting...\r\n');
if (_client == null) {
await Pros.server.refreshData(spi: widget.spi);
}
_client ??= await genClient(widget.spi);
_writeLn('Starting shell...\r\n');
final session = await _client?.shell(

View File

@@ -3,6 +3,7 @@ import 'package:provider/provider.dart';
import 'package:toolbox/core/extension/context/common.dart';
import 'package:toolbox/core/extension/context/dialog.dart';
import 'package:toolbox/core/extension/context/locale.dart';
import 'package:toolbox/core/extension/widget.dart';
import 'package:toolbox/data/provider/server.dart';
import 'package:toolbox/data/res/ui.dart';
import 'package:toolbox/view/page/ssh/page.dart';
@@ -49,12 +50,9 @@ class _SSHTabPageState extends State<SSHTabPage>
children: [
Text(e),
UIs.width7,
InkWell(
borderRadius: BorderRadius.circular(17),
child: const Padding(
padding: EdgeInsets.all(7),
child: Icon(Icons.close, size: 17),
),
const Icon(Icons.close, size: 17)
.padding(const EdgeInsets.all(7))
.tap(
onTap: () async {
final confirm = await context.showRoundDialog<bool>(
title: Text(l10n.attention),
@@ -89,27 +87,29 @@ class _SSHTabPageState extends State<SSHTabPage>
padding: const EdgeInsets.all(7),
itemBuilder: (_, idx) {
final spi = pro.servers.toList()[idx].spi;
return CardX(ListTile(
title: Text(spi.name),
subtitle: Text(spi.id, style: UIs.textGrey),
trailing: const Icon(Icons.chevron_right),
onTap: () {
final name = () {
if (_tabIds.containsKey(spi.name)) {
return '${spi.name}(${_tabIds.length + 1})';
}
return spi.name;
}();
final key = GlobalKey(debugLabel: 'sshTabPage_$name');
_tabIds[name] = SSHPage(
key: key,
spi: spi,
pop: false,
);
_refreshTabs();
_tabController.animateTo(_tabIds.length - 1);
},
));
return CardX(
child: ListTile(
title: Text(spi.name),
subtitle: Text(spi.id, style: UIs.textGrey),
trailing: const Icon(Icons.chevron_right),
onTap: () {
final name = () {
if (_tabIds.containsKey(spi.name)) {
return '${spi.name}(${_tabIds.length + 1})';
}
return spi.name;
}();
final key = GlobalKey(debugLabel: 'sshTabPage_$name');
_tabIds[name] = SSHPage(
key: key,
spi: spi,
pop: false,
);
_refreshTabs();
_tabController.animateTo(_tabIds.length - 1);
},
),
);
},
itemCount: pro.servers.length,
);

View File

@@ -139,32 +139,35 @@ class _LocalStoragePageState extends State<LocalStoragePage> {
var stat = file.statSync();
var isDir = stat.type == FileSystemEntityType.directory;
return CardX(ListTile(
leading: isDir
? const Icon(Icons.folder)
: const Icon(Icons.insert_drive_file),
title: Text(fileName),
subtitle:
isDir ? null : Text(stat.size.convertBytes, style: UIs.textGrey),
trailing: Text(
stat.modified
.toString()
.substring(0, stat.modified.toString().length - 4),
style: UIs.textGrey,
return CardX(
child: ListTile(
leading: isDir
? const Icon(Icons.folder)
: const Icon(Icons.insert_drive_file),
title: Text(fileName),
subtitle: isDir
? null
: Text(stat.size.convertBytes, style: UIs.textGrey),
trailing: Text(
stat.modified
.toString()
.substring(0, stat.modified.toString().length - 4),
style: UIs.textGrey,
),
onLongPress: () {
if (!isDir) return;
_showDirActionDialog(file);
},
onTap: () async {
if (!isDir) {
await _showFileActionDialog(file);
return;
}
_path!.update(fileName);
setState(() {});
},
),
onLongPress: () {
if (!isDir) return;
_showDirActionDialog(file);
},
onTap: () async {
if (!isDir) {
await _showFileActionDialog(file);
return;
}
_path!.update(fileName);
setState(() {});
},
));
);
},
);
}

View File

@@ -264,26 +264,28 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
style: UIs.textGrey,
textAlign: TextAlign.right,
);
return CardX(ListTile(
leading: Icon(isDir ? Icons.folder : Icons.insert_drive_file),
title: Text(file.filename),
trailing: trailing,
subtitle: isDir
? null
: Text(
(file.attr.size ?? 0).convertBytes,
style: UIs.textGrey,
),
onTap: () {
if (isDir) {
_status.path?.update(file.filename);
_listDir();
} else {
_onItemPress(file, true);
}
},
onLongPress: () => _onItemPress(file, !isDir),
));
return CardX(
child: ListTile(
leading: Icon(isDir ? Icons.folder : Icons.insert_drive_file),
title: Text(file.filename),
trailing: trailing,
subtitle: isDir
? null
: Text(
(file.attr.size ?? 0).convertBytes,
style: UIs.textGrey,
),
onTap: () {
if (isDir) {
_status.path?.update(file.filename);
_listDir();
} else {
_onItemPress(file, true);
}
},
onLongPress: () => _onItemPress(file, !isDir),
),
);
}
void _onItemPress(SftpName file, bool notDir) {

View File

@@ -134,7 +134,7 @@ class _SftpMissionPageState extends State<SftpMissionPage> {
}) {
final time = DateTime.fromMicrosecondsSinceEpoch(status.id);
return CardX(
ListTile(
child: ListTile(
leading: Text(time.hourMinute),
title: Text(
status.fileName,

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
class CardX extends StatelessWidget {
const CardX(this.child, {super.key, this.color});
const CardX({super.key, required this.child, this.color});
final Widget child;
final Color? color;

View File

@@ -58,7 +58,7 @@ class _InputState extends State<Input> {
@override
Widget build(BuildContext context) {
return CardX(
Padding(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 17),
child: TextField(
controller: widget.controller,

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:toolbox/core/extension/context/common.dart';
import 'package:toolbox/core/extension/context/dialog.dart';
import 'package:toolbox/core/extension/context/locale.dart';
import 'package:toolbox/core/extension/widget.dart';
import 'package:toolbox/data/res/ui.dart';
import 'package:toolbox/view/widget/input_field.dart';
import 'package:toolbox/view/widget/cardx.dart';
@@ -56,16 +57,17 @@ class TagEditor extends StatefulWidget {
class _TagEditorState extends State<TagEditor> {
@override
Widget build(BuildContext context) {
return CardX(ListTile(
leading: const Icon(Icons.tag),
title: _buildTags(widget.tags),
trailing: InkWell(
child: const Icon(Icons.add),
onTap: () {
_showAddTagDialog();
},
return CardX(
child: ListTile(
leading: const Icon(Icons.tag),
title: _buildTags(widget.tags),
trailing: const Icon(Icons.add).tap(
onTap: () {
_showAddTagDialog();
},
),
),
));
);
}
Widget _buildTags(List<String> tags) {
@@ -176,7 +178,7 @@ class _TagEditorState extends State<TagEditor> {
}
}
class TagSwitcher extends StatelessWidget {
class TagSwitcher extends StatelessWidget implements PreferredSizeWidget {
final List<String> tags;
final double width;
final void Function(String?) onTagChanged;
@@ -199,6 +201,7 @@ class TagSwitcher extends StatelessWidget {
return Container(
height: _kTagBtnHeight,
width: width,
padding: const EdgeInsets.symmetric(horizontal: 7),
alignment: Alignment.center,
color: Colors.transparent,
child: ListView.builder(
@@ -215,6 +218,9 @@ class TagSwitcher extends StatelessWidget {
),
);
}
@override
Size get preferredSize => const Size.fromHeight(_kTagBtnHeight);
}
Widget _wrap(
@@ -228,16 +234,10 @@ Widget _wrap(
borderRadius: const BorderRadius.all(Radius.circular(20.0)),
child: Material(
color: primaryColor.withAlpha(20),
child: InkWell(
onTap: onTap,
onLongPress: onLongPress,
child: Padding(
/// Hard coded padding
/// For centering the text
padding: const EdgeInsets.fromLTRB(11.7, 2.7, 11.7, 0),
child: child,
),
),
child: child.padding(const EdgeInsets.fromLTRB(11.7, 2.7, 11.7, 0)).tap(
onTap: onTap,
onLongTap: onLongPress,
),
),
),
);