i18n support

This commit is contained in:
Junyuan Feng
2022-05-05 16:07:55 +08:00
parent f9aa3b1728
commit 29e3ee0156
22 changed files with 1601 additions and 147 deletions

View File

@@ -4,6 +4,7 @@ import 'package:clipboard/clipboard.dart';
import 'package:flutter/material.dart';
import 'package:toolbox/core/utils.dart';
import 'package:toolbox/data/res/color.dart';
import 'package:toolbox/generated/l10n.dart';
import 'package:toolbox/view/widget/input_field.dart';
import 'package:toolbox/view/widget/round_rect_card.dart';
@@ -20,13 +21,8 @@ class _ConvertPageState extends State<ConvertPage>
late TextEditingController _textEditingControllerResult;
late MediaQueryData _media;
late ThemeData _theme;
late S s;
static const List<String> _typeOption = [
'base64 decode',
'base64 encode',
'URL encode',
'URL decode'
];
int _typeOptionIndex = 0;
@override
@@ -41,6 +37,7 @@ class _ConvertPageState extends State<ConvertPage>
super.didChangeDependencies();
_media = MediaQuery.of(context);
_theme = Theme.of(context);
s = S.of(context);
}
@override
@@ -67,7 +64,7 @@ class _ConvertPageState extends State<ConvertPage>
showSnackBar(context, Text('Error: \n$e'));
}
},
tooltip: 'convert',
tooltip: s.convert,
child: const Icon(Icons.send),
),
);
@@ -85,7 +82,7 @@ class _ConvertPageState extends State<ConvertPage>
case 3:
return Uri.decodeFull(text);
default:
return 'Unknown Convert Method';
return s.unkownConvertMode;
}
}
@@ -97,6 +94,14 @@ class _ConvertPageState extends State<ConvertPage>
}
Widget _buildTypeOption() {
final decode = s.decode;
final encode = s.encode;
final List<String> _typeOption = [
'Base64 $decode',
'Base64 $encode',
'URL $encode',
'URL $decode'
];
return RoundRectCard(
ExpansionTile(
tilePadding: const EdgeInsets.only(left: 7, right: 27),
@@ -106,7 +111,7 @@ class _ConvertPageState extends State<ConvertPage>
TextButton(
style: ButtonStyle(
foregroundColor: MaterialStateProperty.all(primaryColor)),
child: const Icon(Icons.change_circle),
child: Icon(Icons.change_circle, semanticLabel: s.upsideDown),
onPressed: () {
final temp = _textEditingController.text;
_textEditingController.text = _textEditingControllerResult.text;
@@ -116,7 +121,7 @@ class _ConvertPageState extends State<ConvertPage>
TextButton(
style: ButtonStyle(
foregroundColor: MaterialStateProperty.all(primaryColor)),
child: const Icon(Icons.copy),
child: Icon(Icons.copy, semanticLabel: s.copy),
onPressed: () => FlutterClipboard.copy(
_textEditingControllerResult.text == ''
? ' '
@@ -137,11 +142,11 @@ class _ConvertPageState extends State<ConvertPage>
fontSize: 16.0,
fontWeight: FontWeight.w500,
color: primaryColor)),
const Text(
'Current Mode',
Text(
s.currentMode,
textScaleFactor: 1.0,
textAlign: TextAlign.right,
style: TextStyle(fontSize: 9.0, color: Colors.grey),
style: const TextStyle(fontSize: 9.0, color: Colors.grey),
)
],
),

View File

@@ -11,10 +11,12 @@ import 'package:toolbox/data/model/app/navigation_item.dart';
import 'package:toolbox/data/provider/server.dart';
import 'package:toolbox/data/res/build_data.dart';
import 'package:toolbox/data/res/color.dart';
import 'package:toolbox/data/res/font_style.dart';
import 'package:toolbox/data/res/icon/common.dart';
import 'package:toolbox/data/res/tab.dart';
import 'package:toolbox/data/res/url.dart';
import 'package:toolbox/data/store/setting.dart';
import 'package:toolbox/generated/l10n.dart';
import 'package:toolbox/locator.dart';
import 'package:toolbox/view/page/convert.dart';
import 'package:toolbox/view/page/debug.dart';
@@ -44,6 +46,7 @@ class _MyHomePageState extends State<MyHomePage>
late final AdvancedDrawerController _advancedDrawerController;
late int _selectIndex;
late double _width;
late S s;
@override
void initState() {
@@ -58,6 +61,7 @@ class _MyHomePageState extends State<MyHomePage>
@override
void didChangeDependencies() {
super.didChangeDependencies();
s = S.of(context);
_width = MediaQuery.of(context).size.width;
}
@@ -115,11 +119,11 @@ class _MyHomePageState extends State<MyHomePage>
drawer: _buildDrawer(),
child: Scaffold(
appBar: AppBar(
title: Text(tabItems[_selectIndex].title),
title: Text(tabTitleName(context, _selectIndex), style: size18),
actions: [
IconButton(
icon: const Icon(Icons.developer_mode, size: 23),
tooltip: 'Debug',
tooltip: s.debug,
onPressed: () =>
AppRoute(const DebugPage(), 'Debug Page').go(context),
),
@@ -171,7 +175,7 @@ class _MyHomePageState extends State<MyHomePage>
borderRadius: const BorderRadius.all(Radius.circular(50))),
child: IconButton(
icon: Icon(item.icon),
tooltip: item.title,
tooltip: tabTitleName(context, idx),
splashRadius: width / 3.3,
padding: const EdgeInsets.only(left: 17, right: 17),
onPressed: () {
@@ -218,36 +222,35 @@ class _MyHomePageState extends State<MyHomePage>
children: [
ListTile(
leading: const Icon(Icons.settings),
title: const Text('Setting'),
title: Text(s.setting),
onTap: () =>
AppRoute(const SettingPage(), 'Setting').go(context),
),
ListTile(
leading: const Icon(Icons.vpn_key),
title: const Text('Private Key'),
title: Text(s.privateKey),
onTap: () => AppRoute(
const StoredPrivateKeysPage(), 'private key list')
.go(context),
),
ListTile(
leading: const Icon(Icons.snippet_folder),
title: const Text('Snippet'),
title: Text(s.snippet),
onTap: () => AppRoute(const SnippetListPage(), 'snippet list')
.go(context),
),
AboutListTile(
icon: const Icon(Icons.text_snippet),
child: const Text('Licences'),
child: Text(s.license),
applicationName: BuildData.name,
applicationVersion: _buildVersionStr(),
applicationIcon: _buildIcon(),
aboutBoxChildren: const [
aboutBoxChildren: [
UrlText(
text: '\nMade with ❤️ by $myGithub',
text: s.madeWithLove(myGithub),
replace: 'LollipopKit'),
UrlText(
text:
'\nThanks $rainSunMeGithub for participating in the test.\n\nAll rights reserved.',
text: s.aboutThanks(rainSunMeGithub),
replace: 'RainSunMe',
),
],

View File

@@ -4,6 +4,7 @@ import 'package:toolbox/core/utils.dart';
import 'package:toolbox/data/model/server/ping_result.dart';
import 'package:toolbox/data/provider/server.dart';
import 'package:toolbox/data/res/color.dart';
import 'package:toolbox/generated/l10n.dart';
import 'package:toolbox/locator.dart';
import 'package:toolbox/view/widget/input_field.dart';
import 'package:toolbox/view/widget/round_rect_card.dart';
@@ -20,6 +21,10 @@ class _PingPageState extends State<PingPage>
late TextEditingController _textEditingController;
late MediaQueryData _media;
final List<PingResult> _results = [];
late S s;
static const summaryTextStyle = TextStyle(
fontSize: 12,
);
@override
void initState() {
@@ -31,6 +36,7 @@ class _PingPageState extends State<PingPage>
void didChangeDependencies() {
super.didChangeDependencies();
_media = MediaQuery.of(context);
s = S.of(context);
}
@override
@@ -62,37 +68,39 @@ class _PingPageState extends State<PingPage>
}
Widget _buildResultItem(PingResult result) {
final unknown = s.unknown;
final ms = s.ms;
return RoundRectCard(ListTile(
contentPadding: const EdgeInsets.symmetric(vertical: 7, horizontal: 17),
title: Text(result.serverName,
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold, color: primaryColor)),
subtitle: Text(_buildPingSummary(result)),
subtitle: Text(_buildPingSummary(result, unknown, ms), style: summaryTextStyle,),
trailing: Text(
'Avg: ' +
(result.statistic?.avg?.toStringAsFixed(2) ?? 'unkown') +
' ms',
s.pingAvg +
(result.statistic?.avg?.toStringAsFixed(2) ?? s.unknown) +
' $ms',
style: TextStyle(fontSize: 14, color: primaryColor)),
));
}
String _buildPingSummary(PingResult result) {
final ip = result.ip ?? 'unkown';
String _buildPingSummary(PingResult result, String unknown, String ms) {
final ip = result.ip ?? unknown;
if (result.results == null || result.results!.isEmpty) {
return '$ip - no results';
return '$ip - ${s.noResult}';
}
final ttl = result.results?.first.ttl ?? 'unkown';
final loss = result.statistic?.loss ?? 'unkown';
final min = result.statistic?.min ?? 'unkown';
final max = result.statistic?.max ?? 'unkown';
return '$ip\nttl: $ttl, loss: $loss%\nmin: $min ms, max: $max ms';
final ttl = result.results?.first.ttl ?? unknown;
final loss = result.statistic?.loss ?? unknown;
final min = result.statistic?.min ?? unknown;
final max = result.statistic?.max ?? unknown;
return '$ip\n${s.ttl}: $ttl, ${s.loss}: $loss%\n${s.min}: $min $ms, ${s.max}: $max $ms';
}
Future<void> doPing() async {
_results.clear();
final target = _textEditingController.text.trim();
if (target.isEmpty) {
showSnackBar(context, const Text('Please input a target'));
showSnackBar(context, Text(s.pingInputIP));
return;
}
@@ -119,12 +127,12 @@ class _PingPageState extends State<PingPage>
style: ButtonStyle(
foregroundColor: MaterialStateProperty.all(primaryColor)),
child: Row(
children: const [
Icon(Icons.delete),
SizedBox(
children: [
const Icon(Icons.delete),
const SizedBox(
width: 7,
),
Text('Clear')
Text(s.clear)
],
),
onPressed: () {
@@ -136,12 +144,12 @@ class _PingPageState extends State<PingPage>
style: ButtonStyle(
foregroundColor: MaterialStateProperty.all(primaryColor)),
child: Row(
children: const [
Icon(Icons.play_arrow),
SizedBox(
children: [
const Icon(Icons.play_arrow),
const SizedBox(
width: 7,
),
Text('Start')
Text(s.start)
],
),
onPressed: () {

View File

@@ -5,6 +5,8 @@ import 'package:flutter/material.dart';
import 'package:toolbox/core/utils.dart';
import 'package:toolbox/data/model/server/private_key_info.dart';
import 'package:toolbox/data/provider/private_key.dart';
import 'package:toolbox/data/res/font_style.dart';
import 'package:toolbox/generated/l10n.dart';
import 'package:toolbox/locator.dart';
import 'package:toolbox/view/widget/input_decoration.dart';
@@ -25,6 +27,7 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage>
late PrivateKeyProvider _provider;
late Widget loading;
late S s;
@override
void initState() {
@@ -33,12 +36,19 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage>
loading = const SizedBox();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
s = S.of(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Edit'), actions: [
appBar: AppBar(title: Text(s.edit, style: size18), actions: [
widget.info != null
? IconButton(
tooltip: s.delete,
onPressed: () {
_provider.delInfo(widget.info!);
Navigator.of(context).pop();
@@ -52,7 +62,7 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage>
TextField(
controller: nameController,
keyboardType: TextInputType.text,
decoration: buildDecoration('Name', icon: Icons.info),
decoration: buildDecoration(s.name, icon: Icons.info),
),
TextField(
controller: keyController,
@@ -61,14 +71,14 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage>
maxLines: 10,
keyboardType: TextInputType.text,
enableSuggestions: false,
decoration: buildDecoration('Private Key', icon: Icons.vpn_key),
decoration: buildDecoration(s.privateKey, icon: Icons.vpn_key),
),
TextField(
controller: pwdController,
autocorrect: false,
keyboardType: TextInputType.text,
obscureText: true,
decoration: buildDecoration('Password', icon: Icons.password),
decoration: buildDecoration(s.pwd, icon: Icons.password),
),
SizedBox(height: MediaQuery.of(context).size.height * 0.1),
loading
@@ -76,13 +86,14 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage>
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.save),
tooltip: s.save,
onPressed: () async {
final name = nameController.text;
final key = keyController.text;
final pwd = pwdController.text;
if (name.isEmpty || key.isEmpty) {
showSnackBar(
context, const Text('Name and Key must not be empty.'));
context, Text(s.fieldMustNotEmpty));
return;
}
FocusScope.of(context).unfocus();

View File

@@ -3,7 +3,9 @@ import 'package:provider/provider.dart';
import 'package:toolbox/core/route.dart';
import 'package:toolbox/data/provider/private_key.dart';
import 'package:toolbox/data/res/color.dart';
import 'package:toolbox/data/res/font_style.dart';
import 'package:toolbox/data/res/padding.dart';
import 'package:toolbox/generated/l10n.dart';
import 'package:toolbox/view/page/private_key/edit.dart';
import 'package:toolbox/view/widget/round_rect_card.dart';
@@ -16,11 +18,19 @@ class StoredPrivateKeysPage extends StatefulWidget {
class _PrivateKeyListState extends State<StoredPrivateKeysPage> {
final _textStyle = TextStyle(color: primaryColor);
late S s;
@override
void didChangeDependencies() {
super.didChangeDependencies();
s = S.of(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Private Keys'),
title: Text(s.privateKey, style: size18),
),
body: Consumer<PrivateKeyProvider>(
builder: (_, key, __) {
@@ -46,14 +56,14 @@ class _PrivateKeyListState extends State<StoredPrivateKeysPage> {
'private key edit page')
.go(context),
child: Text(
'Edit',
s.edit,
style: _textStyle,
))
],
),
));
})
: const Center(child: Text('No saved private keys.'));
: Center(child: Text(s.noSavedPrivateKey));
},
),
floatingActionButton: FloatingActionButton(

View File

@@ -6,6 +6,7 @@ import 'package:toolbox/data/model/server/server.dart';
import 'package:toolbox/data/model/server/server_status.dart';
import 'package:toolbox/data/provider/server.dart';
import 'package:toolbox/data/res/color.dart';
import 'package:toolbox/data/res/font_style.dart';
import 'package:toolbox/data/res/icon/linux_icons.dart';
import 'package:toolbox/data/res/padding.dart';
import 'package:toolbox/view/widget/round_rect_card.dart';
@@ -43,7 +44,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
Widget _buildMainPage(ServerInfo si) {
return Scaffold(
appBar: AppBar(
title: Text(si.info.name),
title: Text(si.info.name, style: size18),
),
body: ListView(
padding: const EdgeInsets.all(13),

View File

@@ -8,7 +8,9 @@ import 'package:toolbox/data/model/server/server_private_info.dart';
import 'package:toolbox/data/provider/private_key.dart';
import 'package:toolbox/data/provider/server.dart';
import 'package:toolbox/data/res/color.dart';
import 'package:toolbox/data/res/font_style.dart';
import 'package:toolbox/data/store/private_key.dart';
import 'package:toolbox/generated/l10n.dart';
import 'package:toolbox/locator.dart';
import 'package:toolbox/view/page/private_key/edit.dart';
import 'package:toolbox/view/widget/input_decoration.dart';
@@ -32,6 +34,8 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
late ServerProvider _serverProvider;
late S s;
bool usePublicKey = false;
int _pubKeyIndex = -1;
@@ -43,10 +47,16 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
_serverProvider = locator<ServerProvider>();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
s = S.of(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Edit'), actions: [
appBar: AppBar(title: Text(s.edit, style: size18), actions: [
widget.spi != null
? IconButton(
onPressed: () {
@@ -54,7 +64,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
context,
'Attention',
Text(
'Are you sure to delete server [${widget.spi!.name}]'),
s.sureToDeleteServer(widget.spi!.name)),
[
TextButton(
onPressed: () {
@@ -62,13 +72,13 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
Navigator.of(context).pop();
Navigator.of(context).pop();
},
child: const Text(
'Yes',
style: TextStyle(color: Colors.red),
child: Text(
s.ok,
style: const TextStyle(color: Colors.red),
)),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('No'))
child: Text(s.cancel))
]);
},
icon: const Icon(Icons.delete))
@@ -84,20 +94,20 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
controller: nameController,
keyboardType: TextInputType.text,
decoration:
buildDecoration('Name', icon: Icons.info, hint: 'Example'),
buildDecoration(s.name, icon: Icons.info, hint: s.exampleName),
),
TextField(
controller: ipController,
keyboardType: TextInputType.text,
autocorrect: false,
enableSuggestions: false,
decoration: buildDecoration('Host',
decoration: buildDecoration(s.host,
icon: Icons.storage, hint: 'example.com'),
),
TextField(
controller: portController,
keyboardType: TextInputType.number,
decoration: buildDecoration('Port',
decoration: buildDecoration(s.port,
icon: Icons.format_list_numbered, hint: '22'),
),
TextField(
@@ -105,13 +115,13 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
keyboardType: TextInputType.text,
autocorrect: false,
enableSuggestions: false,
decoration: buildDecoration('User',
decoration: buildDecoration(s.user,
icon: Icons.account_box, hint: 'root'),
),
const SizedBox(height: 7),
Row(
children: [
const Text('Key Auth'),
Text(s.keyAuth),
Switch(
value: usePublicKey,
onChanged: (val) => setState(() => usePublicKey = val)),
@@ -122,8 +132,8 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
controller: passwordController,
obscureText: true,
keyboardType: TextInputType.text,
decoration: buildDecoration('Pwd',
icon: Icons.password, hint: 'Password'),
decoration: buildDecoration(s.pwd,
icon: Icons.password, hint: s.pwd),
onSubmitted: (_) => {},
)
: const SizedBox(),
@@ -143,7 +153,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
)
.toList();
tiles.add(ListTile(
title: const Text('Add a Private Key'),
title: Text(s.addPrivateKey),
contentPadding: EdgeInsets.zero,
trailing: IconButton(
icon: const Icon(Icons.add),
@@ -157,9 +167,9 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
iconColor: primaryColor,
tilePadding: EdgeInsets.zero,
childrenPadding: EdgeInsets.zero,
title: const Text(
'Choose Key',
style: TextStyle(fontSize: 14),
title: Text(
s.choosePrivateKey,
style: const TextStyle(fontSize: 14),
),
children: tiles,
);
@@ -172,15 +182,15 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
child: const Icon(Icons.send),
onPressed: () {
if (ipController.text == '') {
showSnackBar(context, const Text('Please enter host.'));
showSnackBar(context, Text(s.plzEnterHost));
return;
}
if (!usePublicKey && passwordController.text == '') {
showSnackBar(context, const Text('Please enter password.'));
showSnackBar(context, Text(s.plzEnterPwd));
return;
}
if (usePublicKey && _pubKeyIndex == -1) {
showSnackBar(context, const Text('Please select a private key.'));
showSnackBar(context, Text(s.plzSelectKey));
return;
}
if (usernameController.text == '') {

View File

@@ -15,6 +15,7 @@ import 'package:toolbox/data/model/server/server_status.dart';
import 'package:toolbox/data/provider/server.dart';
import 'package:toolbox/data/res/color.dart';
import 'package:toolbox/data/store/setting.dart';
import 'package:toolbox/generated/l10n.dart';
import 'package:toolbox/locator.dart';
import 'package:toolbox/view/page/apt.dart';
import 'package:toolbox/view/page/docker.dart';
@@ -39,6 +40,7 @@ class _ServerPageState extends State<ServerPage>
late RefreshController _refreshController;
late ServerProvider _serverProvider;
late S s;
@override
void initState() {
@@ -53,6 +55,7 @@ class _ServerPageState extends State<ServerPage>
_media = MediaQuery.of(context);
_theme = Theme.of(context);
_primaryColor = primaryColor;
s = S.of(context);
}
@override
@@ -62,9 +65,9 @@ class _ServerPageState extends State<ServerPage>
locator<SettingStore>().serverStatusUpdateInterval.fetch() != 0;
final child = Consumer<ServerProvider>(builder: (_, pro, __) {
if (pro.servers.isEmpty) {
return const Center(
return Center(
child: Text(
'There is no server.\nClick the fab to add one.',
s.serverTabEmpty,
textAlign: TextAlign.center,
),
);
@@ -99,7 +102,7 @@ class _ServerPageState extends State<ServerPage>
onPressed: () =>
AppRoute(const ServerEditPage(), 'Add server info page')
.go(context),
tooltip: 'add a server',
tooltip: s.addAServer,
heroTag: 'server page fab',
child: const Icon(Icons.add),
),
@@ -302,7 +305,7 @@ class _ServerPageState extends State<ServerPage>
case ServerConnectionState.connected:
if (temp == '') {
if (upTime == '') {
return 'Loading...';
return s.serverTabLoading;
} else {
return upTime;
}
@@ -314,17 +317,17 @@ class _ServerPageState extends State<ServerPage>
}
}
case ServerConnectionState.connecting:
return 'Connecting...';
return s.serverTabConnecting;
case ServerConnectionState.failed:
if (failedInfo == null) {
return 'Failed';
return s.serverTabFailed;
}
if (failedInfo.contains('encypted')) {
return 'Please "save" this private key again.';
return s.serverTabPlzSave;
}
return failedInfo;
default:
return 'Unknown State';
return s.serverTabUnkown;
}
}

View File

@@ -2,13 +2,16 @@ import 'package:flutter/material.dart';
import 'package:flutter_material_color_picker/flutter_material_color_picker.dart';
import 'package:provider/provider.dart';
import 'package:toolbox/core/update.dart';
import 'package:toolbox/core/utils.dart';
import 'package:toolbox/data/provider/app.dart';
import 'package:toolbox/data/provider/server.dart';
import 'package:toolbox/data/res/build_data.dart';
import 'package:toolbox/data/res/color.dart';
import 'package:toolbox/data/res/font_style.dart';
import 'package:toolbox/data/res/padding.dart';
import 'package:toolbox/data/res/tab.dart';
import 'package:toolbox/data/store/setting.dart';
import 'package:toolbox/generated/l10n.dart';
import 'package:toolbox/locator.dart';
import 'package:toolbox/view/widget/round_rect_card.dart';
@@ -29,6 +32,7 @@ class _SettingPageState extends State<SettingPage> {
late final ServerProvider _serverProvider;
late MediaQueryData _media;
late ThemeData _theme;
late S s;
@override
void didChangeDependencies() {
@@ -36,6 +40,7 @@ class _SettingPageState extends State<SettingPage> {
priColor = primaryColor;
_media = MediaQuery.of(context);
_theme = Theme.of(context);
s = S.of(context);
}
@override
@@ -51,7 +56,7 @@ class _SettingPageState extends State<SettingPage> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Setting'),
title: Text(s.setting, style: size18),
),
body: ListView(
padding: const EdgeInsets.all(17),
@@ -70,12 +75,12 @@ class _SettingPageState extends State<SettingPage> {
String display;
if (app.newestBuild != null) {
if (app.newestBuild! > BuildData.build) {
display = 'Found: v1.0.${app.newestBuild}, click to update';
display = s.versionHaveUpdate(app.newestBuild!);
} else {
display = 'Current: v1.0.${BuildData.build}is up to date';
display = s.versionUpdated(BuildData.build);
}
} else {
display = 'Current: v1.0.${BuildData.build}';
display = s.versionUnknownUpdate(BuildData.build);
}
return ListTile(
contentPadding: roundRectCardPadding,
@@ -94,16 +99,16 @@ class _SettingPageState extends State<SettingPage> {
tilePadding: roundRectCardPadding,
childrenPadding: roundRectCardPadding,
textColor: priColor,
title: const Text(
'Server status update interval',
title: Text(
s.updateServerStatusInterval,
style: textStyle,
textAlign: TextAlign.start,
),
subtitle: const Text(
'Will take effect immediately.',
style: TextStyle(color: Colors.grey),
subtitle: Text(
s.willTakEeffectImmediately,
style: const TextStyle(color: Colors.grey, fontSize: 13),
),
trailing: Text('${_intervalValue.toInt()} s'),
trailing: Text('${_intervalValue.toInt()} ${s.second}'),
children: [
Slider(
thumbColor: priColor,
@@ -120,16 +125,16 @@ class _SettingPageState extends State<SettingPage> {
_store.serverStatusUpdateInterval.put(val.toInt());
_serverProvider.startAutoRefresh();
},
label: '${_intervalValue.toInt()} seconds',
label: '${_intervalValue.toInt()} ${s.second}',
divisions: 10,
),
const SizedBox(
height: 3,
),
_intervalValue == 0.0
? const Text(
'You set to 0, will not update automatically.\nYou can pull to refresh manually.',
style: TextStyle(color: Colors.grey),
? Text(
s.updateIntervalEqual0,
style: const TextStyle(color: Colors.grey, fontSize: 12),
textAlign: TextAlign.center,
)
: const SizedBox(),
@@ -156,8 +161,8 @@ class _SettingPageState extends State<SettingPage> {
width: 27,
),
),
title: const Text(
'App primary color',
title: Text(
s.appPrimaryColor,
style: textStyle,
));
}
@@ -186,14 +191,14 @@ class _SettingPageState extends State<SettingPage> {
textColor: priColor,
tilePadding: roundRectCardPadding,
childrenPadding: roundRectCardPadding,
title: const Text(
'Launch page',
title: Text(
s.launchPage,
style: textStyle,
),
trailing: ConstrainedBox(
constraints: BoxConstraints(maxWidth: _media.size.width * 0.35),
child: Text(
tabs[_launchPageIdx],
tabTitleName(context, _launchPageIdx),
style: textStyle,
textAlign: TextAlign.right,
),
@@ -202,7 +207,7 @@ class _SettingPageState extends State<SettingPage> {
.map((e) => ListTile(
contentPadding: EdgeInsets.zero,
title: Text(
e,
tabTitleName(context, tabs.indexOf(e)),
style: TextStyle(
fontSize: 14,
color: _theme.textTheme.bodyText2!.color!.withAlpha(177)),

View File

@@ -3,6 +3,8 @@ import 'package:flutter/material.dart';
import 'package:toolbox/core/utils.dart';
import 'package:toolbox/data/model/server/snippet.dart';
import 'package:toolbox/data/provider/snippet.dart';
import 'package:toolbox/data/res/font_style.dart';
import 'package:toolbox/generated/l10n.dart';
import 'package:toolbox/locator.dart';
import 'package:toolbox/view/widget/input_decoration.dart';
@@ -21,23 +23,31 @@ class _SnippetEditPageState extends State<SnippetEditPage>
final scriptController = TextEditingController();
late SnippetProvider _provider;
late S s;
@override
void initState() {
super.initState();
_provider = locator<SnippetProvider>();
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
s = S.of(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Edit'), actions: [
appBar: AppBar(title: Text(s.edit, style: size18), actions: [
widget.snippet != null
? IconButton(
onPressed: () {
_provider.del(widget.snippet!);
Navigator.of(context).pop();
},
tooltip: s.delete,
icon: const Icon(Icons.delete))
: const SizedBox()
]),
@@ -47,7 +57,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
TextField(
controller: nameController,
keyboardType: TextInputType.text,
decoration: buildDecoration('Name', icon: Icons.info),
decoration: buildDecoration(s.name, icon: Icons.info),
),
TextField(
controller: scriptController,
@@ -56,7 +66,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
maxLines: 10,
keyboardType: TextInputType.text,
enableSuggestions: false,
decoration: buildDecoration('Snippet', icon: Icons.code),
decoration: buildDecoration(s.snippet, icon: Icons.code),
),
],
),
@@ -66,7 +76,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
final name = nameController.text;
final script = scriptController.text;
if (name.isEmpty || script.isEmpty) {
showSnackBar(context, const Text('Two fields must not be empty.'));
showSnackBar(context, Text(s.fieldMustNotEmpty));
return;
}
final snippet = Snippet(name, script);

View File

@@ -8,7 +8,9 @@ import 'package:toolbox/data/model/server/snippet.dart';
import 'package:toolbox/data/provider/server.dart';
import 'package:toolbox/data/provider/snippet.dart';
import 'package:toolbox/data/res/color.dart';
import 'package:toolbox/data/res/font_style.dart';
import 'package:toolbox/data/res/padding.dart';
import 'package:toolbox/generated/l10n.dart';
import 'package:toolbox/locator.dart';
import 'package:toolbox/view/page/snippet/edit.dart';
import 'package:toolbox/view/widget/round_rect_card.dart';
@@ -28,14 +30,24 @@ class _SnippetListPageState extends State<SnippetListPage> {
final _exportFieldController = TextEditingController();
final _textStyle = TextStyle(color: primaryColor);
late S s;
@override
void didChangeDependencies() {
super.didChangeDependencies();
s = S.of(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Snippet List'),
title: Text(s.snippet, style: size18),
actions: [
IconButton(
onPressed: () => _showImportExport(),
tooltip: s.importAndExport,
icon: const Icon(Icons.import_export)),
],
),
@@ -51,17 +63,17 @@ class _SnippetListPageState extends State<SnippetListPage> {
Future<void> _showImportExport() async {
await showRoundDialog(
context,
'Choose',
s.choose,
Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
title: const Text('Import'),
title: Text(s.import),
leading: const Icon(Icons.download),
onTap: () => _showImportDialog(),
),
ListTile(
title: const Text('Export'),
title: Text(s.export),
leading: const Icon(Icons.file_upload),
onTap: () => _showExportDialog(),
),
@@ -75,7 +87,7 @@ class _SnippetListPageState extends State<SnippetListPage> {
_exportFieldController.text = locator<SnippetProvider>().export;
await showRoundDialog(
context,
'Export',
s.export,
TextField(
decoration: const InputDecoration(
labelText: 'JSON',
@@ -85,7 +97,7 @@ class _SnippetListPageState extends State<SnippetListPage> {
),
[
TextButton(
child: const Text('OK'),
child: Text(s.ok),
onPressed: () => Navigator.pop(context),
),
]);
@@ -95,10 +107,10 @@ class _SnippetListPageState extends State<SnippetListPage> {
Navigator.of(context).pop();
await showRoundDialog(
context,
'Import',
s.import,
TextField(
decoration: const InputDecoration(
labelText: 'Url or JSON',
decoration: InputDecoration(
labelText: s.urlOrJson,
),
maxLines: 2,
controller: _importFieldController,
@@ -106,18 +118,18 @@ class _SnippetListPageState extends State<SnippetListPage> {
[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel')),
child: Text(s.cancel)),
TextButton(
onPressed: () async =>
await _import(_importFieldController.text.trim()),
child: const Text('GO'),
child: Text('GO'),
)
]);
}
Future<void> _import(String text) async {
if (text.isEmpty) {
showSnackBar(context, const Text('field can not be empty'));
showSnackBar(context, Text(s.fieldMustNotEmpty));
return;
}
final snippetProvider = locator<SnippetProvider>();
@@ -125,7 +137,7 @@ class _SnippetListPageState extends State<SnippetListPage> {
final resp = await Dio().get(text);
if (resp.statusCode != 200) {
showSnackBar(
context, Text('request failed, status code: ${resp.statusCode}'));
context, Text(s.httpFailedWithCode(resp.statusCode ?? '-1')));
return;
}
for (final snippet in getSnippetList(resp.data)) {
@@ -166,7 +178,7 @@ class _SnippetListPageState extends State<SnippetListPage> {
'snippet edit page')
.go(context),
child: Text(
'Edit',
s.edit,
style: _textStyle,
)),
TextButton(
@@ -179,7 +191,7 @@ class _SnippetListPageState extends State<SnippetListPage> {
run(context, snippet);
},
child: Text(
'Run',
s.run,
style: _textStyle,
))
])
@@ -187,16 +199,16 @@ class _SnippetListPageState extends State<SnippetListPage> {
),
));
})
: const Center(child: Text('No saved snippets.'));
: Center(child: Text(s.noSavedSnippet));
},
);
}
void _showRunDialog(Snippet snippet) {
showRoundDialog(context, 'Choose destination',
showRoundDialog(context, s.chooseDestination,
Consumer<ServerProvider>(builder: (_, provider, __) {
if (provider.servers.isEmpty) {
return const Text('No server available');
return Text(s.noServerAvailable);
}
_selectedIndex = provider.servers.first.info;
return SizedBox(
@@ -235,10 +247,10 @@ class _SnippetListPageState extends State<SnippetListPage> {
}), [
TextButton(
onPressed: () async => run(context, snippet),
child: const Text('Run')),
child: Text(s.run)),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel')),
child: Text(s.cancel)),
]);
}
@@ -246,11 +258,11 @@ class _SnippetListPageState extends State<SnippetListPage> {
final result = await locator<ServerProvider>()
.runSnippet(widget.spi ?? _selectedIndex, snippet);
if (result != null) {
showRoundDialog(context, 'Result',
showRoundDialog(context, s.result,
Text(result, style: const TextStyle(fontSize: 13)), [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Close'))
child: Text(s.close))
]);
}
}