migrate: material 3

This commit is contained in:
lollipopkit
2023-05-07 01:28:51 +08:00
parent e932241df0
commit 5afa543ba5
29 changed files with 438 additions and 480 deletions

View File

@@ -183,6 +183,12 @@ abstract class S {
/// **'Run in backgroud'** /// **'Run in backgroud'**
String get bgRun; String get bgRun;
/// No description provided for @canPullRefresh.
///
/// In en, this message translates to:
/// **'You can pull to refresh.'**
String get canPullRefresh;
/// No description provided for @cancel. /// No description provided for @cancel.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

View File

@@ -49,6 +49,9 @@ class SEn extends S {
@override @override
String get bgRun => 'Run in backgroud'; String get bgRun => 'Run in backgroud';
@override
String get canPullRefresh => 'You can pull to refresh.';
@override @override
String get cancel => 'Cancel'; String get cancel => 'Cancel';

View File

@@ -49,6 +49,9 @@ class SZh extends S {
@override @override
String get bgRun => '后台运行'; String get bgRun => '后台运行';
@override
String get canPullRefresh => '可以下拉刷新';
@override @override
String get cancel => '取消'; String get cancel => '取消';

View File

@@ -359,7 +359,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 280; CURRENT_PROJECT_VERSION = 284;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -367,7 +367,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.280; MARKETING_VERSION = 1.0.284;
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";
@@ -490,7 +490,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 280; CURRENT_PROJECT_VERSION = 284;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -498,7 +498,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.280; MARKETING_VERSION = 1.0.284;
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";
@@ -515,7 +515,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 280; CURRENT_PROJECT_VERSION = 284;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -523,7 +523,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.280; MARKETING_VERSION = 1.0.284;
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";

View File

@@ -1,7 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import '/core/extension/colorx.dart';
import 'core/utils/ui.dart'; import 'core/utils/ui.dart';
import 'data/res/build_data.dart'; import 'data/res/build_data.dart';
import 'data/res/color.dart'; import 'data/res/color.dart';

View File

@@ -53,10 +53,9 @@ Future<void> doUpdate(BuildContext context, {bool force = false}) async {
if (update.build.min.current! > BuildData.build) { if (update.build.min.current! > BuildData.build) {
showRoundDialog( showRoundDialog(
context, context: context,
s.attention, child: Text(s.updateTipTooLow(newest)),
Text(s.updateTipTooLow(newest)), actions: [
[
TextButton( TextButton(
onPressed: () => _doUpdate(url, context, s), onPressed: () => _doUpdate(url, context, s),
child: Text(s.ok), child: Text(s.ok),
@@ -84,11 +83,15 @@ Future<void> _doUpdate(String url, BuildContext context, S s) async {
} else if (isIOS) { } else if (isIOS) {
await RUpgrade.upgradeFromAppStore('1586449703'); await RUpgrade.upgradeFromAppStore('1586449703');
} else { } else {
showRoundDialog(context, s.attention, Text(s.platformNotSupportUpdate), [ showRoundDialog(
TextButton( context: context,
onPressed: () => context.pop(), child: Text(s.platformNotSupportUpdate),
child: Text(s.ok), actions: [
) TextButton(
]); onPressed: () => context.pop(),
child: Text(s.ok),
)
],
);
} }
} }

View File

@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:toolbox/core/utils/navigator.dart'; import 'package:toolbox/core/utils/navigator.dart';
import 'package:toolbox/data/res/ui.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import '../../data/model/server/snippet.dart'; import '../../data/model/server/snippet.dart';
@@ -25,8 +26,12 @@ bool isDarkMode(BuildContext context) =>
void showSnackBar(BuildContext context, Widget child) => void showSnackBar(BuildContext context, Widget child) =>
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: child)); ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: child));
void showSnackBarWithAction(BuildContext context, String content, String action, void showSnackBarWithAction(
GestureTapCallback onTap) { BuildContext context,
String content,
String action,
GestureTapCallback onTap,
) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar( ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(content), content: Text(content),
behavior: SnackBarBehavior.floating, behavior: SnackBarBehavior.floating,
@@ -41,24 +46,33 @@ Future<bool> openUrl(String url) async {
return await launchUrl(url.uri, mode: LaunchMode.externalApplication); return await launchUrl(url.uri, mode: LaunchMode.externalApplication);
} }
Future<T?>? showRoundDialog<T>( Future<T?>? showRoundDialog<T>({
BuildContext context, String title, Widget child, List<Widget> actions, required BuildContext context,
{EdgeInsets? padding, bool barrierDismiss = true}) { Widget? child,
List<Widget>? actions,
Widget? title,
EdgeInsets? padding,
bool barrierDismiss = true,
}) {
return showDialog<T>( return showDialog<T>(
context: context, context: context,
barrierDismissible: barrierDismiss, barrierDismissible: barrierDismiss,
builder: (ctx) { builder: (ctx) {
return CardDialog( return CardDialog(
title: Text(title), title: title,
content: child, content: child,
actions: actions, actions: actions,
padding: padding, padding: padding,
); );
}); },
);
} }
Widget buildSwitch(BuildContext context, StoreProperty<bool> prop, Widget buildSwitch(
{Function(bool)? func}) { BuildContext context,
StoreProperty<bool> prop, {
Function(bool)? func,
}) {
return ValueListenableBuilder( return ValueListenableBuilder(
valueListenable: prop.listenable(), valueListenable: prop.listenable(),
builder: (context, bool value, widget) { builder: (context, bool value, widget) {
@@ -75,27 +89,26 @@ Widget buildSwitch(BuildContext context, StoreProperty<bool> prop,
void setTransparentNavigationBar(BuildContext context) { void setTransparentNavigationBar(BuildContext context) {
if (isAndroid) { if (isAndroid) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( SystemChrome.setSystemUIOverlayStyle(
systemNavigationBarColor: Colors.transparent, const SystemUiOverlayStyle(
systemNavigationBarContrastEnforced: true)); systemNavigationBarColor: Colors.transparent,
systemNavigationBarContrastEnforced: true),
);
} }
} }
Widget buildPopuopMenu( Widget buildPopuopMenu<T>({
{required List<PopupMenuEntry> items, required List<PopupMenuEntry<T>> items,
required Function(dynamic) onSelected}) { required void Function(T) onSelected,
return PopupMenuButton( Widget child = popMenuChild,
EdgeInsetsGeometry? padding,
}) {
return PopupMenuButton<T>(
itemBuilder: (_) => items, itemBuilder: (_) => items,
onSelected: onSelected, onSelected: onSelected,
padding: EdgeInsets.zero, padding: padding ?? EdgeInsets.zero,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
child: const Padding( child: child,
padding: EdgeInsets.only(left: 7),
child: Icon(
Icons.more_vert,
size: 21,
),
),
); );
} }
@@ -123,14 +136,16 @@ Future<void> loadFontFile(String? localPath) async {
} }
void showSnippetDialog( void showSnippetDialog(
BuildContext context, S s, Function(Snippet s) onSelected) { BuildContext context,
S s,
Function(Snippet s) onSelected,
) {
final provider = locator<SnippetProvider>(); final provider = locator<SnippetProvider>();
if (provider.snippets.isEmpty) { if (provider.snippets.isEmpty) {
showRoundDialog( showRoundDialog(
context, context: context,
s.attention, child: Text(s.noSavedSnippet),
Text(s.noSavedSnippet), actions: [
[
TextButton( TextButton(
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
child: Text(s.ok), child: Text(s.ok),
@@ -149,13 +164,13 @@ void showSnippetDialog(
var snippet = provider.snippets.first; var snippet = provider.snippets.first;
showRoundDialog( showRoundDialog(
context, context: context,
s.choose, title: Text(s.chooseDestination),
buildPicker( child: buildPicker(
provider.snippets.map((e) => Text(e.name)).toList(), provider.snippets.map((e) => Text(e.name)).toList(),
(idx) => snippet = provider.snippets[idx], (idx) => snippet = provider.snippets[idx],
), ),
[ actions: [
TextButton( TextButton(
onPressed: () async { onPressed: () async {
context.pop(); context.pop();

View File

@@ -1,8 +0,0 @@
import 'package:flutter/material.dart';
class NavigationItem {
final IconData icon;
final String title;
NavigationItem(this.icon, this.title);
}

View File

@@ -0,0 +1 @@
enum AppTab { servers, encode, ping }

View File

@@ -58,10 +58,9 @@ class PkgProvider extends BusyProvider {
} }
Future<void> refresh() async { Future<void> refresh() async {
final result = await _update();_parse(result); final result = await _update();
try { _parse(result);
try {} catch (e) {
} catch (e) {
error = '[Server Raw]:\n$result\n[App Error]:\n$e'; error = '[Server Raw]:\n$result\n[App Error]:\n$e';
} finally { } finally {
notifyListeners(); notifyListeners();
@@ -70,11 +69,8 @@ class PkgProvider extends BusyProvider {
void _parse(String? raw) { void _parse(String? raw) {
if (raw == null) return; if (raw == null) return;
final list = type final list = type?.updateListRemoveUnused(raw.split('\n'));
?.updateListRemoveUnused(raw.split('\n')); upgradeable = list?.map((e) => UpgradePkgInfo(e, type)).toList();
upgradeable = list
?.map((e) => UpgradePkgInfo(e, type))
.toList();
} }
Future<String?> _update() async { Future<String?> _update() async {

View File

@@ -3,7 +3,6 @@ import 'dart:async';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import '../../core/build_mode.dart';
import '../../core/extension/uint8list.dart'; import '../../core/extension/uint8list.dart';
import '../../core/provider_base.dart'; import '../../core/provider_base.dart';
import '../../core/utils/server.dart'; import '../../core/utils/server.dart';
@@ -182,9 +181,6 @@ class ServerProvider extends BusyProvider {
s.state = ServerState.failed; s.state = ServerState.failed;
s.status.failedInfo = e.toString(); s.status.failedInfo = e.toString();
_logger.warning(e); _logger.warning(e);
if (BuildMode.isDebug) {
rethrow;
}
} finally { } finally {
notifyListeners(); notifyListeners();
} }

View File

@@ -2,8 +2,8 @@
class BuildData { class BuildData {
static const String name = "ServerBox"; static const String name = "ServerBox";
static const int build = 280; static const int build = 284;
static const String engine = "3.7.11"; static const String engine = "3.7.11";
static const String buildAt = "2023-04-27 18:10:45.464081"; static const String buildAt = "2023-05-07 00:52:57.124037";
static const int modifications = 4; static const int modifications = 23;
} }

View File

@@ -1,9 +0,0 @@
import 'package:flutter/material.dart';
import 'package:toolbox/data/model/app/navigation_item.dart';
final List<String> tabs = ['Servers', 'En/Decode', 'Ping'];
final List<NavigationItem> tabItems = [
NavigationItem(Icons.cloud, 'Server'),
NavigationItem(Icons.code, 'Convert'),
NavigationItem(Icons.leak_add, 'Ping'),
];

View File

@@ -23,3 +23,11 @@ const roundRectCardPadding = EdgeInsets.symmetric(horizontal: 17, vertical: 13);
const height13 = SizedBox(height: 13); const height13 = SizedBox(height: 13);
const width13 = SizedBox(width: 13); const width13 = SizedBox(width: 13);
const width7 = SizedBox(width: 7); const width7 = SizedBox(width: 7);
const popMenuChild = Padding(
padding: EdgeInsets.only(left: 7),
child: Icon(
Icons.more_vert,
size: 21,
),
);

View File

@@ -14,6 +14,7 @@
"backupTip": "The exported data is simply encrypted. \nPlease keep it safe.", "backupTip": "The exported data is simply encrypted. \nPlease keep it safe.",
"backupVersionNotMatch": "Backup version is not match.", "backupVersionNotMatch": "Backup version is not match.",
"bgRun": "Run in backgroud", "bgRun": "Run in backgroud",
"canPullRefresh": "You can pull to refresh.",
"cancel": "Cancel", "cancel": "Cancel",
"choose": "Choose", "choose": "Choose",
"chooseDestination": "Choose destination", "chooseDestination": "Choose destination",

View File

@@ -14,6 +14,7 @@
"backupTip": "导出的数据仅进行了简单加密,请妥善保管。", "backupTip": "导出的数据仅进行了简单加密,请妥善保管。",
"backupVersionNotMatch": "备份版本不匹配,无法恢复", "backupVersionNotMatch": "备份版本不匹配,无法恢复",
"bgRun": "后台运行", "bgRun": "后台运行",
"canPullRefresh": "可以下拉刷新",
"cancel": "取消", "cancel": "取消",
"choose": "选择", "choose": "选择",
"chooseDestination": "选择目标", "chooseDestination": "选择目标",

View File

@@ -141,10 +141,9 @@ class BackupPage extends StatelessWidget {
} }
await showRoundDialog( await showRoundDialog(
context, context: context,
s.attention, child: Text(s.restoreSureWithDate(backup.date)),
Text(s.restoreSureWithDate(backup.date)), actions: [
[
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(s.cancel), child: Text(s.cancel),
@@ -165,10 +164,9 @@ class BackupPage extends StatelessWidget {
} }
context.pop(); context.pop();
showRoundDialog( showRoundDialog(
context, context: context,
s.attention, child: Text(s.restoreSuccess),
Text(s.restoreSuccess), actions: [
[
TextButton( TextButton(
onPressed: () => rebuildAll(context), onPressed: () => rebuildAll(context),
child: Text(s.restart), child: Text(s.restart),

View File

@@ -124,7 +124,7 @@ class _ConvertPageState extends State<ConvertPage>
onPressed: () => Clipboard.setData( onPressed: () => Clipboard.setData(
ClipboardData( ClipboardData(
text: _textEditingControllerResult.text == '' text: _textEditingControllerResult.text == ''
? ' ' ? ''
: _textEditingControllerResult.text, : _textEditingControllerResult.text,
), ),
), ),

View File

@@ -89,9 +89,9 @@ class _DockerManagePageState extends State<DockerManagePage> {
final nameCtrl = TextEditingController(); final nameCtrl = TextEditingController();
final argsCtrl = TextEditingController(); final argsCtrl = TextEditingController();
await showRoundDialog( await showRoundDialog(
context, context: context,
_s.newContainer, title: Text(_s.newContainer),
Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
TextField( TextField(
@@ -118,7 +118,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
), ),
], ],
), ),
[ actions: [
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.cancel), child: Text(_s.cancel),
@@ -142,10 +142,10 @@ class _DockerManagePageState extends State<DockerManagePage> {
Future<void> _showAddCmdPreview(String cmd) async { Future<void> _showAddCmdPreview(String cmd) async {
await showRoundDialog( await showRoundDialog(
context, context: context,
_s.preview, title: Text(_s.preview),
Text(cmd), child: Text(cmd),
[ actions: [
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.cancel), child: Text(_s.cancel),
@@ -186,12 +186,16 @@ class _DockerManagePageState extends State<DockerManagePage> {
void onSubmitted() { void onSubmitted() {
if (_textController.text == '') { if (_textController.text == '') {
showRoundDialog(context, _s.attention, Text(_s.fieldMustNotEmpty), [ showRoundDialog(
TextButton( context: context,
onPressed: () => context.pop(), child: Text(_s.fieldMustNotEmpty),
child: Text(_s.ok), actions: [
), TextButton(
]); onPressed: () => context.pop(),
child: Text(_s.ok),
),
],
);
return; return;
} }
context.pop(); context.pop();
@@ -200,9 +204,9 @@ class _DockerManagePageState extends State<DockerManagePage> {
Future<String> onPwdRequest() async { Future<String> onPwdRequest() async {
if (!mounted) return ''; if (!mounted) return '';
await showRoundDialog( await showRoundDialog(
context, context: context,
widget.spi.user, title: Text(widget.spi.user),
TextField( child: TextField(
controller: _textController, controller: _textController,
keyboardType: TextInputType.visiblePassword, keyboardType: TextInputType.visiblePassword,
obscureText: true, obscureText: true,
@@ -211,7 +215,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
labelText: _s.pwd, labelText: _s.pwd,
), ),
), ),
[ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
context.pop(); context.pop();
@@ -290,10 +294,9 @@ class _DockerManagePageState extends State<DockerManagePage> {
icon: const Icon(Icons.delete), icon: const Icon(Icons.delete),
onPressed: () async { onPressed: () async {
showRoundDialog( showRoundDialog(
context, context: context,
_s.attention, child: Text(_s.sureDelete(e.repo)),
Text(_s.sureDelete(e.repo)), actions: [
[
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.cancel), child: Text(_s.cancel),
@@ -366,9 +369,9 @@ class _DockerManagePageState extends State<DockerManagePage> {
Future<void> _showEditHostDialog() async { Future<void> _showEditHostDialog() async {
await showRoundDialog( await showRoundDialog(
context, context: context,
_s.dockerEditHost, title: Text(_s.dockerEditHost),
TextField( child: TextField(
maxLines: 1, maxLines: 1,
autocorrect: false, autocorrect: false,
controller: controller:
@@ -379,7 +382,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
context.pop(); context.pop();
}, },
), ),
[ actions: [
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.cancel), child: Text(_s.cancel),
@@ -473,10 +476,9 @@ class _DockerManagePageState extends State<DockerManagePage> {
switch (item) { switch (item) {
case DockerMenuItems.rm: case DockerMenuItems.rm:
showRoundDialog( showRoundDialog(
context, context: context,
_s.attention, child: Text(_s.sureDelete(dItem.name)),
Text(_s.sureDelete(dItem.name)), actions: [
[
TextButton( TextButton(
onPressed: () { onPressed: () {
context.pop(); context.pop();

View File

@@ -5,17 +5,15 @@ import 'package:get_it/get_it.dart';
import 'package:toolbox/core/utils/navigator.dart'; import 'package:toolbox/core/utils/navigator.dart';
import 'package:toolbox/data/provider/app.dart'; import 'package:toolbox/data/provider/app.dart';
import 'package:toolbox/data/res/misc.dart'; import 'package:toolbox/data/res/misc.dart';
import 'package:toolbox/view/widget/round_rect_card.dart';
import '../../core/analysis.dart'; import '../../core/analysis.dart';
import '../../core/route.dart'; import '../../core/route.dart';
import '../../core/update.dart'; import '../../core/update.dart';
import '../../core/utils/platform.dart'; import '../../core/utils/platform.dart';
import '../../core/utils/ui.dart'; import '../../core/utils/ui.dart';
import '../../data/model/app/dynamic_color.dart';
import '../../data/model/app/navigation_item.dart';
import '../../data/provider/server.dart'; import '../../data/provider/server.dart';
import '../../data/res/build_data.dart'; import '../../data/res/build_data.dart';
import '../../data/res/tab.dart';
import '../../data/res/ui.dart'; import '../../data/res/ui.dart';
import '../../data/res/url.dart'; import '../../data/res/url.dart';
import '../../data/store/setting.dart'; import '../../data/store/setting.dart';
@@ -31,9 +29,6 @@ import 'setting.dart';
import 'sftp/downloaded.dart'; import 'sftp/downloaded.dart';
import 'snippet/list.dart'; import 'snippet/list.dart';
final _bottomItemOverlayColor =
DynamicColor(Colors.black.withOpacity(0.07), Colors.white12);
class MyHomePage extends StatefulWidget { class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key); const MyHomePage({Key? key}) : super(key: key);
@@ -52,7 +47,6 @@ class _MyHomePageState extends State<MyHomePage>
late final PageController _pageController; late final PageController _pageController;
late int _selectIndex; late int _selectIndex;
late double _width;
late S _s; late S _s;
@override @override
@@ -67,7 +61,6 @@ class _MyHomePageState extends State<MyHomePage>
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();
_s = S.of(context)!; _s = S.of(context)!;
_width = MediaQuery.of(context).size.width;
} }
@override @override
@@ -135,33 +128,35 @@ class _MyHomePageState extends State<MyHomePage>
); );
} }
Widget _buildBottomBar(BuildContext context){ Widget _buildBottomBar(BuildContext context) {
return NavigationBar( return NavigationBar(
selectedIndex: _selectIndex, selectedIndex: _selectIndex,
animationDuration: const Duration(milliseconds: 250), animationDuration: const Duration(milliseconds: 250),
onDestinationSelected: (int index) { onDestinationSelected: (int index) {
setState(() { setState(() {
_selectIndex = index; _selectIndex = index;
_pageController.animateToPage(index, _pageController.animateToPage(
duration: const Duration(milliseconds: 677), index,
curve: Curves.fastLinearToSlowEaseIn); duration: const Duration(milliseconds: 677),
curve: Curves.fastLinearToSlowEaseIn,
);
}); });
}, },
elevation: 0.47,
labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected, labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected,
destinations: const [ destinations: [
NavigationDestination( NavigationDestination(
icon: Icon(Icons.cloud_outlined), icon: const Icon(Icons.cloud_outlined),
label: 'Server', label: _s.server,
selectedIcon: Icon(Icons.cloud), selectedIcon: const Icon(Icons.cloud),
), ),
NavigationDestination( NavigationDestination(
icon: Icon(Icons.code), icon: const Icon(Icons.code),
label: 'Convert', label: _s.convert,
), ),
NavigationDestination( NavigationDestination(
icon: Icon(Icons.leak_add), icon: const Icon(Icons.leak_add),
label: 'Ping', label: _s.ping,
selectedIcon: Icon(Icons.leak_add_outlined),
), ),
], ],
); );
@@ -169,16 +164,16 @@ class _MyHomePageState extends State<MyHomePage>
Widget _buildDrawer() { Widget _buildDrawer() {
return Drawer( return Drawer(
surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
_buildIcon(), _buildIcon(),
TextButton( TextButton(
onPressed: () => showRoundDialog( onPressed: () => showRoundDialog(
context, context: context,
_versionStr, title: Text(_versionStr),
const Text(BuildData.buildAt), child: const Text(BuildData.buildAt),
[],
), ),
child: Text( child: Text(
'${BuildData.name}\n$_versionStr', '${BuildData.name}\n$_versionStr',
@@ -190,7 +185,7 @@ class _MyHomePageState extends State<MyHomePage>
height: MediaQuery.of(context).size.height * 0.07, height: MediaQuery.of(context).size.height * 0.07,
), ),
Padding( Padding(
padding: const EdgeInsets.only(left: 29), padding: const EdgeInsets.symmetric(horizontal: 13),
child: Column( child: Column(
children: [ children: [
ListTile( ListTile(
@@ -224,10 +219,9 @@ class _MyHomePageState extends State<MyHomePage>
leading: const Icon(Icons.info), leading: const Icon(Icons.info),
title: Text(_s.feedback), title: Text(_s.feedback),
onTap: () => showRoundDialog( onTap: () => showRoundDialog(
context, context: context,
_s.feedback, child: Text(_s.feedbackOnGithub),
Text(_s.feedbackOnGithub), actions: [
[
TextButton( TextButton(
onPressed: () => openUrl(issueUrl), onPressed: () => openUrl(issueUrl),
child: Text(_s.feedback), child: Text(_s.feedback),
@@ -250,9 +244,9 @@ class _MyHomePageState extends State<MyHomePage>
title: Text(_s.about), title: Text(_s.about),
onTap: () { onTap: () {
showRoundDialog( showRoundDialog(
context, context: context,
_s.about, title: Text(_s.about),
Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@@ -271,7 +265,7 @@ class _MyHomePageState extends State<MyHomePage>
) )
], ],
), ),
[ actions: [
TextButton( TextButton(
onPressed: () => showLicensePage(context: context), onPressed: () => showLicensePage(context: context),
child: Text(_s.license), child: Text(_s.license),
@@ -284,7 +278,7 @@ class _MyHomePageState extends State<MyHomePage>
); );
}, },
) )
], ].map((e) => RoundRectCard(e)).toList(),
), ),
), ),
], ],

View File

@@ -72,10 +72,9 @@ class _PkgManagePageState extends State<PkgManagePage>
void onSubmitted() { void onSubmitted() {
if (_textController.text == '') { if (_textController.text == '') {
showRoundDialog( showRoundDialog(
context, context: context,
_s.attention, child: Text(_s.fieldMustNotEmpty),
Text(_s.fieldMustNotEmpty), actions: [
[
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.ok), child: Text(_s.ok),
@@ -90,9 +89,9 @@ class _PkgManagePageState extends State<PkgManagePage>
Future<String> onPwdRequest() async { Future<String> onPwdRequest() async {
if (!mounted) return ''; if (!mounted) return '';
await showRoundDialog( await showRoundDialog(
context, context: context,
widget.spi.user, title: Text(widget.spi.user),
TextField( child: TextField(
controller: _textController, controller: _textController,
keyboardType: TextInputType.visiblePassword, keyboardType: TextInputType.visiblePassword,
obscureText: true, obscureText: true,
@@ -101,7 +100,7 @@ class _PkgManagePageState extends State<PkgManagePage>
labelText: _s.pwd, labelText: _s.pwd,
), ),
), ),
[ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
context.pop(); context.pop();
@@ -234,10 +233,7 @@ class _PkgManagePageState extends State<PkgManagePage>
}(); }();
return ListTile( return ListTile(
title: Text(info.package), title: Text(info.package),
subtitle: Text( subtitle: Text(t, style: grey),
t,
style: grey
),
); );
} }
} }

View File

@@ -68,10 +68,9 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
? IconButton( ? IconButton(
onPressed: () { onPressed: () {
showRoundDialog( showRoundDialog(
context, context: context,
_s.attention, child: Text(_s.sureToDeleteServer(widget.spi!.name)),
Text(_s.sureToDeleteServer(widget.spi!.name)), actions: [
[
TextButton( TextButton(
onPressed: () { onPressed: () {
_serverProvider.delServer(widget.spi!.id); _serverProvider.delServer(widget.spi!.id);
@@ -148,10 +147,11 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
hint: 'root', hint: 'root',
), ),
), ),
const SizedBox(height: 7), width7,
Row( Row(
children: [ children: [
Text(_s.keyAuth), Text(_s.keyAuth),
width13,
Switch( Switch(
value: usePublicKey, value: usePublicKey,
onChanged: (val) => setState(() => usePublicKey = val), onChanged: (val) => setState(() => usePublicKey = val),
@@ -228,10 +228,9 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
} }
if (!usePublicKey && _passwordController.text == '') { if (!usePublicKey && _passwordController.text == '') {
final cancel = await showRoundDialog<bool>( final cancel = await showRoundDialog<bool>(
context, context: context,
_s.attention, child: Text(_s.sureNoPwd),
Text(_s.sureNoPwd), actions: [
[
TextButton( TextButton(
onPressed: () => context.pop(false), onPressed: () => context.pop(false),
child: Text(_s.ok), child: Text(_s.ok),

View File

@@ -166,12 +166,16 @@ class _ServerPageState extends State<ServerPage>
hasError hasError
? GestureDetector( ? GestureDetector(
onTap: () => showRoundDialog( onTap: () => showRoundDialog(
context, _s.error, Text(ss.failedInfo ?? ''), []), context: context,
title: Text(_s.error),
child: Text(ss.failedInfo ?? ''),
),
child: Text( child: Text(
_s.clickSee, _s.clickSee,
style: style, style: style,
textScaleFactor: 1.0, textScaleFactor: 1.0,
)) ),
)
: Text(topRightStr, style: style, textScaleFactor: 1.0), : Text(topRightStr, style: style, textScaleFactor: 1.0),
const SizedBox(width: 9), const SizedBox(width: 9),
_buildSSHBtn(spi), _buildSSHBtn(spi),
@@ -218,15 +222,15 @@ class _ServerPageState extends State<ServerPage>
onTap: () async { onTap: () async {
if (_settingStore.firstTimeUseSshTerm.fetch()!) { if (_settingStore.firstTimeUseSshTerm.fetch()!) {
await showRoundDialog( await showRoundDialog(
context, context: context,
_s.attention, child: UrlText(
UrlText(
text: _s.sshTip(issueUrl), text: _s.sshTip(issueUrl),
replace: 'Github Issue', replace: 'Github Issue',
), ),
[ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
_settingStore.firstTimeUseSshTerm.put(false);
context.pop(); context.pop();
AppRoute(SSHPage(spi: spi), 'ssh page').go(context); AppRoute(SSHPage(spi: spi), 'ssh page').go(context);
}, },
@@ -234,7 +238,6 @@ class _ServerPageState extends State<ServerPage>
) )
], ],
); );
_settingStore.firstTimeUseSshTerm.put(false);
} else { } else {
AppRoute(SSHPage(spi: spi), 'ssh page').go(context); AppRoute(SSHPage(spi: spi), 'ssh page').go(context);
} }
@@ -269,13 +272,11 @@ class _ServerPageState extends State<ServerPage>
break; break;
case ServerTabMenuItems.snippet: case ServerTabMenuItems.snippet:
showSnippetDialog(context, _s, (s) async { showSnippetDialog(context, _s, (s) async {
final result = final result = await _serverProvider.runSnippet(spi.id, s);
await locator<ServerProvider>().runSnippet(spi.id, s);
showRoundDialog( showRoundDialog(
context, context: context,
_s.result, child: Text(result ?? _s.error, style: textSize13),
Text(result ?? _s.error, style: textSize13), actions: [
[
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.ok), child: Text(_s.ok),

View File

@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_material_color_picker/flutter_material_color_picker.dart'; import 'package:flutter_material_color_picker/flutter_material_color_picker.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:toolbox/data/model/app/tab.dart';
import '../../core/utils/misc.dart'; import '../../core/utils/misc.dart';
import '../../core/utils/platform.dart'; import '../../core/utils/platform.dart';
@@ -15,7 +16,6 @@ import '../../data/provider/server.dart';
import '../../data/res/build_data.dart'; import '../../data/res/build_data.dart';
import '../../data/res/color.dart'; import '../../data/res/color.dart';
import '../../data/res/path.dart'; import '../../data/res/path.dart';
import '../../data/res/tab.dart';
import '../../data/res/ui.dart'; import '../../data/res/ui.dart';
import '../../data/store/setting.dart'; import '../../data/store/setting.dart';
import '../../locator.dart'; import '../../locator.dart';
@@ -39,8 +39,8 @@ class _SettingPageState extends State<SettingPage> {
late int _launchPageIdx; late int _launchPageIdx;
late int _termThemeIdx; late int _termThemeIdx;
late int _nightMode; late int _nightMode;
late double _maxRetryCount; late int _maxRetryCount;
late double _updateInterval; late int _updateInterval;
String? _pushToken; String? _pushToken;
@@ -59,8 +59,8 @@ class _SettingPageState extends State<SettingPage> {
_launchPageIdx = _setting.launchPage.fetch()!; _launchPageIdx = _setting.launchPage.fetch()!;
_termThemeIdx = _setting.termColorIdx.fetch()!; _termThemeIdx = _setting.termColorIdx.fetch()!;
_nightMode = _setting.themeMode.fetch()!; _nightMode = _setting.themeMode.fetch()!;
_updateInterval = _setting.serverStatusUpdateInterval.fetch()!.toDouble(); _updateInterval = _setting.serverStatusUpdateInterval.fetch()!;
_maxRetryCount = _setting.maxRetryCount.fetch()!.toDouble(); _maxRetryCount = _setting.maxRetryCount.fetch()!;
_selectedColorValue = _setting.primaryColor.fetch()!; _selectedColorValue = _setting.primaryColor.fetch()!;
} }
@@ -175,8 +175,15 @@ class _SettingPageState extends State<SettingPage> {
} }
Widget _buildUpdateInterval() { Widget _buildUpdateInterval() {
return ExpansionTile( final items = List.generate(
textColor: primaryColor, 10,
(index) => PopupMenuItem(
value: index,
child: Text('$index ${_s.second}'),
),
growable: false,
).toList();
return ListTile(
title: Text( title: Text(
_s.updateServerStatusInterval, _s.updateServerStatusInterval,
), ),
@@ -184,48 +191,27 @@ class _SettingPageState extends State<SettingPage> {
_s.willTakEeffectImmediately, _s.willTakEeffectImmediately,
style: grey, style: grey,
), ),
trailing: Text( trailing: buildPopuopMenu<int>(
'${_updateInterval.toInt()} ${_s.second}', items: items,
onSelected: (val) {
setState(() {
_updateInterval = val;
});
_setting.serverStatusUpdateInterval.put(_updateInterval.toInt());
_serverProvider.startAutoRefresh();
if (val == 0) {
showSnackBar(context, Text(_s.updateIntervalEqual0));
}
},
child: Text(
'${_updateInterval.toInt()} ${_s.second}',
),
), ),
children: [
Slider(
thumbColor: primaryColor,
activeColor: primaryColor.withOpacity(0.7),
min: 0,
max: 10,
value: _updateInterval,
onChanged: (newValue) {
setState(() {
_updateInterval = newValue;
});
},
onChangeEnd: (val) {
_setting.serverStatusUpdateInterval.put(val.toInt());
_serverProvider.startAutoRefresh();
},
label: '${_updateInterval.toInt()} ${_s.second}',
divisions: 10,
),
const SizedBox(
height: 3,
),
_updateInterval == 0.0
? Text(
_s.updateIntervalEqual0,
style: grey,
textAlign: TextAlign.center,
)
: const SizedBox(),
const SizedBox(
height: 13,
)
],
); );
} }
Widget _buildAppColorPreview() { Widget _buildAppColorPreview() {
return ExpansionTile( return ListTile(
textColor: primaryColor,
trailing: ClipOval( trailing: ClipOval(
child: Container( child: Container(
color: primaryColor, color: primaryColor,
@@ -236,188 +222,146 @@ class _SettingPageState extends State<SettingPage> {
title: Text( title: Text(
_s.appPrimaryColor, _s.appPrimaryColor,
), ),
children: [_buildAppColorPicker(), _buildColorPickerConfirmBtn()], onTap: () async {
); await showRoundDialog(
} context: context,
child: MaterialColorPicker(
Widget _buildAppColorPicker() { shrinkWrap: true,
return MaterialColorPicker( allowShades: true,
shrinkWrap: true, onColorChange: (color) {
allowShades: false, _selectedColorValue = color.value;
onMainColorChange: (ColorSwatch<dynamic>? color) { },
if(color == null) return; selectedColor: primaryColor,
_selectedColorValue = color.value; ),
actions: [
TextButton(
onPressed: () {
_setting.primaryColor.put(_selectedColorValue);
Navigator.pop(context);
_showRestartSnackbar();
},
child: Text(_s.ok),
)
],
);
}, },
selectedColor: primaryColor,
);
}
Widget _buildColorPickerConfirmBtn() {
return IconButton(
icon: const Icon(Icons.save),
onPressed: (() {
_setting.primaryColor.put(_selectedColorValue);
_showRestartSnackbar();
}),
); );
} }
Widget _buildLaunchPage() { Widget _buildLaunchPage() {
return ExpansionTile( final items = AppTab.values
childrenPadding: const EdgeInsets.only(left: 17, right: 7), .map(
textColor: primaryColor, (e) => PopupMenuItem(
value: e.index,
child: Text(tabTitleName(context, e.index)),
),
)
.toList();
return ListTile(
title: Text( title: Text(
_s.launchPage, _s.launchPage,
), ),
trailing: ConstrainedBox( trailing: buildPopuopMenu<int>(
constraints: BoxConstraints(maxWidth: _media.size.width * 0.35), items: items,
child: Text( onSelected: (idx) {
tabTitleName(context, _launchPageIdx), setState(() {
textAlign: TextAlign.right, _launchPageIdx = idx;
});
_setting.launchPage.put(_launchPageIdx);
},
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: _media.size.width * 0.35),
child: Text(
tabTitleName(context, _launchPageIdx),
textAlign: TextAlign.right,
),
), ),
), ),
children: tabs
.map(
(e) => ListTile(
contentPadding: EdgeInsets.zero,
title: Text(
tabTitleName(context, tabs.indexOf(e)),
),
trailing: _buildRadio(tabs.indexOf(e)),
),
)
.toList(),
);
}
Radio _buildRadio(int index) {
return Radio<int>(
value: index,
groupValue: _launchPageIdx,
onChanged: (int? value) {
setState(() {
_launchPageIdx = value!;
_setting.launchPage.put(value);
});
},
); );
} }
Widget _buildTermTheme() { Widget _buildTermTheme() {
return ExpansionTile( final items = TerminalColorsPlatform.values
textColor: primaryColor,
childrenPadding: const EdgeInsets.only(left: 17),
title: Text(
_s.termTheme,
),
trailing: Text(
TerminalColorsPlatform.values[_termThemeIdx].name,
),
children: _buildTermThemeRadioList(),
);
}
List<Widget> _buildTermThemeRadioList() {
return TerminalColorsPlatform.values
.map( .map(
(e) => ListTile( (e) => PopupMenuItem<int>(
contentPadding: EdgeInsets.zero, value: e.index,
title: Text( child: Text(e.name),
e.name,
),
trailing: _buildTermThemeRadio(e),
), ),
) )
.toList(); .toList();
} return ListTile(
title: Text(
Radio _buildTermThemeRadio(TerminalColorsPlatform platform) { _s.termTheme,
return Radio<int>( ),
value: platform.index, trailing: buildPopuopMenu<int>(
groupValue: _termThemeIdx, items: items,
onChanged: (int? value) { onSelected: (idx) {
setState(() { setState(() {
value ??= 0; _termThemeIdx = idx;
_termThemeIdx = value!; });
_setting.termColorIdx.put(value!); _setting.termColorIdx.put(idx);
}); },
}, child: Text(
TerminalColorsPlatform.values[_termThemeIdx].name,
),
),
); );
} }
Widget _buildMaxRetry() { Widget _buildMaxRetry() {
return ExpansionTile( final items = List.generate(
textColor: primaryColor, 10,
(index) => PopupMenuItem(
value: index,
child: Text('$index ${_s.times}'),
),
growable: false,
).toList();
final help =
_maxRetryCount == 0 ? _s.maxRetryCountEqual0 : _s.canPullRefresh;
return ListTile(
title: Text( title: Text(
_s.maxRetryCount, _s.maxRetryCount,
textAlign: TextAlign.start, textAlign: TextAlign.start,
), ),
trailing: Text( trailing: buildPopuopMenu<int>(
'${_maxRetryCount.toInt()} ${_s.times}', items: items,
onSelected: (val) {
setState(() {
_maxRetryCount = val;
});
_setting.maxRetryCount.put(_maxRetryCount);
},
child: Text(
'${_maxRetryCount.toInt()} ${_s.times}',
),
), ),
children: [ subtitle: Text(help, style: grey),
Slider(
thumbColor: primaryColor,
activeColor: primaryColor.withOpacity(0.7),
min: 0,
max: 10,
value: _maxRetryCount,
onChanged: (newValue) {
setState(() {
_maxRetryCount = newValue;
});
},
onChangeEnd: (val) {
_setting.maxRetryCount.put(val.toInt());
},
label: '${_maxRetryCount.toInt()} ${_s.times}',
divisions: 10,
),
const SizedBox(
height: 3,
),
_maxRetryCount == 0.0
? Text(
_s.maxRetryCountEqual0,
style: grey,
textAlign: TextAlign.center,
)
: const SizedBox(),
const SizedBox(
height: 13,
)
],
); );
} }
Widget _buildThemeMode() { Widget _buildThemeMode() {
return ExpansionTile( final items = ThemeMode.values.map(
textColor: primaryColor, (e) {
final str = _buildThemeModeStr(e.index);
return PopupMenuItem(
value: e.index,
child: Text(str),
);
},
).toList();
return ListTile(
title: Text( title: Text(
_s.themeMode, _s.themeMode,
), ),
trailing: Text( trailing: buildPopuopMenu<int>(
_buildThemeModeStr(_nightMode), items: items,
onSelected: (idx) {
_nightMode = idx;
_setting.themeMode.put(_nightMode);
},
child: Text(_buildThemeModeStr(_nightMode)),
), ),
children: [
Slider(
thumbColor: primaryColor,
activeColor: primaryColor.withOpacity(0.7),
min: 0,
max: 2,
value: _nightMode.toDouble(),
onChanged: (newValue) {
setState(() {
_nightMode = newValue.toInt();
});
},
onChangeEnd: (val) {
_setting.themeMode.put(val.toInt());
},
label: _buildThemeModeStr(_nightMode),
divisions: 2,
),
],
); );
} }
@@ -467,27 +411,30 @@ class _SettingPageState extends State<SettingPage> {
} }
Widget _buildFont() { Widget _buildFont() {
return ExpansionTile( return ListTile(
title: Text(_s.chooseFontFile), title: Text(_s.chooseFontFile),
trailing: Text(getFileName(_setting.fontPath.fetch()) ?? _s.notSelected), trailing: Text(getFileName(_setting.fontPath.fetch()) ?? _s.notSelected),
children: [ onTap: () {
Row( showRoundDialog(
mainAxisAlignment: MainAxisAlignment.spaceAround, context: context,
children: [ child: Row(
TextButton( mainAxisAlignment: MainAxisAlignment.spaceAround,
onPressed: () async => _pickFontFile(), children: [
child: Text(_s.pickFile), TextButton(
), onPressed: () async => await _pickFontFile(),
TextButton( child: Text(_s.pickFile),
onPressed: () => setState(() { ),
_setting.fontPath.delete(); TextButton(
_showRestartSnackbar(); onPressed: () => setState(() {
}), _setting.fontPath.delete();
child: Text(_s.clear), _showRestartSnackbar();
) }),
], child: Text(_s.clear),
) )
], ],
),
);
},
); );
} }

View File

@@ -136,9 +136,8 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
void showFileActionDialog(FileSystemEntity file) { void showFileActionDialog(FileSystemEntity file) {
final fileName = file.path.split('/').last; final fileName = file.path.split('/').last;
showRoundDialog( showRoundDialog(
context, context: context,
_s.choose, child: Column(
Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
ListTile( ListTile(
@@ -147,10 +146,9 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
onTap: () { onTap: () {
context.pop(); context.pop();
showRoundDialog( showRoundDialog(
context, context: context,
_s.sureDelete(fileName), child: Text(_s.sureDelete(fileName)),
const SizedBox(), actions: [
[
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.cancel), child: Text(_s.cancel),
@@ -176,7 +174,7 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
), ),
], ],
), ),
[ actions: [
TextButton( TextButton(
onPressed: (() => context.pop()), onPressed: (() => context.pop()),
child: Text(_s.close), child: Text(_s.close),

View File

@@ -98,9 +98,8 @@ class _SFTPPageState extends State<SFTPPage> {
), ),
IconButton( IconButton(
onPressed: (() => showRoundDialog( onPressed: (() => showRoundDialog(
context, context: context,
_s.choose, child: Column(
Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
ListTile( ListTile(
@@ -113,7 +112,7 @@ class _SFTPPageState extends State<SFTPPage> {
onTap: () => newFile(context)), onTap: () => newFile(context)),
], ],
), ),
[ actions: [
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.close), child: Text(_s.close),
@@ -126,9 +125,9 @@ class _SFTPPageState extends State<SFTPPage> {
padding: const EdgeInsets.all(0), padding: const EdgeInsets.all(0),
onPressed: () async { onPressed: () async {
final p = await showRoundDialog<String?>( final p = await showRoundDialog<String?>(
context, context: context,
_s.goto, title: Text(_s.goto),
Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
TextField( TextField(
@@ -140,7 +139,7 @@ class _SFTPPageState extends State<SFTPPage> {
), ),
], ],
), ),
[ actions: [
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.cancel), child: Text(_s.cancel),
@@ -254,9 +253,8 @@ class _SFTPPageState extends State<SFTPPage> {
void onItemPress(BuildContext context, SftpName file, bool showDownload) { void onItemPress(BuildContext context, SftpName file, bool showDownload) {
showRoundDialog( showRoundDialog(
context, context: context,
_s.choose, child: Column(
Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
ListTile( ListTile(
@@ -278,7 +276,7 @@ class _SFTPPageState extends State<SFTPPage> {
: const SizedBox() : const SizedBox()
], ],
), ),
[ actions: [
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.cancel), child: Text(_s.cancel),
@@ -289,10 +287,9 @@ class _SFTPPageState extends State<SFTPPage> {
void download(BuildContext context, SftpName name) { void download(BuildContext context, SftpName name) {
showRoundDialog( showRoundDialog(
context, context: context,
_s.download, child: Text('${_s.dl2Local(name.filename)}\n${_s.keepForeground}'),
Text('${_s.dl2Local(name.filename)}\n${_s.keepForeground}'), actions: [
[
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.cancel), child: Text(_s.cancel),
@@ -326,12 +323,13 @@ class _SFTPPageState extends State<SFTPPage> {
void delete(BuildContext context, SftpName file) { void delete(BuildContext context, SftpName file) {
context.pop(); context.pop();
final isDir = file.attr.isDirectory; final isDir = file.attr.isDirectory;
final dirText = isDir ? '\n${_s.sureDirEmpty}' : '';
final text = '${_s.sureDelete(file.filename)}$dirText';
final child = Text(text);
showRoundDialog( showRoundDialog(
context, context: context,
_s.attention, child: child,
Text( actions: [
'${_s.sureDelete(file.filename)}${isDir ? '\n${_s.sureDirEmpty}' : ''}'),
[
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.cancel), child: Text(_s.cancel),
@@ -339,8 +337,11 @@ class _SFTPPageState extends State<SFTPPage> {
TextButton( TextButton(
onPressed: () async { onPressed: () async {
context.pop(); context.pop();
showRoundDialog(context, 'Waiting...', centerSizedLoading, [], showRoundDialog(
barrierDismiss: false); context: context,
child: centerSizedLoading,
barrierDismiss: false,
);
final remotePath = _getRemotePath(file); final remotePath = _getRemotePath(file);
try { try {
if (file.attr.isDirectory) { if (file.attr.isDirectory) {
@@ -352,10 +353,10 @@ class _SFTPPageState extends State<SFTPPage> {
} catch (e) { } catch (e) {
context.pop(); context.pop();
showRoundDialog( showRoundDialog(
context, context: context,
_s.attention, title: Text(_s.error),
Text(e.toString()), child: Text(e.toString()),
[ actions: [
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.ok), child: Text(_s.ok),
@@ -379,15 +380,15 @@ class _SFTPPageState extends State<SFTPPage> {
context.pop(); context.pop();
final textController = TextEditingController(); final textController = TextEditingController();
showRoundDialog( showRoundDialog(
context, context: context,
_s.createFolder, title: Text(_s.createFolder),
TextField( child: TextField(
controller: textController, controller: textController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: _s.name, labelText: _s.name,
), ),
), ),
[ actions: [
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.cancel), child: Text(_s.cancel),
@@ -396,10 +397,9 @@ class _SFTPPageState extends State<SFTPPage> {
onPressed: () { onPressed: () {
if (textController.text == '') { if (textController.text == '') {
showRoundDialog( showRoundDialog(
context, context: context,
_s.attention, child: Text(_s.fieldMustNotEmpty),
Text(_s.fieldMustNotEmpty), actions: [
[
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.ok), child: Text(_s.ok),
@@ -426,15 +426,15 @@ class _SFTPPageState extends State<SFTPPage> {
context.pop(); context.pop();
final textController = TextEditingController(); final textController = TextEditingController();
showRoundDialog( showRoundDialog(
context, context: context,
_s.createFile, title: Text(_s.createFile),
TextField( child: TextField(
controller: textController, controller: textController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: _s.name, labelText: _s.name,
), ),
), ),
[ actions: [
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.cancel), child: Text(_s.cancel),
@@ -443,10 +443,9 @@ class _SFTPPageState extends State<SFTPPage> {
onPressed: () async { onPressed: () async {
if (textController.text == '') { if (textController.text == '') {
showRoundDialog( showRoundDialog(
context, context: context,
_s.attention, child: Text(_s.fieldMustNotEmpty),
Text(_s.fieldMustNotEmpty), actions: [
[
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.ok), child: Text(_s.ok),
@@ -474,24 +473,23 @@ class _SFTPPageState extends State<SFTPPage> {
context.pop(); context.pop();
final textController = TextEditingController(); final textController = TextEditingController();
showRoundDialog( showRoundDialog(
context, context: context,
_s.rename, title: Text(_s.rename),
TextField( child: TextField(
controller: textController, controller: textController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: _s.name, labelText: _s.name,
), ),
), ),
[ actions: [
TextButton(onPressed: () => context.pop(), child: Text(_s.cancel)), TextButton(onPressed: () => context.pop(), child: Text(_s.cancel)),
TextButton( TextButton(
onPressed: () async { onPressed: () async {
if (textController.text == '') { if (textController.text == '') {
showRoundDialog( showRoundDialog(
context, context: context,
_s.attention, child: Text(_s.fieldMustNotEmpty),
Text(_s.fieldMustNotEmpty), actions: [
[
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.ok), child: Text(_s.ok),
@@ -540,10 +538,10 @@ class _SFTPPageState extends State<SFTPPage> {
} }
} catch (e) { } catch (e) {
await showRoundDialog( await showRoundDialog(
context, context: context,
_s.error, title: Text(_s.error),
Text(e.toString()), child: Text(e.toString()),
[ actions: [
TextButton( TextButton(
onPressed: () => context.pop(), onPressed: () => context.pop(),
child: Text(_s.ok), child: Text(_s.ok),

View File

@@ -13,7 +13,8 @@ class CardDialog extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertDialog( return AlertDialog(
contentPadding: padding ?? const EdgeInsets.fromLTRB(24, 17, 24, 7), contentPadding: padding,
actionsPadding: const EdgeInsets.all(7),
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20.0)), borderRadius: BorderRadius.all(Radius.circular(20.0)),
), ),

View File

@@ -1,13 +1,18 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
Widget buildPicker(List<Widget> items, Function(int idx) onSelected) { Widget buildPicker(
List<Widget> items,
Function(int idx) onSelected, {
double height = 157,
}) {
final pad = (height - 37) / 2;
return SizedBox( return SizedBox(
height: 111, height: height,
child: Stack( child: Stack(
children: [ children: [
Positioned( Positioned(
top: 36, top: pad,
bottom: 36, bottom: pad,
left: 0, left: 0,
right: 0, right: 0,
child: Container( child: Container(
@@ -20,7 +25,7 @@ Widget buildPicker(List<Widget> items, Function(int idx) onSelected) {
), ),
ListWheelScrollView.useDelegate( ListWheelScrollView.useDelegate(
itemExtent: 37, itemExtent: 37,
diameterRatio: 1.2, diameterRatio: 2.7,
controller: FixedExtentScrollController(initialItem: 0), controller: FixedExtentScrollController(initialItem: 0),
onSelectedItemChanged: (idx) => onSelected(idx), onSelectedItemChanged: (idx) => onSelected(idx),
physics: const FixedExtentScrollPhysics(), physics: const FixedExtentScrollPhysics(),

View File

@@ -1,16 +1,20 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class RoundRectCard extends StatelessWidget { class RoundRectCard extends StatelessWidget {
const RoundRectCard(this.child, {Key? key}) : super(key: key); const RoundRectCard(this.child, {Key? key, this.color}) : super(key: key);
final Widget child; final Widget child;
final Color? color;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Card( return Card(
key: key,
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
color: color,
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(17))), borderRadius: BorderRadius.all(Radius.circular(17)),
),
child: child, child: child,
); );
} }