diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4fff412..7e46129 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,14 +1,18 @@
## Unreleased
### Added
- Full Material 3 Support
+- add pure dark background option
+- add use gridview option
### Changed
-- \[BREAK\]
+- \[BREAK CHANGE\] ⚠️ App signature changed. Uninstall old app and install new one. (REALLY SORRY FOR THIS CHANGE)
- app tile long press to open context menu
+- all tools use monochrome icons
### Fixed
- fix search view not hide when scroll
- fix searchView title
+- use wrong mono font
## 2.0.1-dev+37 - 2023-11-21
### Added
diff --git a/assets/fonts/NotoSansMono-Regular.ttf b/assets/fonts/NotoSansMono-Regular.ttf
deleted file mode 100644
index ff9389a..0000000
Binary files a/assets/fonts/NotoSansMono-Regular.ttf and /dev/null differ
diff --git a/assets/fonts/OFL.txt b/assets/fonts/OFL.txt
deleted file mode 100644
index 0e6dea2..0000000
--- a/assets/fonts/OFL.txt
+++ /dev/null
@@ -1,93 +0,0 @@
-Copyright 2022 The Noto Project Authors (https://github.com/notofonts/latin-greek-cyrillic)
-
-This Font Software is licensed under the SIL Open Font License, Version 1.1.
-This license is copied below, and is also available with a FAQ at:
-http://scripts.sil.org/OFL
-
-
------------------------------------------------------------
-SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
------------------------------------------------------------
-
-PREAMBLE
-The goals of the Open Font License (OFL) are to stimulate worldwide
-development of collaborative font projects, to support the font creation
-efforts of academic and linguistic communities, and to provide a free and
-open framework in which fonts may be shared and improved in partnership
-with others.
-
-The OFL allows the licensed fonts to be used, studied, modified and
-redistributed freely as long as they are not sold by themselves. The
-fonts, including any derivative works, can be bundled, embedded,
-redistributed and/or sold with any software provided that any reserved
-names are not used by derivative works. The fonts and derivatives,
-however, cannot be released under any other type of license. The
-requirement for fonts to remain under this license does not apply
-to any document created using the fonts or their derivatives.
-
-DEFINITIONS
-"Font Software" refers to the set of files released by the Copyright
-Holder(s) under this license and clearly marked as such. This may
-include source files, build scripts and documentation.
-
-"Reserved Font Name" refers to any names specified as such after the
-copyright statement(s).
-
-"Original Version" refers to the collection of Font Software components as
-distributed by the Copyright Holder(s).
-
-"Modified Version" refers to any derivative made by adding to, deleting,
-or substituting -- in part or in whole -- any of the components of the
-Original Version, by changing formats or by porting the Font Software to a
-new environment.
-
-"Author" refers to any designer, engineer, programmer, technical
-writer or other person who contributed to the Font Software.
-
-PERMISSION & CONDITIONS
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of the Font Software, to use, study, copy, merge, embed, modify,
-redistribute, and sell modified and unmodified copies of the Font
-Software, subject to the following conditions:
-
-1) Neither the Font Software nor any of its individual components,
-in Original or Modified Versions, may be sold by itself.
-
-2) Original or Modified Versions of the Font Software may be bundled,
-redistributed and/or sold with any software, provided that each copy
-contains the above copyright notice and this license. These can be
-included either as stand-alone text files, human-readable headers or
-in the appropriate machine-readable metadata fields within text or
-binary files as long as those fields can be easily viewed by the user.
-
-3) No Modified Version of the Font Software may use the Reserved Font
-Name(s) unless explicit written permission is granted by the corresponding
-Copyright Holder. This restriction only applies to the primary font name as
-presented to the users.
-
-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
-Software shall not be used to promote, endorse or advertise any
-Modified Version, except to acknowledge the contribution(s) of the
-Copyright Holder(s) and the Author(s) or with their explicit written
-permission.
-
-5) The Font Software, modified or unmodified, in part or in whole,
-must be distributed entirely under this license, and must not be
-distributed under any other license. The requirement for fonts to
-remain under this license does not apply to any document created
-using the Font Software.
-
-TERMINATION
-This license becomes null and void if any of the above conditions are
-not met.
-
-DISCLAIMER
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
-OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
-COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
-DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
-OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/assets/icons/dart.svg b/assets/icons/dart.svg
index b9ddcdf..c49b354 100644
--- a/assets/icons/dart.svg
+++ b/assets/icons/dart.svg
@@ -1,23 +1 @@
-
-
-
+
\ No newline at end of file
diff --git a/assets/icons/js.svg b/assets/icons/js.svg
index 64b2440..8db86fd 100644
--- a/assets/icons/js.svg
+++ b/assets/icons/js.svg
@@ -1,31 +1 @@
-
+
\ No newline at end of file
diff --git a/assets/icons/json.svg b/assets/icons/json.svg
index fca9b87..12682fe 100644
--- a/assets/icons/json.svg
+++ b/assets/icons/json.svg
@@ -1,104 +1 @@
-
-
-
-
+
\ No newline at end of file
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index b398d1b..3dd2ce8 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -26,6 +26,10 @@
$(FLUTTER_BUILD_NUMBER)
LSRequiresIPhoneOS
+ NSPhotoLibraryUsageDescription
+ Request Photo Library
+ NSCameraUsageDescription
+ Request Camera
UIApplicationSupportsIndirectInputEvents
UILaunchStoryboardName
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb
index 7cf4610..c64d2f9 100644
--- a/lib/l10n/app_en.arb
+++ b/lib/l10n/app_en.arb
@@ -20,6 +20,7 @@
"colorPrimary": "Primary",
"colorString": "Color String",
"colorWheel": "Wheel",
+ "commonConverter": "Common Converter",
"commonInfo": "Common Info",
"compress": "Compress",
"computerName": "Computer Name",
@@ -75,6 +76,7 @@
"generatorUUID": "UUID Generator",
"generators": "Generators",
"github": "github",
+ "gridLayout": "Use Grid Layout",
"hashAlgorithm": "Hash Algorithm",
"hashHMAC": "HMAC",
"hashHMACDes": "Keyed-hash message authentication code",
@@ -159,6 +161,7 @@
"previousMonth": "Previous Month",
"previousSecond": "Previous Second",
"previousYear": "Previous Year",
+ "pureBlack": "Use Pure Black Background",
"pythonDictFormatter": "Python Dict formatter",
"qrAutoVersion": "Auto",
"qrCodeTool": "QR Code Tool",
diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb
index 7ca677b..e5c79c9 100644
--- a/lib/l10n/app_ja.arb
+++ b/lib/l10n/app_ja.arb
@@ -2,6 +2,7 @@
"about": "についてです",
"absoluteLengthConverter": "絶対長変換ツールです",
"activeCPUs": "アクティブなCPU",
+ "addFavorite": "お気に入りに追加",
"allApps": "あらゆる応用",
"allTools": "あらゆるツール",
"appName": "Alga",
@@ -11,8 +12,15 @@
"clear": "クリアです",
"cleared": "クリアしました",
"clickToCopy": "クリックしてコピーする",
+ "colorAccent": "二次色",
+ "colorBoth": "Both",
+ "colorBw": "黒 & 白",
"colorConverter": "色変換ツールです",
+ "colorCustom": "カスタム",
+ "colorPrimary": "メインカラー",
"colorString": "色文字列",
+ "colorWheel": "Wheel",
+ "commonConverter": "Common Converter",
"commonInfo": "共通情報",
"compress": "圧縮です",
"computerName": "デバイス名",
@@ -39,7 +47,9 @@
"deviceArch": "CPUアーキテクチャ",
"deviceInfo": "デバイス情報",
"deviceModel": "装置の型番です",
+ "emptyValue": "Empty content",
"encode": "エンコードです",
+ "encoded": "Encoded",
"encoderDecoderApp": "コード化と復号化",
"encoderDecoderBase64": "Base64符号化/復号化ツール",
"encoderDecoderGzip": "GZipコード/デコードツール",
@@ -50,6 +60,8 @@
"favorite": "コレクション",
"followSystem": "追従システム",
"format": "フォーマット",
+ "formatException": "Format Error",
+ "formatTable": "Foramt Table",
"formattedDateString": "フォーマットされた日付",
"formatterApp": "フォーマット",
"formatterDart": "Dartフォーマット",
@@ -69,6 +81,7 @@
"hashHMACDes": "鍵に基づくメッセージ識別認証ハッシュ",
"hashSM3": "国密SM3ハッシュ",
"hashSecretkey": "鍵",
+ "help": "Help",
"hexdecimal": "16進法",
"hostName": "ホストネーム",
"hypens": "ハイフン",
@@ -124,6 +137,7 @@
"octal": "8進法",
"operatingSystem": "オペレーティングシステム",
"operatingSystemVersion": "osバージョン",
+ "original": "Original",
"osRelease": "os版",
"output": "アウトプットです",
"parsedDate": "解析の日付",
@@ -160,6 +174,7 @@
"regexText": "テキスト",
"regexUnicode": "Unicode",
"regularExpression": "正規表現",
+ "removeFavorite": "Remove from Favorites",
"sassCssGenerator": "sass/cssジェネレータ",
"sassResult": "出力",
"sassSource": "ソースコード",
diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb
index f0fd2f1..123c19a 100644
--- a/lib/l10n/app_zh.arb
+++ b/lib/l10n/app_zh.arb
@@ -12,8 +12,15 @@
"clear": "清除",
"cleared": "已清除",
"clickToCopy": "点击复制",
+ "colorAccent": "次色调",
+ "colorBoth": "Both",
+ "colorBw": "黑 & 白",
"colorConverter": "颜色转换工具",
+ "colorCustom": "自定义",
+ "colorPrimary": "主色调",
"colorString": "颜色字符串",
+ "colorWheel": "滑轮",
+ "commonConverter": "通用转换工具",
"commonInfo": "通用信息",
"compress": "压缩",
"computerName": "设备名",
@@ -54,6 +61,7 @@
"followSystem": "跟随系统",
"format": "格式化",
"formatException": "格式错误",
+ "formatTable": "支持的格式化",
"formattedDateString": "格式化的日期",
"formatterApp": "格式化",
"formatterDart": "Dart 格式化",
diff --git a/lib/l10n/l10n.dart b/lib/l10n/l10n.dart
index 97ca556..3cff36b 100644
--- a/lib/l10n/l10n.dart
+++ b/lib/l10n/l10n.dart
@@ -35,3 +35,18 @@ extension L10nX on BuildContext {
MaterialLocalizations get mtr => MaterialLocalizations.of(this);
}
+
+extension LocaleExt on Locale? {
+ String getName(BuildContext context) {
+ switch (this) {
+ case const Locale('zh', null):
+ return '简体中文';
+ case const Locale('en', null):
+ return 'English';
+ case const Locale('ja', null):
+ return '日本語';
+ default:
+ return context.tr.followSystem;
+ }
+ }
+}
diff --git a/lib/l10n/untranslated/desired.json b/lib/l10n/untranslated/desired.json
index 000416f..9b1ee00 100644
--- a/lib/l10n/untranslated/desired.json
+++ b/lib/l10n/untranslated/desired.json
@@ -1,28 +1,11 @@
{
"ja": [
- "addFavorite",
- "colorAccent",
- "colorBoth",
- "colorBw",
- "colorCustom",
- "colorPrimary",
- "colorWheel",
- "emptyValue",
- "encoded",
- "formatException",
- "formatTable",
- "help",
- "original",
- "removeFavorite"
+ "gridLayout",
+ "pureBlack"
],
"zh": [
- "colorAccent",
- "colorBoth",
- "colorBw",
- "colorCustom",
- "colorPrimary",
- "colorWheel",
- "formatTable"
+ "gridLayout",
+ "pureBlack"
]
}
diff --git a/lib/main.dart b/lib/main.dart
index 79f8314..92be6ac 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -29,20 +29,14 @@ class MyApp extends ConsumerWidget {
valueListenable: AppConfigBox.keys(),
builder: (context, _, __) {
return DynamicColorBuilder(builder: (light, dark) {
- ColorScheme? lightScheme;
- ColorScheme? darkScheme;
- if (light != null && dark != null) {
- lightScheme = light.harmonized();
- darkScheme = dark.harmonized();
- }
- lightScheme ??= kDefaultLightColorScheme;
- darkScheme ??= kDefaultDarkColorScheme;
+ light = light?.harmonized() ?? kDefaultLightColorScheme;
+ dark = dark?.harmonized() ?? kDefaultDarkColorScheme;
return MaterialApp.router(
routerConfig: ref.watch(appRouterProvider),
onGenerateTitle: (context) => S.of(context).appName,
- theme: ThemeUtil(lightScheme).getTheme(Brightness.light),
- darkTheme: ThemeUtil(darkScheme).getTheme(Brightness.dark),
- themeMode: AppConfigBox.themeMode,
+ theme: ref.watch(appThemeDataProvider(light, Brightness.light)),
+ darkTheme: ref.watch(appThemeDataProvider(dark, Brightness.dark)),
+ themeMode: ref.watch(appThemeModeProvider),
localizationsDelegates: S.localizationsDelegates,
supportedLocales: S.supportedLocales,
locale: AppConfigBox.locale,
diff --git a/lib/models/app_atom.dart b/lib/models/app_atom.dart
index e206539..620d391 100644
--- a/lib/models/app_atom.dart
+++ b/lib/models/app_atom.dart
@@ -135,7 +135,7 @@ class AppAtom {
);
static final sass2CssGenerator = AppAtom(
- icon: const SvgAssetIcon('assets/icons/sass.svg', colorIcon: true),
+ icon: const SvgAssetIcon('assets/icons/sass.svg'),
title: (context) => context.tr.sassCssGenerator,
path: Sass2cssRoute().location,
categories: [
@@ -224,7 +224,7 @@ class AppAtom {
);
static final jwtDecoder = AppAtom(
- icon: const SvgAssetIcon('assets/icons/JWT.svg', colorIcon: true),
+ icon: const SvgAssetIcon('assets/icons/JWT.svg'),
title: (context) => context.tr.decoderJWT,
path: JwtDecoderRoute().location,
categories: [AppCategory.encodersDecoders],
@@ -238,13 +238,13 @@ class AppAtom {
);
static final jsonFormatter = AppAtom(
- icon: const SvgAssetIcon('assets/icons/json.svg', colorIcon: true),
+ icon: const SvgAssetIcon('assets/icons/json.svg'),
title: (context) => context.tr.formatterJson,
path: JsonFormatterRoute().location,
categories: [AppCategory.formatter],
);
static final dartFormatter = AppAtom(
- icon: const SvgAssetIcon('assets/icons/dart.svg', colorIcon: true),
+ icon: const SvgAssetIcon('assets/icons/dart.svg'),
title: (context) => context.tr.formatterDart,
path: DartFormatterRoute().location,
categories: [AppCategory.formatter],
@@ -270,7 +270,7 @@ class AppAtom {
categories: [AppCategory.infomation],
);
static final quickJs = AppAtom(
- icon: const SvgAssetIcon('assets/icons/js.svg', colorIcon: true),
+ icon: const SvgAssetIcon('assets/icons/js.svg'),
title: (context) => 'Quick JS Tool',
path: QuickJsRoute().location,
categories: [AppCategory.frontEnd],
diff --git a/lib/routers/app_router.dart b/lib/routers/app_router.dart
index 61939a9..5a705f0 100644
--- a/lib/routers/app_router.dart
+++ b/lib/routers/app_router.dart
@@ -55,9 +55,7 @@ GoRouter appRouter(AppRouterRef ref) {
]),
TypedGoRoute(path: '/favorite'),
TypedGoRoute(path: '/search'),
- TypedGoRoute(path: '/settings', routes: [
- TypedGoRoute(path: 'licenses'),
- ]),
+ TypedGoRoute(path: '/settings'),
],
)
class RootRoute extends ShellRouteData {
diff --git a/lib/tools/converters/color_converter/color_converter.dart b/lib/tools/converters/color_converter/color_converter.dart
index 3a384fb..4d8b3ed 100644
--- a/lib/tools/converters/color_converter/color_converter.dart
+++ b/lib/tools/converters/color_converter/color_converter.dart
@@ -2,6 +2,7 @@ import 'package:alga/l10n/l10n.dart';
import 'package:alga/tools/converters/color_converter/color_picker.dart';
import 'package:alga/tools/converters/color_converter/color_view_background_patiner.dart';
import 'package:alga/tools/tools.provider.dart';
+import 'package:alga/ui/widgets/app_text_field.dart';
import 'package:alga/ui/widgets/configurations/configurations.dart';
import 'package:alga/ui/widgets/custom_icon_button.dart';
import 'package:alga/ui/widgets/scaffold/scrollable_scaffold.dart';
@@ -71,7 +72,7 @@ class _ColorConverterPageState extends ConsumerState {
),
],
),
- TextField(
+ AppInput(
controller: _controller,
decoration: InputDecoration(errorText: result.$2),
),
diff --git a/lib/tools/converters/common_converter/common_converter.dart b/lib/tools/converters/common_converter/common_converter.dart
index 8a98b69..b60ced2 100644
--- a/lib/tools/converters/common_converter/common_converter.dart
+++ b/lib/tools/converters/common_converter/common_converter.dart
@@ -170,10 +170,9 @@ class ConverterInput extends ConsumerWidget {
],
),
Expanded(
- child: TextField(
+ child: AppInput(
expands: true,
maxLines: null,
- textAlignVertical: TextAlignVertical.top,
controller: CommonConverterPage.of(context),
),
),
diff --git a/lib/tools/encoders_decoders/base_64_encoder_decoder/base64.dart b/lib/tools/encoders_decoders/base_64_encoder_decoder/base64.dart
index 64ff09a..bce7eff 100644
--- a/lib/tools/encoders_decoders/base_64_encoder_decoder/base64.dart
+++ b/lib/tools/encoders_decoders/base_64_encoder_decoder/base64.dart
@@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:alga/l10n/l10n.dart';
import 'package:alga/tools/tools.provider.dart';
+import 'package:alga/ui/widgets/app_text_field.dart';
import 'package:alga/ui/widgets/buttons/clear_button.dart';
import 'package:alga/ui/widgets/buttons/copy_button.dart';
import 'package:alga/ui/widgets/configurations/configurations.dart';
@@ -72,7 +73,7 @@ class _Base64CodecPageState extends ConsumerState {
],
),
Expanded(
- child: TextField(
+ child: AppInput(
expands: true,
maxLines: null,
controller: _originalInput,
@@ -96,7 +97,7 @@ class _Base64CodecPageState extends ConsumerState {
],
),
Expanded(
- child: TextField(
+ child: AppInput(
expands: true,
maxLines: null,
controller: _encodedInput,
diff --git a/lib/tools/encoders_decoders/gzip_compress_decompress/gzip_compress.dart b/lib/tools/encoders_decoders/gzip_compress_decompress/gzip_compress.dart
index e3da5c3..4dba64f 100644
--- a/lib/tools/encoders_decoders/gzip_compress_decompress/gzip_compress.dart
+++ b/lib/tools/encoders_decoders/gzip_compress_decompress/gzip_compress.dart
@@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:alga/l10n/l10n.dart';
+import 'package:alga/ui/widgets/app_text_field.dart';
import 'package:alga/ui/widgets/buttons/clear_button.dart';
import 'package:alga/ui/widgets/buttons/copy_button.dart';
import 'package:alga/ui/widgets/toolbar/alga_toolbar.dart';
@@ -61,7 +62,7 @@ class _GZipCompressPageState extends State {
],
),
Expanded(
- child: TextField(
+ child: AppInput(
expands: true,
maxLines: null,
controller: _originalInput,
@@ -85,7 +86,7 @@ class _GZipCompressPageState extends State {
],
),
Expanded(
- child: TextField(
+ child: AppInput(
expands: true,
maxLines: null,
controller: _encodedInput,
diff --git a/lib/tools/encoders_decoders/jwt_decoder/jwt_decoder.dart b/lib/tools/encoders_decoders/jwt_decoder/jwt_decoder.dart
index f1bd608..425c424 100644
--- a/lib/tools/encoders_decoders/jwt_decoder/jwt_decoder.dart
+++ b/lib/tools/encoders_decoders/jwt_decoder/jwt_decoder.dart
@@ -62,7 +62,7 @@ class _JwtDecoderPageState extends ConsumerState {
ClearButton(controller: _controller),
],
),
- TextField(
+ AppInput(
controller: _controller,
minLines: 3,
maxLines: 12,
diff --git a/lib/tools/encoders_decoders/uri_encoder_decoder/uri_codec.dart b/lib/tools/encoders_decoders/uri_encoder_decoder/uri_codec.dart
index f0440f4..d79c774 100644
--- a/lib/tools/encoders_decoders/uri_encoder_decoder/uri_codec.dart
+++ b/lib/tools/encoders_decoders/uri_encoder_decoder/uri_codec.dart
@@ -1,4 +1,5 @@
import 'package:alga/l10n/l10n.dart';
+import 'package:alga/ui/widgets/app_text_field.dart';
import 'package:alga/ui/widgets/buttons/clear_button.dart';
import 'package:alga/ui/widgets/buttons/copy_button.dart';
import 'package:alga/ui/widgets/configurations/configurations.dart';
@@ -103,7 +104,7 @@ class _UriCodecPageState extends ConsumerState {
],
),
Expanded(
- child: TextField(
+ child: AppInput(
expands: true,
maxLines: null,
controller: _originalInput,
@@ -127,7 +128,7 @@ class _UriCodecPageState extends ConsumerState {
],
),
Expanded(
- child: TextField(
+ child: AppInput(
expands: true,
maxLines: null,
controller: _encodedInput,
diff --git a/lib/tools/encoders_decoders/uri_parser/uri_parser.dart b/lib/tools/encoders_decoders/uri_parser/uri_parser.dart
index d8c79ff..a76ab9c 100644
--- a/lib/tools/encoders_decoders/uri_parser/uri_parser.dart
+++ b/lib/tools/encoders_decoders/uri_parser/uri_parser.dart
@@ -63,7 +63,7 @@ class _UriParserPageState extends ConsumerState {
ClearButton(controller: _controller),
],
),
- TextField(
+ AppInput(
controller: _controller,
decoration: InputDecoration(errorText: result.$2),
),
diff --git a/lib/tools/formatters/dart/dart_formatter.dart b/lib/tools/formatters/dart/dart_formatter.dart
index 027b484..ab220f0 100644
--- a/lib/tools/formatters/dart/dart_formatter.dart
+++ b/lib/tools/formatters/dart/dart_formatter.dart
@@ -1,4 +1,5 @@
import 'package:alga/l10n/l10n.dart';
+import 'package:alga/ui/widgets/app_text_field.dart';
import 'package:alga/ui/widgets/buttons/copy_button.dart';
import 'package:alga/ui/widgets/configurations/configurations.dart';
import 'package:alga/ui/widgets/custom_icon_button.dart';
@@ -76,7 +77,7 @@ class _DartFormatterPageState extends ConsumerState {
child: ValueListenableBuilder(
valueListenable: _errorMessage,
builder: (context, message, _) {
- return TextField(
+ return AppInput(
expands: true,
maxLines: null,
minLines: null,
diff --git a/lib/tools/formatters/json/json_formatter.dart b/lib/tools/formatters/json/json_formatter.dart
index 2d36880..866e510 100644
--- a/lib/tools/formatters/json/json_formatter.dart
+++ b/lib/tools/formatters/json/json_formatter.dart
@@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:alga/l10n/l10n.dart';
+import 'package:alga/ui/widgets/app_text_field.dart';
import 'package:alga/ui/widgets/buttons/copy_button.dart';
import 'package:alga/ui/widgets/configurations/configurations.dart';
import 'package:alga/ui/widgets/custom_icon_button.dart';
@@ -89,7 +90,7 @@ class _JsonFormatterPageState extends ConsumerState {
child: ValueListenableBuilder(
valueListenable: _errorMessage,
builder: (context, message, _) {
- return TextField(
+ return AppInput(
expands: true,
maxLines: null,
minLines: null,
diff --git a/lib/tools/generators/hash_generator/hash_gen.dart b/lib/tools/generators/hash_generator/hash_gen.dart
index b33d52e..2ae1ca7 100644
--- a/lib/tools/generators/hash_generator/hash_gen.dart
+++ b/lib/tools/generators/hash_generator/hash_gen.dart
@@ -90,14 +90,14 @@ class _HashGenPageState extends ConsumerState {
ClearButton(controller: _input),
],
),
- TextField(
+ AppInput(
minLines: 1,
maxLines: 12,
controller: _input,
),
CrossFade(
state: ref.watch(useHmac),
- first: TextField(
+ first: AppInput(
minLines: 1,
maxLines: 12,
controller: _hmac,
diff --git a/lib/tools/generators/sass_css_generator/sass2css.dart b/lib/tools/generators/sass_css_generator/sass2css.dart
index e8b40e8..eced75c 100644
--- a/lib/tools/generators/sass_css_generator/sass2css.dart
+++ b/lib/tools/generators/sass_css_generator/sass2css.dart
@@ -89,7 +89,7 @@ class _Sass2cssPageState extends ConsumerState {
),
Consumer(
builder: (context, ref, _) {
- return TextField(
+ return AppInput(
minLines: 3,
maxLines: 8,
controller: _input,
diff --git a/lib/tools/image_tools/blur_hash_tool/blur_hash.dart b/lib/tools/image_tools/blur_hash_tool/blur_hash.dart
index 8461979..d4d1e39 100644
--- a/lib/tools/image_tools/blur_hash_tool/blur_hash.dart
+++ b/lib/tools/image_tools/blur_hash_tool/blur_hash.dart
@@ -203,7 +203,7 @@ class _DecodeViewState extends State {
return ListView(
padding: const EdgeInsets.all(8),
children: [
- TextField(
+ AppInput(
controller: controller,
decoration: InputDecoration(
errorText: result.$2,
diff --git a/lib/tools/image_tools/qrcode_tool/qrcode.dart b/lib/tools/image_tools/qrcode_tool/qrcode.dart
index 196054c..8a580dc 100644
--- a/lib/tools/image_tools/qrcode_tool/qrcode.dart
+++ b/lib/tools/image_tools/qrcode_tool/qrcode.dart
@@ -1,5 +1,6 @@
import 'package:alga/l10n/l10n.dart';
import 'package:alga/tools/tools.provider.dart';
+import 'package:alga/ui/widgets/app_text_field.dart';
import 'package:alga/ui/widgets/configurations/configurations.dart';
import 'package:alga/ui/widgets/scaffold/scrollable_scaffold.dart';
import 'package:alga/ui/widgets/toolbar/alga_toolbar.dart';
@@ -86,7 +87,7 @@ class _QrCodePageState extends ConsumerState {
PasteButton(controller: _input),
],
),
- TextField(
+ AppInput(
maxLines: 2,
controller: _input,
),
diff --git a/lib/tools/js_tools/quick_js_tool/quick_js_view.dart b/lib/tools/js_tools/quick_js_tool/quick_js_view.dart
index a566197..8b74d6d 100644
--- a/lib/tools/js_tools/quick_js_tool/quick_js_view.dart
+++ b/lib/tools/js_tools/quick_js_tool/quick_js_view.dart
@@ -32,7 +32,7 @@ class QuickJsView extends ConsumerWidget {
child: Row(
children: [
Expanded(
- child: TextField(
+ child: AppInput(
maxLines: 3,
minLines: 1,
controller: ref.watch(jsInputControllerProvider),
diff --git a/lib/tools/text_tools/date_parser/date_parser.dart b/lib/tools/text_tools/date_parser/date_parser.dart
index 8283f2d..4636070 100644
--- a/lib/tools/text_tools/date_parser/date_parser.dart
+++ b/lib/tools/text_tools/date_parser/date_parser.dart
@@ -102,7 +102,7 @@ class _DateParserPageState extends ConsumerState {
),
],
),
- TextField(
+ AppInput(
controller: _input,
decoration: InputDecoration(errorText: dateResult.$2),
),
@@ -146,7 +146,7 @@ class _DateParserPageState extends ConsumerState {
HelpButton(onPressed: () => DateFormatHelper.show(context)),
],
),
- TextField(controller: _formatInput),
+ AppInput(controller: _formatInput),
AlgaToolbar(
actions: [
CopyButton(() => ref.read(formatDateResultProvider)),
diff --git a/lib/tools/text_tools/markdown_preview/markdown_preview.dart b/lib/tools/text_tools/markdown_preview/markdown_preview.dart
index e9ae492..a298863 100644
--- a/lib/tools/text_tools/markdown_preview/markdown_preview.dart
+++ b/lib/tools/text_tools/markdown_preview/markdown_preview.dart
@@ -1,5 +1,6 @@
import 'package:alga/l10n/l10n.dart';
import 'package:alga/tools/tools.provider.dart';
+import 'package:alga/ui/widgets/app_text_field.dart';
import 'package:alga/ui/widgets/buttons/clear_button.dart';
import 'package:alga/ui/widgets/buttons/copy_button.dart';
import 'package:alga/ui/widgets/toolbar/alga_toolbar.dart';
@@ -156,7 +157,7 @@ class MarkdownEditor extends StatelessWidget {
],
),
Expanded(
- child: TextField(
+ child: AppInput(
controller: controller,
expands: true,
maxLines: null,
diff --git a/lib/tools/text_tools/regex_tester/regex_tester.dart b/lib/tools/text_tools/regex_tester/regex_tester.dart
index 857458f..625d365 100644
--- a/lib/tools/text_tools/regex_tester/regex_tester.dart
+++ b/lib/tools/text_tools/regex_tester/regex_tester.dart
@@ -2,6 +2,7 @@ import 'package:alga/l10n/l10n.dart';
import 'package:alga/tools/text_tools/regex_tester/regex_tester.provider.dart';
import 'package:alga/tools/text_tools/regex_tester/regex_tester_text_builder.dart';
import 'package:alga/tools/tools.provider.dart';
+import 'package:alga/ui/widgets/app_text_field.dart';
import 'package:alga/ui/widgets/buttons/clear_button.dart';
import 'package:alga/ui/widgets/configurations/configurations.dart';
import 'package:alga/ui/widgets/scaffold/scrollable_scaffold.dart';
@@ -89,7 +90,7 @@ class _RegexTesterPageState extends ConsumerState {
ClearButton(controller: _regexController),
],
),
- TextField(
+ AppInput(
minLines: 2,
maxLines: 4,
controller: _regexController,
@@ -102,7 +103,7 @@ class _RegexTesterPageState extends ConsumerState {
ClearButton(controller: _textController),
],
),
- TextField(
+ AppInput(
minLines: 2,
maxLines: 12,
controller: _textController,
diff --git a/lib/ui/alga_view/alga_view.dart b/lib/ui/alga_view/alga_view.dart
index 484eb76..cf3b667 100644
--- a/lib/ui/alga_view/alga_view.dart
+++ b/lib/ui/alga_view/alga_view.dart
@@ -1,7 +1,7 @@
-import 'package:alga/ui/alga_view/widgets/alga_navigation_bar.dart';
import 'package:alga/ui/alga_view/widgets/alga_panel.dart';
import 'package:alga/ui/global.provider.dart';
-import 'package:alga/utils/constants/import_helper.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
class AlgaShellRouteView extends ConsumerWidget {
const AlgaShellRouteView({super.key, required this.child});
@@ -25,9 +25,9 @@ class AlgaShellRouteView extends ConsumerWidget {
),
);
} else {
- return Scaffold(
- body: child,
- bottomNavigationBar: const AlgaNavigationBar(),
+ return Material(
+ color: Theme.of(context).scaffoldBackgroundColor,
+ child: child,
);
}
}
diff --git a/lib/ui/alga_view/all_apps/alga_app_item.dart b/lib/ui/alga_view/all_apps/alga_app_item.dart
index 5bdab26..ce352d3 100644
--- a/lib/ui/alga_view/all_apps/alga_app_item.dart
+++ b/lib/ui/alga_view/all_apps/alga_app_item.dart
@@ -4,22 +4,23 @@ import 'package:alga/utils/hive_boxes/favorite_box.dart';
import 'package:auto_size_text/auto_size_text.dart';
class AlgaAppItem extends StatelessWidget {
- const AlgaAppItem(this.item, {super.key, this.listenable = true});
+ const AlgaAppItem(this.item,
+ {super.key, this.listenable = true, this.useGrid = true});
final AppAtom item;
final bool listenable;
+ final bool useGrid;
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
- Color iconColor = colorScheme.tertiary;
+ Color iconColor = colorScheme.primary;
+ if (useGrid) {
+ return ValueListenableBuilder(
+ valueListenable: FavoriteBox.listener(item.path),
+ builder: (context, _, child) {
+ final state = FavoriteBox.get(item);
- return ValueListenableBuilder(
- valueListenable: FavoriteBox.listener(item.path),
- builder: (context, _, child) {
- final state = FavoriteBox.get(item);
- return GestureDetector(
- onLongPressStart: (detail) {},
- child: Material(
+ return Material(
color: colorScheme.surfaceVariant,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
@@ -40,51 +41,73 @@ class AlgaAppItem extends StatelessWidget {
},
child: child,
),
- ),
- );
- },
- child: Stack(
- children: [
- Positioned(
- left: 16 - 48,
- bottom: 16,
- child: Padding(
- padding: const EdgeInsets.all(8.0),
- child: IconTheme.merge(
- data: IconThemeData(
- size: 84,
- color: iconColor.withOpacity(0.4),
+ );
+ },
+ child: Stack(
+ children: [
+ Positioned(
+ top: 0,
+ left: 0,
+ bottom: 0,
+ child: Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: IconTheme.merge(
+ data: IconThemeData(
+ size: 48,
+ color: iconColor.withOpacity(0.4),
+ ),
+ child: item.icon,
),
- child: item.icon,
),
),
- ),
- Positioned(
- left: 48,
- right: 16,
- top: 8,
- bottom: 16,
- child: Padding(
- padding: const EdgeInsets.all(4.0),
- child: Align(
- alignment: Alignment.bottomRight,
- child: AutoSizeText(
- item.title(context),
- maxLines: 2,
- minFontSize: 12,
- textAlign: TextAlign.end,
- style: TextStyle(
- color: colorScheme.onSecondaryContainer,
- fontSize: 16,
- fontWeight: FontWeight.bold,
+ Positioned(
+ left: 56,
+ right: 16,
+ top: 8,
+ bottom: 16,
+ child: Padding(
+ padding: const EdgeInsets.all(4.0),
+ child: Align(
+ alignment: Alignment.bottomRight,
+ child: AutoSizeText(
+ item.title(context),
+ maxLines: 2,
+ minFontSize: 12,
+ textAlign: TextAlign.end,
+ style: TextStyle(
+ color: colorScheme.onSecondaryContainer,
+ fontSize: 16,
+ fontWeight: FontWeight.bold,
+ ),
),
),
),
),
- ),
- ],
- ),
- );
+ ],
+ ),
+ );
+ } else {
+ return ValueListenableBuilder(
+ valueListenable: FavoriteBox.listener(item.path),
+ builder: (context, _, __) {
+ final state = FavoriteBox.get(item);
+ return GestureDetector(
+ onSecondaryTapUp: (detail) {
+ _showMenu(context, detail.localPosition, state);
+ },
+ child: ListTile(
+ leading: item.icon,
+ title: Text(item.title(context)),
+ onTap: () {
+ GoRouter.of(context).go(item.path);
+ },
+ onLongPress: () {
+ _showMenuModal(context, state);
+ },
+ ),
+ );
+ });
+ }
}
_showMenu(BuildContext context, Offset offset, bool like) async {
@@ -138,6 +161,7 @@ class AlgaAppItem extends StatelessWidget {
showModalBottomSheet(
context: context,
showDragHandle: true,
+ useSafeArea: false,
builder: (context) {
return Column(
mainAxisSize: MainAxisSize.min,
diff --git a/lib/ui/alga_view/all_apps/alga_app_view.dart b/lib/ui/alga_view/all_apps/alga_app_view.dart
index 81d484a..85d17ec 100644
--- a/lib/ui/alga_view/all_apps/alga_app_view.dart
+++ b/lib/ui/alga_view/all_apps/alga_app_view.dart
@@ -4,7 +4,9 @@ import 'package:alga/models/app_atom.dart';
import 'package:alga/models/app_category.dart';
import 'package:alga/routers/app_router.dart';
import 'package:alga/ui/global.provider.dart';
+import 'package:alga/ui/views/favorite_view.dart';
import 'package:alga/ui/views/search_view.dart';
+import 'package:alga/ui/views/settings_view.dart';
import 'package:alga/utils/constants/import_helper.dart';
import 'alga_app_item.dart';
@@ -30,20 +32,18 @@ class AlgaAppViewState extends ConsumerState
Widget build(BuildContext context) {
final isDesktop = ref.watch(isDesktopProvider);
return Scaffold(
- appBar: AppBar(
- title: Text(S.of(context).appName),
- centerTitle: Platform.isIOS,
- bottom: const AppCategoriesPanel(),
- actions: [
- if (!isDesktop)
- IconButton(
- onPressed: () {
- SearchRoute().push(context);
- },
- icon: const Icon(Icons.search_rounded),
+ appBar: isDesktop
+ ? AppBar(
+ title: Text(S.of(context).appName),
+ centerTitle: Platform.isIOS,
+ bottom: const AppCategoriesPanel(),
+ )
+ : AppBar(
+ // title: Text(S.of(context).appName),
+ title: const MobileSearchBar(),
+ centerTitle: Platform.isIOS,
+ bottom: const AppCategoriesPanel(),
),
- ],
- ),
body: Consumer(builder: (context, ref, _) {
return TabBarView(
controller: ref.watch(appTabControllerProvider(vsync: this)),
@@ -56,6 +56,49 @@ class AlgaAppViewState extends ConsumerState
}
}
+class MobileSearchBar extends StatelessWidget {
+ const MobileSearchBar({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return SearchAnchor.bar(
+ barHintText: '${context.tr.appName} ${context.tr.search}',
+ isFullScreen: true,
+ suggestionsBuilder: (BuildContext context, SearchController controller) {
+ final items = SearchViewState.findAtom(context, controller.text);
+ return items
+ .map(
+ (e) => ListTile(
+ leading: e.icon,
+ title: Text(e.title(context)),
+ onTap: () {
+ GoRouter.of(context).go(e.path);
+ },
+ ),
+ )
+ .toList();
+ },
+ barElevation: const MaterialStatePropertyAll(0),
+ barBackgroundColor: MaterialStateProperty.all(
+ Theme.of(context).colorScheme.surfaceVariant),
+ barTrailing: [
+ IconButton(
+ onPressed: () {
+ FavoriteRoute().push(context);
+ },
+ icon: const Icon(Icons.favorite_outline_rounded),
+ ),
+ IconButton(
+ onPressed: () {
+ SettingsRoute().push(context);
+ },
+ icon: const Icon(Icons.settings_outlined),
+ ),
+ ],
+ );
+ }
+}
+
class AppCategoriesPanel extends StatefulWidget implements PreferredSizeWidget {
const AppCategoriesPanel({super.key});
@@ -87,7 +130,7 @@ class _AppCategoriesPanelState extends State {
}
}
-class AppCategoryView extends StatelessWidget {
+class AppCategoryView extends ConsumerWidget {
const AppCategoryView(this.category, {super.key});
final AppCategory category;
@@ -95,20 +138,37 @@ class AppCategoryView extends StatelessWidget {
List get items => categoryMapping[category.uuid] ?? [];
@override
- Widget build(BuildContext context) {
- return GridView.builder(
- padding: const EdgeInsets.all(16),
- gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
- maxCrossAxisExtent: 220,
- childAspectRatio: 2.0,
- mainAxisSpacing: 8,
- crossAxisSpacing: 8,
- ),
- itemBuilder: (BuildContext context, int index) {
- final item = items[index];
- return AlgaAppItem(item);
- },
- itemCount: items.length,
- );
+ Widget build(BuildContext context, WidgetRef ref) {
+ if (ref.watch(useGridLayoutProvider)) {
+ return GridView.builder(
+ keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
+ padding: const EdgeInsets.all(16),
+ gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
+ maxCrossAxisExtent: 220,
+ childAspectRatio: 2.0,
+ mainAxisSpacing: 8,
+ crossAxisSpacing: 8,
+ ),
+ itemBuilder: (BuildContext context, int index) {
+ final item = items[index];
+ return AlgaAppItem(item);
+ },
+ itemCount: items.length,
+ );
+ } else {
+ return Align(
+ alignment: Alignment.centerLeft,
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(maxWidth: 480),
+ child: ListView.builder(
+ itemBuilder: (context, index) {
+ final item = items[index];
+ return AlgaAppItem(item, useGrid: false);
+ },
+ itemCount: items.length,
+ ),
+ ),
+ );
+ }
}
}
diff --git a/lib/ui/alga_view/widgets/alga_navigation_bar.dart b/lib/ui/alga_view/widgets/alga_navigation_bar.dart
deleted file mode 100644
index 324d6ce..0000000
--- a/lib/ui/alga_view/widgets/alga_navigation_bar.dart
+++ /dev/null
@@ -1,59 +0,0 @@
-import 'package:alga/routers/app_router.dart';
-import 'package:alga/ui/alga_view/all_apps/alga_app_view.dart';
-import 'package:alga/ui/views/favorite_view.dart';
-import 'package:alga/ui/views/search_view.dart';
-import 'package:alga/ui/views/settings_view.dart';
-import 'package:alga/utils/constants/import_helper.dart';
-
-class AlgaNavigationBar extends StatefulWidget {
- const AlgaNavigationBar({super.key});
-
- @override
- State createState() => _AlgaNavigationBarState();
-}
-
-class _AlgaNavigationBarState extends State {
- int getIndex(BuildContext context) {
- final location = GoRouterState.of(context).matchedLocation;
- if (location.startsWith(AppsRoute().location)) return 0;
- if (location.startsWith(FavoriteRoute().location)) return 1;
- if (location.startsWith(SearchRoute().location)) return 0;
- if (location.startsWith(SettingsRoute().location)) return 2;
- return 0;
- }
-
- @override
- Widget build(BuildContext context) {
- return NavigationBar(
- selectedIndex: getIndex(context),
- onDestinationSelected: (index) {
- switch (index) {
- case 0:
- AppsRoute().go(context);
- case 1:
- FavoriteRoute().go(context);
- case 2:
- SettingsRoute().go(context);
- default:
- }
- },
- destinations: [
- NavigationDestination(
- icon: const Icon(Icons.category_outlined),
- selectedIcon: const Icon(Icons.category_rounded),
- label: context.tr.allApps,
- ),
- NavigationDestination(
- icon: const Icon(Icons.favorite_outline_rounded),
- selectedIcon: const Icon(Icons.favorite_rounded),
- label: context.tr.favorite,
- ),
- NavigationDestination(
- icon: const Icon(Icons.settings_outlined),
- selectedIcon: const Icon(Icons.settings),
- label: context.tr.settings,
- ),
- ],
- );
- }
-}
diff --git a/lib/ui/views/search_view.dart b/lib/ui/views/search_view.dart
index d4564d5..ed38a1e 100644
--- a/lib/ui/views/search_view.dart
+++ b/lib/ui/views/search_view.dart
@@ -16,10 +16,10 @@ class SearchView extends StatefulWidget {
const SearchView({super.key});
@override
- State createState() => _SearchViewState();
+ State createState() => SearchViewState();
}
-class _SearchViewState extends State {
+class SearchViewState extends State {
final _textController = TextEditingController();
List _items = [];
@@ -27,10 +27,10 @@ class _SearchViewState extends State {
@override
void initState() {
super.initState();
- _items = _findAtom(_textController.text);
+ _items = findAtom(context, _textController.text);
_textController.addListener(() {
- _items = _findAtom(_textController.text);
+ _items = findAtom(context, _textController.text);
if (mounted) setState(() {});
});
}
@@ -41,7 +41,7 @@ class _SearchViewState extends State {
super.dispose();
}
- List _findAtom(String query) {
+ static List findAtom(BuildContext context, String query) {
if (query.isEmpty) return AppAtom.items.toList();
query = query.toLowerCase();
final results = {};
diff --git a/lib/ui/views/settings_view.dart b/lib/ui/views/settings_view.dart
index 6ce750a..703430a 100644
--- a/lib/ui/views/settings_view.dart
+++ b/lib/ui/views/settings_view.dart
@@ -1,4 +1,3 @@
-import 'package:alga/routers/app_router.dart';
import 'package:alga/ui/widgets/alga_logo.dart';
import 'package:alga/ui/widgets/app_show_menu.dart';
import 'package:alga/ui/widgets/setting_title.dart';
@@ -13,13 +12,6 @@ class SettingsRoute extends GoRouteData {
}
}
-class LicensesRoute extends GoRouteData {
- @override
- Widget build(BuildContext context, GoRouterState state) {
- return const LicensePage();
- }
-}
-
class SettingsView extends StatefulWidget {
const SettingsView({super.key});
@@ -41,9 +33,8 @@ class _SettingsViewState extends State {
SliverList(
delegate: SliverChildListDelegate([
SettingTitle(Text(context.tr.lookAndFeel)),
- ValueListenableBuilder(
- valueListenable: AppConfigBox.key(AppConfigType.themeMode),
- builder: (context, _, __) {
+ Consumer(
+ builder: (context, ref, child) {
return AppShowMenu(
items: ThemeMode.values
.map((e) => PopupMenuItem(
@@ -51,44 +42,79 @@ class _SettingsViewState extends State {
child: Text(e.getName(context)),
))
.toList(),
- initialValue: AppConfigBox.themeMode,
+ initialValue: ref.watch(appThemeModeProvider),
onSelected: (item) {
- AppConfigBox.themeMode = item;
+ ref.read(appThemeModeProvider.notifier).change(item);
},
childBuilder: (context, open) {
return ListTile(
onTap: open,
leading: const Icon(Icons.dark_mode),
- title: Text(S.of(context).themeMode),
- trailing: Text(AppConfigBox.themeMode.getName(context)),
+ title: Text(context.tr.themeMode),
+ trailing: Text(
+ ref.watch(appThemeModeProvider).getName(context),
+ ),
);
},
);
},
),
- ValueListenableBuilder(
- valueListenable: AppConfigBox.key(AppConfigType.locale),
- builder: (context, _, __) {
- return AppShowMenu(
- items: AppConfigBox.localCodes.map((e) {
- return PopupMenuItem(
- value: e,
- child: Text(S.getlang(context, e)),
- );
- }).toList(),
- initialValue: AppConfigBox.localeValue,
- onSelected: (item) {
- AppConfigBox.localeValue = item;
- },
+ Consumer(
+ builder: (context, ref, _) {
+ return AppShowMenu(
+ items: [
+ PopupMenuItem(
+ value: null,
+ onTap: () {
+ ref.read(appLocaleProvider.notifier).change(null);
+ },
+ child: Text(context.tr.followSystem),
+ ),
+ ...S.supportedLocales.map((e) {
+ return PopupMenuItem(
+ value: e,
+ child: Text(e.getName(context)),
+ );
+ }),
+ ],
+ initialValue: ref.watch(appLocaleProvider),
childBuilder: (context, open) {
return ListTile(
- onTap: open,
leading: const Icon(Icons.language),
- title: Text(S.of(context).language),
+ title: Text(context.tr.language),
trailing:
- Text(S.getlang(context, AppConfigBox.localeValue)),
+ Text(ref.watch(appLocaleProvider).getName(context)),
+ onTap: open,
);
},
+ onSelected: (t) {
+ ref.read(appLocaleProvider.notifier).change(t);
+ },
+ );
+ },
+ ),
+ if (isDark(context))
+ Consumer(
+ builder: (context, ref, _) {
+ return SwitchListTile(
+ secondary: const Icon(Icons.night_shelter_rounded),
+ title: Text(context.tr.pureBlack),
+ value: ref.watch(pureBlackBackgroundProvider),
+ onChanged: (t) {
+ ref.read(pureBlackBackgroundProvider.notifier).change(t);
+ },
+ );
+ },
+ ),
+ Consumer(
+ builder: (context, ref, _) {
+ return SwitchListTile(
+ secondary: const Icon(Icons.grid_view_rounded),
+ title: Text(context.tr.gridLayout),
+ value: ref.watch(useGridLayoutProvider),
+ onChanged: (t) {
+ ref.read(useGridLayoutProvider.notifier).change(t);
+ },
);
},
),
@@ -113,22 +139,22 @@ class _SettingsViewState extends State {
);
},
),
- ListTile(
- leading: const Icon(Icons.info_rounded),
- title: Text(context.tr.licenses),
- onTap: () {
- LicensesRoute().go(context);
- },
- ),
- ListTile(
- leading: const AlgaLogo(radius: 24),
- title: Text(context.tr.appName),
- subtitle: Text('${context.tr.version}$_buildName+$_buildNumber'),
+ AboutListTile(
+ icon: const AlgaLogo(radius: 24),
+ applicationName: context.tr.appName,
+ applicationIcon: const AlgaLogo(radius: 24),
+ applicationLegalese: 'MIT',
+ applicationVersion:
+ '${context.tr.version}$_buildName+$_buildNumber',
+ aboutBoxChildren: [],
),
])),
],
);
- result = Material(child: result);
+ result = Material(
+ color: Theme.of(context).scaffoldBackgroundColor,
+ child: result,
+ );
return result;
}
}
diff --git a/lib/ui/widgets/alga_logo.dart b/lib/ui/widgets/alga_logo.dart
index 9cafaac..9355e94 100644
--- a/lib/ui/widgets/alga_logo.dart
+++ b/lib/ui/widgets/alga_logo.dart
@@ -13,6 +13,7 @@ class AlgaLogo extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final colorScheme = Theme.of(context).colorScheme;
return Material(
color: Theme.of(context).colorScheme.primaryContainer,
shape: ContinuousRectangleBorder(
@@ -20,8 +21,13 @@ class AlgaLogo extends StatelessWidget {
),
child: Padding(
padding: padding,
- child: SvgPicture.asset('assets/logo/logo.svg',
- height: radius, width: radius),
+ child: SvgPicture.asset(
+ 'assets/logo/logo.svg',
+ height: radius,
+ width: radius,
+ colorFilter:
+ ColorFilter.mode(colorScheme.onBackground, BlendMode.srcIn),
+ ),
),
);
}
diff --git a/lib/ui/widgets/app_text_field.dart b/lib/ui/widgets/app_text_field.dart
index 9df1d9f..835e47d 100644
--- a/lib/ui/widgets/app_text_field.dart
+++ b/lib/ui/widgets/app_text_field.dart
@@ -1,3 +1,4 @@
+import 'package:alga/utils/theme_util.dart';
import 'package:flutter/material.dart';
import 'package:language_textfield/lang_special_builder.dart';
import 'package:language_textfield/language_textfield.dart';
@@ -67,6 +68,40 @@ class _AppTextFieldState extends State {
textAlign: TextAlign.start,
textAlignVertical: TextAlignVertical.top,
decoration: widget.decoration,
+ style: getMonoTextStyle(context),
+ );
+ }
+}
+
+class AppInput extends StatelessWidget {
+ const AppInput({
+ super.key,
+ required this.controller,
+ this.minLines,
+ this.maxLines = 1,
+ this.decoration = const InputDecoration(),
+ this.expands = false,
+ this.textAlignVertical = TextAlignVertical.top,
+ this.onChanged,
+ });
+ final TextEditingController controller;
+ final int? minLines;
+ final int? maxLines;
+ final bool expands;
+ final InputDecoration? decoration;
+ final TextAlignVertical textAlignVertical;
+ final ValueChanged? onChanged;
+ @override
+ Widget build(BuildContext context) {
+ return TextField(
+ controller: controller,
+ minLines: minLines,
+ maxLines: maxLines,
+ style: getMonoTextStyle(context),
+ textAlignVertical: textAlignVertical,
+ decoration: decoration,
+ onChanged: onChanged,
+ expands: expands,
);
}
}
diff --git a/lib/ui/widgets/scaffold/scrollable_scaffold.dart b/lib/ui/widgets/scaffold/scrollable_scaffold.dart
index 807ee7a..7cc722f 100644
--- a/lib/ui/widgets/scaffold/scrollable_scaffold.dart
+++ b/lib/ui/widgets/scaffold/scrollable_scaffold.dart
@@ -40,6 +40,7 @@ class ScrollableScaffold extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
+ keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
slivers: [
SliverAppBar.medium(title: title, actions: actions),
if (configurations.isNotEmpty)
diff --git a/lib/ui/widgets/svg_asset_icon.dart b/lib/ui/widgets/svg_asset_icon.dart
index 07ffa08..791c5c9 100644
--- a/lib/ui/widgets/svg_asset_icon.dart
+++ b/lib/ui/widgets/svg_asset_icon.dart
@@ -8,8 +8,7 @@ class SvgAssetIcon extends StatelessWidget {
const SvgAssetIcon(this.svg, {super.key, this.colorIcon = false});
Color? _color(BuildContext context) {
- if (colorIcon) return null;
- return Theme.of(context).colorScheme.secondary;
+ return Theme.of(context).colorScheme.primary;
}
@override
@@ -21,9 +20,8 @@ class SvgAssetIcon extends StatelessWidget {
svg,
width: iconSize,
height: iconSize,
- colorFilter: color == null
- ? null
- : ColorFilter.mode(iconColor ?? color, BlendMode.srcIn),
+ colorFilter:
+ colorIcon ? null : ColorFilter.mode(iconColor!, BlendMode.srcIn),
);
return SizedBox.square(
dimension: iconSize,
diff --git a/lib/ui/widgets/tool_view_config.dart b/lib/ui/widgets/tool_view_config.dart
index badcde9..655b0eb 100644
--- a/lib/ui/widgets/tool_view_config.dart
+++ b/lib/ui/widgets/tool_view_config.dart
@@ -20,10 +20,7 @@ class ToolViewConfig extends StatelessWidget {
final scheme = Theme.of(context).colorScheme;
return ListTile(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
- title: DefaultTextStyle.merge(
- style: TextStyle(color: scheme.onSecondaryContainer, fontFamily: ''),
- child: title,
- ),
+ title: title,
subtitle: subtitle,
leading: leading,
trailing: trailing,
diff --git a/lib/utils/hive_boxes/app_config_box.dart b/lib/utils/hive_boxes/app_config_box.dart
index 1ded59c..caed348 100644
--- a/lib/utils/hive_boxes/app_config_box.dart
+++ b/lib/utils/hive_boxes/app_config_box.dart
@@ -66,6 +66,46 @@ class AppConfigBox {
}
return HiveUtil.appConfigBox.listenable(keys: listenKeys);
}
+
+ static String? get _languageCode =>
+ HiveUtil.appConfigBox.get(AppConfigType.languageCode.name);
+ static set _languageCode(String? data) {
+ HiveUtil.appConfigBox.put(AppConfigType.languageCode.name, data);
+ }
+
+ static String? get _countryCode =>
+ HiveUtil.appConfigBox.get(AppConfigType.countryCode.name);
+ static set _countryCode(String? data) {
+ HiveUtil.appConfigBox.put(AppConfigType.countryCode.name, data);
+ }
+
+ static Locale? get appLocale {
+ if (_languageCode == null) return null;
+ return Locale(_languageCode!, _countryCode);
+ }
+
+ static set appLocale(Locale? locale) {
+ if (locale == null) {
+ _languageCode = null;
+ _countryCode = null;
+ } else {
+ _languageCode = locale.languageCode;
+ _countryCode = locale.countryCode;
+ }
+ }
+
+ static bool get pureBlackBackground =>
+ HiveUtil.appConfigBox.get(AppConfigType.pureBlackBackground.name) ??
+ false;
+
+ static set pureBlackBackground(bool state) =>
+ HiveUtil.appConfigBox.put(AppConfigType.pureBlackBackground.name, state);
+
+ static bool get useGridLayout =>
+ HiveUtil.appConfigBox.get(AppConfigType.useGridLayout.name) ?? true;
+
+ static set useGridLayout(bool state) =>
+ HiveUtil.appConfigBox.put(AppConfigType.useGridLayout.name, state);
}
extension ThemeModeX on ThemeMode {
@@ -85,4 +125,8 @@ enum AppConfigType {
themeColor,
themeMode,
locale,
+ languageCode,
+ countryCode,
+ pureBlackBackground,
+ useGridLayout,
}
diff --git a/lib/utils/theme_util.dart b/lib/utils/theme_util.dart
index 19d1f05..bc63a74 100644
--- a/lib/utils/theme_util.dart
+++ b/lib/utils/theme_util.dart
@@ -1,6 +1,10 @@
+import 'package:alga/utils/hive_boxes/app_config_box.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
+import 'package:riverpod_annotation/riverpod_annotation.dart';
+
+part 'theme_util.g.dart';
bool isDark(BuildContext context) =>
Theme.of(context).brightness == Brightness.dark;
@@ -20,45 +24,91 @@ final kDefaultDarkColorScheme = ColorScheme.fromSeed(
brightness: Brightness.dark,
);
-class ThemeUtil {
- late ColorScheme colorScheme;
- ThemeUtil(this.colorScheme);
+@Riverpod(keepAlive: true)
+class AppThemeMode extends _$AppThemeMode {
+ @override
+ ThemeMode build() => AppConfigBox.themeMode;
- static final _inputDecorationTheme = InputDecorationTheme(
- border: OutlineInputBorder(
- borderRadius: BorderRadius.circular(8),
- ),
- );
+ void change(ThemeMode mode) {
+ state = mode;
+ AppConfigBox.themeMode = mode;
+ }
+}
- ThemeData getTheme(Brightness brightness) {
- ColorScheme scheme = colorScheme;
- final typo = Typography.material2021(
- platform: defaultTargetPlatform, colorScheme: colorScheme);
- final baseTextStyle = switch (brightness) {
- Brightness.dark => typo.white,
- Brightness.light => typo.black,
- };
- return ThemeData(
- colorScheme: colorScheme,
- splashFactory: InkSparkle.splashFactory,
- inputDecorationTheme: _inputDecorationTheme.copyWith(
- contentPadding: const EdgeInsets.all(12),
- ),
- listTileTheme: ListTileThemeData(
- iconColor: scheme.secondary,
- ),
- snackBarTheme: SnackBarThemeData(
- actionTextColor: scheme.background,
- shape: const RoundedRectangleBorder(
- borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
- ),
+@Riverpod(keepAlive: true)
+class AppLocale extends _$AppLocale {
+ @override
+ Locale? build() => AppConfigBox.appLocale;
+
+ void change(Locale? locale) {
+ AppConfigBox.appLocale = locale;
+ state = locale;
+ }
+}
+
+@Riverpod(keepAlive: true)
+class PureBlackBackground extends _$PureBlackBackground {
+ @override
+ bool build() => AppConfigBox.pureBlackBackground;
+
+ void change(bool value) {
+ state = value;
+ AppConfigBox.pureBlackBackground = value;
+ }
+}
+
+@Riverpod(keepAlive: true)
+class UseGridLayout extends _$UseGridLayout {
+ @override
+ bool build() => AppConfigBox.useGridLayout;
+
+ void change(bool value) {
+ state = value;
+ AppConfigBox.useGridLayout = value;
+ }
+}
+
+@Riverpod(keepAlive: true)
+ThemeData appThemeData(
+ AppThemeDataRef ref, ColorScheme colorScheme, Brightness brightness) {
+ Color? scaffoldColor;
+ if (brightness == Brightness.dark) {
+ final isPureDark = ref.watch(pureBlackBackgroundProvider);
+ if (isPureDark) scaffoldColor = Colors.black;
+ }
+
+ return ThemeData(
+ colorScheme: colorScheme,
+ splashFactory: InkSparkle.splashFactory,
+ scaffoldBackgroundColor: scaffoldColor,
+ inputDecorationTheme: InputDecorationTheme(
+ border: OutlineInputBorder(
+ borderRadius: BorderRadius.circular(8),
),
- textTheme: TextTheme(
- bodyLarge: GoogleFonts.notoSansMonoTextTheme(baseTextStyle)
- .bodyLarge
- ?.copyWith(fontSize: 12),
+ ).copyWith(
+ contentPadding: const EdgeInsets.all(12),
+ ),
+ listTileTheme: ListTileThemeData(
+ iconColor: colorScheme.secondary,
+ ),
+ snackBarTheme: SnackBarThemeData(
+ actionTextColor: colorScheme.background,
+ shape: const RoundedRectangleBorder(
+ borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
- useMaterial3: true,
- );
- }
+ ),
+ );
+}
+
+TextStyle getMonoTextStyle(BuildContext context) {
+ final theme = Theme.of(context);
+ final typo = Typography.material2021(
+ platform: defaultTargetPlatform, colorScheme: theme.colorScheme);
+ final baseTextStyle = switch (theme.brightness) {
+ Brightness.dark => typo.white,
+ Brightness.light => typo.black,
+ };
+ return GoogleFonts.notoSansMonoTextTheme(baseTextStyle)
+ .bodyLarge!
+ .copyWith(fontSize: 12);
}
diff --git a/pubspec.lock b/pubspec.lock
index 701e780..48da220 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -552,10 +552,10 @@ packages:
dependency: "direct main"
description:
name: flutter_riverpod
- sha256: d261b0f2461e0595b96f92ed807841eb72cea84a6b12b8fd0c76e5ed803e7921
+ sha256: da9591d1f8d5881628ccd5c25c40e74fc3eef50ba45e40c3905a06e1712412d5
url: "https://pub.flutter-io.cn"
source: hosted
- version: "2.4.8"
+ version: "2.4.9"
flutter_svg:
dependency: "direct main"
description:
@@ -1168,10 +1168,10 @@ packages:
dependency: transitive
description:
name: riverpod
- sha256: "08451ddbaad6eae73e2422d8109775885623340d721c6637b8719c9f4b478848"
+ sha256: "942999ee48b899f8a46a860f1e13cee36f2f77609eb54c5b7a669bb20d550b11"
url: "https://pub.flutter-io.cn"
source: hosted
- version: "2.4.8"
+ version: "2.4.9"
riverpod_analyzer_utils:
dependency: transitive
description:
@@ -1184,26 +1184,26 @@ packages:
dependency: "direct main"
description:
name: riverpod_annotation
- sha256: "02c9bced96ed3ed8d9970820d1ce7b16600955bc01aa8b2276f09dd3d9d29ed9"
+ sha256: b70e95fbd5ca7ce42f5148092022971bb2e9843b6ab71e97d479e8ab52e98979
url: "https://pub.flutter-io.cn"
source: hosted
- version: "2.3.2"
+ version: "2.3.3"
riverpod_generator:
dependency: "direct dev"
description:
name: riverpod_generator
- sha256: "94b6c49bba879729611d690d434796e3b4e7c72a27e88b482b92c505e90f90d9"
+ sha256: ff8f064f1d7ef3cc6af481bba8e9a3fcdb4d34df34fac1b39bbc003167065be0
url: "https://pub.flutter-io.cn"
source: hosted
- version: "2.3.8"
+ version: "2.3.9"
riverpod_lint:
dependency: "direct dev"
description:
name: riverpod_lint
- sha256: "6fc64ae102ba39b0889b7aa7f4ef6c5a8f71a2ad215b90c787f319a9407a128b"
+ sha256: "944929ef82c9bfeaa455ccab97920abcf847a0ffed5c9f6babc520a95db25176"
url: "https://pub.flutter-io.cn"
source: hosted
- version: "2.3.6"
+ version: "2.3.7"
rxdart:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 7ba4005..265812e 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -39,7 +39,7 @@ dependencies:
flutter_markdown: ^0.6.18+2
url_launcher: ^6.2.1
animations: ^2.0.8
- flutter_riverpod: ^2.4.8
+ flutter_riverpod: ^2.4.9
sass: ^1.69.5
file_selector: ^1.0.1
shelf_static: ^1.1.2
@@ -67,7 +67,7 @@ dependencies:
multi_split_view: ^2.4.0
auto_size_text: ^3.0.0
filesize: ^2.0.1
- riverpod_annotation: ^2.3.2
+ riverpod_annotation: ^2.3.3
file: ^7.0.0
path_provider: ^2.1.1
quick_actions: ^1.0.6
@@ -88,9 +88,9 @@ dev_dependencies:
flutter_lints: ^3.0.1
build_runner: ^2.4.6
msix: ^3.16.6
- riverpod_generator: ^2.3.8
+ riverpod_generator: ^2.3.9
go_router_builder: ^2.3.4
- riverpod_lint: ^2.3.6
+ riverpod_lint: ^2.3.7
custom_lint: ^0.5.7
grinder: ^0.9.5
cli_util: ^0.4.0
@@ -105,11 +105,6 @@ flutter:
- assets/images/
- assets/android/
- fonts:
- - family: Noto Sans Mono
- fonts:
- - asset: assets/fonts/NotoSansMono-Regular.ttf
-
import_sorter:
emojis: true
comments: false