diff --git a/assets/i18n/en.json b/assets/i18n/en.json
index ef4c44c..31316d3 100644
--- a/assets/i18n/en.json
+++ b/assets/i18n/en.json
@@ -285,6 +285,7 @@
"delivery": "Delivery",
"enable_badge": "Enable icon 'badge' bubble",
"enable_notifications": "Enable notifications",
+ "enable_wallpaper": "Enable wallpaper",
"message_notification_content": "Message notification content",
"invitation_accepted": "On invitation accept/reject",
"message_received": "On message received",
diff --git a/assets/images/wallpaper/arctic.svg b/assets/images/wallpaper/arctic.svg
new file mode 100644
index 0000000..ebcaee4
--- /dev/null
+++ b/assets/images/wallpaper/arctic.svg
@@ -0,0 +1,13 @@
+
+
+
diff --git a/assets/images/wallpaper/babydoll.svg b/assets/images/wallpaper/babydoll.svg
new file mode 100644
index 0000000..55f28a3
--- /dev/null
+++ b/assets/images/wallpaper/babydoll.svg
@@ -0,0 +1,663 @@
+
+
+
diff --git a/assets/images/wallpaper/eggplant.svg b/assets/images/wallpaper/eggplant.svg
new file mode 100644
index 0000000..48e6ad3
--- /dev/null
+++ b/assets/images/wallpaper/eggplant.svg
@@ -0,0 +1,495 @@
+
+
+
diff --git a/assets/images/wallpaper/elite.svg b/assets/images/wallpaper/elite.svg
new file mode 100644
index 0000000..606d21f
--- /dev/null
+++ b/assets/images/wallpaper/elite.svg
@@ -0,0 +1,6 @@
+
+
+
diff --git a/assets/images/wallpaper/forest.svg b/assets/images/wallpaper/forest.svg
new file mode 100644
index 0000000..fb61069
--- /dev/null
+++ b/assets/images/wallpaper/forest.svg
@@ -0,0 +1,6888 @@
+
+
+
diff --git a/assets/images/wallpaper/garden.svg b/assets/images/wallpaper/garden.svg
new file mode 100644
index 0000000..f4e6372
--- /dev/null
+++ b/assets/images/wallpaper/garden.svg
@@ -0,0 +1,8182 @@
+
+
+
diff --git a/assets/images/wallpaper/gold.svg b/assets/images/wallpaper/gold.svg
new file mode 100644
index 0000000..16e5ab5
--- /dev/null
+++ b/assets/images/wallpaper/gold.svg
@@ -0,0 +1,26 @@
+
+
+
diff --git a/assets/images/wallpaper/grim.svg b/assets/images/wallpaper/grim.svg
new file mode 100644
index 0000000..7c3968f
--- /dev/null
+++ b/assets/images/wallpaper/grim.svg
@@ -0,0 +1,928 @@
+
+
+
diff --git a/assets/images/wallpaper/lapis.svg b/assets/images/wallpaper/lapis.svg
new file mode 100644
index 0000000..c78fa58
--- /dev/null
+++ b/assets/images/wallpaper/lapis.svg
@@ -0,0 +1,39 @@
+
+
+
diff --git a/assets/images/wallpaper/lime.svg b/assets/images/wallpaper/lime.svg
new file mode 100644
index 0000000..d65222e
--- /dev/null
+++ b/assets/images/wallpaper/lime.svg
@@ -0,0 +1,25 @@
+
+
+
diff --git a/assets/images/wallpaper/scarlet.svg b/assets/images/wallpaper/scarlet.svg
new file mode 100644
index 0000000..7047ca7
--- /dev/null
+++ b/assets/images/wallpaper/scarlet.svg
@@ -0,0 +1,349 @@
+
+
+
diff --git a/assets/images/wallpaper/vapor.svg b/assets/images/wallpaper/vapor.svg
new file mode 100644
index 0000000..34bfe59
--- /dev/null
+++ b/assets/images/wallpaper/vapor.svg
@@ -0,0 +1,25 @@
+
+
+
diff --git a/lib/app.dart b/lib/app.dart
index fade2c8..72d845f 100644
--- a/lib/app.dart
+++ b/lib/app.dart
@@ -6,7 +6,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
-import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
import 'package:provider/provider.dart';
@@ -49,19 +48,24 @@ class VeilidChatApp extends StatelessWidget {
final ThemeData initialThemeData;
void _reloadTheme(BuildContext context) {
- log.info('Reloading theme');
- final theme =
- PreferencesRepository.instance.value.themePreference.themeData();
- ThemeSwitcher.of(context).changeTheme(theme: theme);
-
- // Hack to reload translations
- final localizationDelegate = LocalizedApp.of(context).delegate;
singleFuture(this, () async {
- await LocalizationDelegate.create(
- fallbackLocale: localizationDelegate.fallbackLocale.toString(),
- supportedLocales: localizationDelegate.supportedLocales
- .map((x) => x.toString())
- .toList());
+ log.info('Reloading theme');
+
+ await VeilidChatGlobalInit.loadAssetManifest();
+
+ final theme =
+ PreferencesRepository.instance.value.themePreference.themeData();
+ if (context.mounted) {
+ ThemeSwitcher.of(context).changeTheme(theme: theme);
+
+ // Hack to reload translations
+ final localizationDelegate = LocalizedApp.of(context).delegate;
+ await LocalizationDelegate.create(
+ fallbackLocale: localizationDelegate.fallbackLocale.toString(),
+ supportedLocales: localizationDelegate.supportedLocales
+ .map((x) => x.toString())
+ .toList());
+ }
});
}
@@ -164,17 +168,17 @@ class VeilidChatApp extends StatelessWidget {
scale.primaryScale.subtleBackground,
]);
+ final wallpaper = PreferencesRepository
+ .instance.value.themePreference
+ .wallpaper();
+
return Stack(
fit: StackFit.expand,
alignment: Alignment.center,
children: [
- DecoratedBox(
- decoration: BoxDecoration(gradient: gradient)),
- SvgPicture.asset(
- 'assets/images/grid.svg',
- fit: BoxFit.cover,
- colorFilter: overlayFilter,
- ),
+ wallpaper ??
+ DecoratedBox(
+ decoration: BoxDecoration(gradient: gradient)),
MaterialApp.router(
scrollBehavior: const ScrollBehaviorModified(),
debugShowCheckedModeBanner: false,
diff --git a/lib/chat/views/chat_component_widget.dart b/lib/chat/views/chat_component_widget.dart
index 79d1f1c..8e43299 100644
--- a/lib/chat/views/chat_component_widget.dart
+++ b/lib/chat/views/chat_component_widget.dart
@@ -82,15 +82,16 @@ class ChatComponentWidget extends StatelessWidget {
Widget _buildChatComponent(BuildContext context) {
final theme = Theme.of(context);
- final scale = theme.extension()!;
+ final scaleScheme = theme.extension()!;
final scaleConfig = theme.extension()!;
+ final scale = scaleScheme.scale(ScaleKind.primary);
final textTheme = theme.textTheme;
- final chatTheme = makeChatTheme(scale, scaleConfig, textTheme);
+ final chatTheme = makeChatTheme(scaleScheme, scaleConfig, textTheme);
final errorChatTheme = (ChatThemeEditor(chatTheme)
- ..inputTextColor = scale.errorScale.primary
+ ..inputTextColor = scaleScheme.errorScale.primary
..sendButtonIcon = Image.asset(
'assets/icon-send.png',
- color: scale.errorScale.primary,
+ color: scaleScheme.errorScale.primary,
package: 'flutter_chat_ui',
))
.commit();
@@ -126,7 +127,7 @@ class ChatComponentWidget extends StatelessWidget {
Container(
height: 48,
decoration: BoxDecoration(
- color: scale.primaryScale.subtleBorder,
+ color: scale.border,
),
child: Row(children: [
Align(
@@ -136,12 +137,11 @@ class ChatComponentWidget extends StatelessWidget {
child: Text(title,
textAlign: TextAlign.start,
style: textTheme.titleMedium!
- .copyWith(color: scale.primaryScale.borderText)),
+ .copyWith(color: scale.borderText)),
)),
const Spacer(),
IconButton(
- icon:
- Icon(Icons.close, color: scale.primaryScale.borderText),
+ icon: Icon(Icons.close, color: scale.borderText),
onPressed: _onClose)
.paddingLTRB(16, 0, 16, 0)
]),
@@ -201,54 +201,51 @@ class ChatComponentWidget extends StatelessWidget {
2048;
return Chat(
- key: chatComponentState.chatKey,
- theme:
- messageIsValid ? chatTheme : errorChatTheme,
- messages: messageWindow.window.toList(),
- scrollToBottomOnSend: isFirstPage,
- scrollController:
- chatComponentState.scrollController,
- inputOptions: InputOptions(
- inputClearMode: messageIsValid
- ? InputClearMode.always
- : InputClearMode.never,
- textEditingController:
- chatComponentState.textEditingController),
- // isLastPage: isLastPage,
- // onEndReached: () async {
- // await _handlePageBackward(
- // chatComponentCubit, messageWindow);
- // },
- //onEndReachedThreshold: onEndReachedThreshold,
- //onAttachmentPressed: _handleAttachmentPressed,
- //onMessageTap: _handleMessageTap,
- //onPreviewDataFetched: _handlePreviewDataFetched,
- usePreviewData: false, //
- onSendPressed: (pt) {
- try {
- if (!messageIsValid) {
- context.read().error(
- text:
- translate('chat.message_too_long'));
- return;
- }
- _handleSendPressed(chatComponentCubit, pt);
- } on FormatException {
- context.read().error(
- text: translate('chat.message_too_long'));
- }
- },
- listBottomWidget: messageIsValid
- ? null
- : Text(translate('chat.message_too_long'),
- style: TextStyle(
- color: scale.errorScale.primary))
- .toCenter(),
- //showUserAvatars: false,
- //showUserNames: true,
- user: localUser,
- emptyState: const EmptyChatWidget())
- .paddingLTRB(0, 2, 0, 0);
+ key: chatComponentState.chatKey,
+ theme: messageIsValid ? chatTheme : errorChatTheme,
+ messages: messageWindow.window.toList(),
+ scrollToBottomOnSend: isFirstPage,
+ scrollController: chatComponentState.scrollController,
+ inputOptions: InputOptions(
+ inputClearMode: messageIsValid
+ ? InputClearMode.always
+ : InputClearMode.never,
+ textEditingController:
+ chatComponentState.textEditingController),
+ // isLastPage: isLastPage,
+ // onEndReached: () async {
+ // await _handlePageBackward(
+ // chatComponentCubit, messageWindow);
+ // },
+ //onEndReachedThreshold: onEndReachedThreshold,
+ //onAttachmentPressed: _handleAttachmentPressed,
+ //onMessageTap: _handleMessageTap,
+ //onPreviewDataFetched: _handlePreviewDataFetched,
+ usePreviewData: false, //
+ onSendPressed: (pt) {
+ try {
+ if (!messageIsValid) {
+ context.read().error(
+ text: translate('chat.message_too_long'));
+ return;
+ }
+ _handleSendPressed(chatComponentCubit, pt);
+ } on FormatException {
+ context.read().error(
+ text: translate('chat.message_too_long'));
+ }
+ },
+ listBottomWidget: messageIsValid
+ ? null
+ : Text(translate('chat.message_too_long'),
+ style: TextStyle(
+ color:
+ scaleScheme.errorScale.primary))
+ .toCenter(),
+ //showUserAvatars: false,
+ //showUserNames: true,
+ user: localUser,
+ emptyState: const EmptyChatWidget());
}))).expanded(),
],
);
diff --git a/lib/chat/views/no_conversation_widget.dart b/lib/chat/views/no_conversation_widget.dart
index df1b6d3..830e0d6 100644
--- a/lib/chat/views/no_conversation_widget.dart
+++ b/lib/chat/views/no_conversation_widget.dart
@@ -12,11 +12,13 @@ class NoConversationWidget extends StatelessWidget {
BuildContext context,
) {
final theme = Theme.of(context);
- final scale = theme.extension()!;
+ final scaleScheme = theme.extension()!;
+ final scaleConfig = theme.extension()!;
+ final scale = scaleScheme.scale(ScaleKind.primary);
return DecoratedBox(
decoration: BoxDecoration(
- color: scale.primaryScale.appBackground.withAlpha(192),
+ color: scale.appBackground.withAlpha(scaleConfig.wallpaperAlpha),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
@@ -24,14 +26,14 @@ class NoConversationWidget extends StatelessWidget {
children: [
Icon(
Icons.diversity_3,
- color: scale.primaryScale.appText.withAlpha(127),
+ color: scale.appText.withAlpha(127),
size: 48,
),
Text(
textAlign: TextAlign.center,
translate('chat.start_a_conversation'),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
- color: scale.primaryScale.appText.withAlpha(127),
+ color: scale.appText.withAlpha(127),
),
),
],
diff --git a/lib/init.dart b/lib/init.dart
index d2744c7..8c80bf9 100644
--- a/lib/init.dart
+++ b/lib/init.dart
@@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
+import 'package:flutter/services.dart';
import 'package:veilid_support/veilid_support.dart';
import 'account_manager/account_manager.dart';
@@ -8,6 +9,8 @@ import 'app.dart';
import 'tools/tools.dart';
import 'veilid_processor/veilid_processor.dart';
+List rootAssets = [];
+
class VeilidChatGlobalInit {
VeilidChatGlobalInit._();
@@ -28,14 +31,22 @@ class VeilidChatGlobalInit {
logger: (message) => log.debug('DHTRecordPool: $message'));
}
-// Initialize repositories
+ // Initialize repositories
Future _initializeRepositories() async {
await AccountRepository.instance.init();
}
+ // Initialize asset manifest
+ static Future loadAssetManifest() async {
+ final assetManifest = await AssetManifest.loadFromAssetBundle(rootBundle);
+ rootAssets = assetManifest.listAssets();
+ }
+
static Future initialize() async {
final veilidChatGlobalInit = VeilidChatGlobalInit._();
+ await loadAssetManifest();
+
log.info('Initializing Veilid');
await veilidChatGlobalInit._initializeVeilid();
log.info('Initializing Repositories');
diff --git a/lib/layout/home/drawer_menu/drawer_menu.dart b/lib/layout/home/drawer_menu/drawer_menu.dart
index b56d437..f88a888 100644
--- a/lib/layout/home/drawer_menu/drawer_menu.dart
+++ b/lib/layout/home/drawer_menu/drawer_menu.dart
@@ -320,29 +320,7 @@ class _DrawerMenuState extends State {
return DecoratedBox(
decoration: ShapeDecoration(
- shadows: [
- if (scaleConfig.useVisualIndicators && !scaleConfig.preferBorders)
- BoxShadow(
- color: scale.primary.darken(60),
- spreadRadius: 2,
- )
- else if (scaleConfig.useVisualIndicators &&
- scaleConfig.preferBorders)
- BoxShadow(
- color: scale.border,
- spreadRadius: 2,
- )
- else
- BoxShadow(
- color: scale.appBackground.darken(60).withAlpha(0x3F),
- blurRadius: 16,
- spreadRadius: 2,
- offset: const Offset(
- 0,
- 2,
- ),
- ),
- ],
+ shadows: themedShadow(scaleConfig, scale),
gradient: scaleConfig.useVisualIndicators ? null : gradient,
color: scaleConfig.useVisualIndicators
? (scaleConfig.preferBorders
@@ -381,7 +359,7 @@ class _DrawerMenuState extends State {
'assets/images/title.svg',
colorFilter: scaleConfig.useVisualIndicators
? grayColorFilter
- : dodgeFilter),
+ : src96StencilFilter),
]))),
Text(translate('menu.accounts'),
style: theme.textTheme.titleMedium!.copyWith(
diff --git a/lib/layout/home/home_account_ready.dart b/lib/layout/home/home_account_ready.dart
index 7b67c0f..8710eea 100644
--- a/lib/layout/home/home_account_ready.dart
+++ b/lib/layout/home/home_account_ready.dart
@@ -146,8 +146,9 @@ class _HomeAccountReadyState extends State {
);
final theme = Theme.of(context);
- final scale = theme.extension()!;
+ final scaleScheme = theme.extension()!;
final scaleConfig = theme.extension()!;
+ final scale = scaleScheme.scale(ScaleKind.primary);
final activeChat = context.watch().state;
final hasActiveChat = activeChat != null;
@@ -163,7 +164,9 @@ class _HomeAccountReadyState extends State {
visibleLeft = true;
visibleRight = true;
leftWidth = leftColumnSize;
- rightWidth = constraints.maxWidth - leftColumnSize - 2;
+ rightWidth = constraints.maxWidth -
+ leftColumnSize -
+ (scaleConfig.useVisualIndicators ? 2 : 0);
} else {
if (hasActiveChat) {
visibleLeft = false;
@@ -180,19 +183,21 @@ class _HomeAccountReadyState extends State {
return Row(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
Offstage(
- offstage: !visibleLeft,
- child: ConstrainedBox(
- constraints: BoxConstraints(maxWidth: leftWidth),
- child: buildLeftPane(context))),
- Offstage(
- offstage: !(visibleLeft && visibleRight),
- child: SizedBox(
- width: 2,
- height: double.infinity,
- child: ColoredBox(
- color: scaleConfig.preferBorders
- ? scale.primaryScale.subtleBorder
- : scale.primaryScale.subtleBackground))),
+ offstage: !visibleLeft,
+ child: ConstrainedBox(
+ constraints: BoxConstraints(maxWidth: leftWidth),
+ child: buildLeftPane(context)))
+ .withThemedShadow(scaleConfig, scale),
+ if (scaleConfig.useVisualIndicators)
+ Offstage(
+ offstage: !(visibleLeft && visibleRight),
+ child: SizedBox(
+ width: 2,
+ height: double.infinity,
+ child: ColoredBox(
+ color: scaleConfig.preferBorders
+ ? scale.subtleBorder
+ : scale.subtleBackground))),
Offstage(
offstage: !visibleRight,
child: ConstrainedBox(
diff --git a/lib/layout/home/home_screen.dart b/lib/layout/home/home_screen.dart
index 3e8e98b..0ec1f26 100644
--- a/lib/layout/home/home_screen.dart
+++ b/lib/layout/home/home_screen.dart
@@ -229,6 +229,7 @@ class HomeScreenState extends State
angle: 0,
//mainScreenOverlayColor: theme.shadowColor.withAlpha(0x2F),
openCurve: Curves.fastEaseInToSlowEaseOut,
+ closeCurve: Curves.fastEaseInToSlowEaseOut,
// duration: const Duration(milliseconds: 250),
// reverseDuration: const Duration(milliseconds: 250),
menuScreenTapClose: canClose,
diff --git a/lib/settings/settings_page.dart b/lib/settings/settings_page.dart
index 05ba514..f164992 100644
--- a/lib/settings/settings_page.dart
+++ b/lib/settings/settings_page.dart
@@ -53,6 +53,8 @@ class SettingsPageState extends State {
.paddingLTRB(0, 8, 0, 0),
buildSettingsPageBrightnessPreferences(
context: context, onChanged: () => setState(() {})),
+ buildSettingsPageWallpaperPreferences(
+ context: context, onChanged: () => setState(() {})),
buildSettingsPageNotificationPreferences(
context: context, onChanged: () => setState(() {})),
].map((x) => x.paddingLTRB(0, 0, 0, 8)).toList(),
diff --git a/lib/theme/models/chat_theme.dart b/lib/theme/models/chat_theme.dart
index cd0b9ce..5552979 100644
--- a/lib/theme/models/chat_theme.dart
+++ b/lib/theme/models/chat_theme.dart
@@ -14,14 +14,15 @@ ChatTheme makeChatTheme(
secondaryColor: scaleConfig.preferBorders
? scale.secondaryScale.calloutText
: scale.secondaryScale.calloutBackground,
- backgroundColor: scale.grayScale.appBackground.withAlpha(192),
+ backgroundColor:
+ scale.grayScale.appBackground.withAlpha(scaleConfig.wallpaperAlpha),
messageBorderRadius: scaleConfig.borderRadiusScale * 16,
bubbleBorderSide: scaleConfig.preferBorders
? BorderSide(
color: scale.primaryScale.calloutBackground,
width: 2,
)
- : null,
+ : BorderSide(width: 2, color: Colors.black.withAlpha(96)),
sendButtonIcon: Image.asset(
'assets/icon-send.png',
color: scaleConfig.preferBorders
@@ -86,7 +87,8 @@ ChatTheme makeChatTheme(
receivedEmojiMessageTextStyle: const TextStyle(
color: Colors.white,
fontSize: 64,
- ));
+ ),
+ dateDividerTextStyle: textTheme.labelSmall!);
class EditedChatTheme extends ChatTheme {
const EditedChatTheme({
diff --git a/lib/theme/models/radix_generator.dart b/lib/theme/models/radix_generator.dart
index a5c5f87..3e3b0e6 100644
--- a/lib/theme/models/radix_generator.dart
+++ b/lib/theme/models/radix_generator.dart
@@ -603,6 +603,33 @@ TextTheme makeRadixTextTheme(Brightness brightness) {
return textTheme;
}
+double wallpaperAlpha(Brightness brightness, RadixThemeColor themeColor) {
+ switch (themeColor) {
+ case RadixThemeColor.scarlet:
+ return 64;
+ case RadixThemeColor.babydoll:
+ return 192;
+ case RadixThemeColor.vapor:
+ return 192;
+ case RadixThemeColor.gold:
+ return 192;
+ case RadixThemeColor.garden:
+ return brightness == Brightness.dark ? 192 : 128;
+ case RadixThemeColor.forest:
+ return 192;
+ case RadixThemeColor.arctic:
+ return brightness == Brightness.dark ? 208 : 180;
+ case RadixThemeColor.lapis:
+ return brightness == Brightness.dark ? 128 : 192;
+ case RadixThemeColor.eggplant:
+ return brightness == Brightness.dark ? 192 : 192;
+ case RadixThemeColor.lime:
+ return brightness == Brightness.dark ? 192 : 128;
+ case RadixThemeColor.grim:
+ return brightness == Brightness.dark ? 240 : 224;
+ }
+}
+
ThemeData radixGenerator(Brightness brightness, RadixThemeColor themeColor) {
final textTheme = makeRadixTextTheme(brightness);
final radix = _radixScheme(brightness, themeColor);
@@ -611,6 +638,7 @@ ThemeData radixGenerator(Brightness brightness, RadixThemeColor themeColor) {
useVisualIndicators: false,
preferBorders: false,
borderRadiusScale: 1,
+ wallpaperAlpha: wallpaperAlpha(brightness, themeColor),
);
final scaleTheme = ScaleTheme(
diff --git a/lib/theme/models/scale_theme/scale_scheme.dart b/lib/theme/models/scale_theme/scale_scheme.dart
index 8c4a6b8..6bdae25 100644
--- a/lib/theme/models/scale_theme/scale_scheme.dart
+++ b/lib/theme/models/scale_theme/scale_scheme.dart
@@ -111,22 +111,27 @@ class ScaleConfig extends ThemeExtension {
required this.useVisualIndicators,
required this.preferBorders,
required this.borderRadiusScale,
- });
+ required double wallpaperAlpha,
+ }) : _wallpaperAlpha = wallpaperAlpha;
final bool useVisualIndicators;
final bool preferBorders;
final double borderRadiusScale;
+ final double _wallpaperAlpha;
+
+ int get wallpaperAlpha => _wallpaperAlpha.toInt();
@override
- ScaleConfig copyWith({
- bool? useVisualIndicators,
- bool? preferBorders,
- double? borderRadiusScale,
- }) =>
+ ScaleConfig copyWith(
+ {bool? useVisualIndicators,
+ bool? preferBorders,
+ double? borderRadiusScale,
+ double? wallpaperAlpha}) =>
ScaleConfig(
useVisualIndicators: useVisualIndicators ?? this.useVisualIndicators,
preferBorders: preferBorders ?? this.preferBorders,
borderRadiusScale: borderRadiusScale ?? this.borderRadiusScale,
+ wallpaperAlpha: wallpaperAlpha ?? this._wallpaperAlpha,
);
@override
@@ -139,6 +144,8 @@ class ScaleConfig extends ThemeExtension {
t < .5 ? useVisualIndicators : other.useVisualIndicators,
preferBorders: t < .5 ? preferBorders : other.preferBorders,
borderRadiusScale:
- lerpDouble(borderRadiusScale, other.borderRadiusScale, t) ?? 1);
+ lerpDouble(borderRadiusScale, other.borderRadiusScale, t) ?? 1,
+ wallpaperAlpha:
+ lerpDouble(_wallpaperAlpha, other._wallpaperAlpha, t) ?? 1);
}
}
diff --git a/lib/theme/models/theme_preference.dart b/lib/theme/models/theme_preference.dart
index 9d6f6dc..dc2e082 100644
--- a/lib/theme/models/theme_preference.dart
+++ b/lib/theme/models/theme_preference.dart
@@ -1,7 +1,10 @@
import 'package:change_case/change_case.dart';
import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_svg/svg.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
+import '../../init.dart';
import '../views/widget_helpers.dart';
import 'contrast_generator.dart';
import 'radix_generator.dart';
@@ -53,6 +56,7 @@ class ThemePreferences with _$ThemePreferences {
BrightnessPreference brightnessPreference,
@Default(ColorPreference.vapor) ColorPreference colorPreference,
@Default(1) double displayScale,
+ @Default(true) bool enableWallpaper,
}) = _ThemePreferences;
factory ThemePreferences.fromJson(dynamic json) =>
@@ -62,6 +66,17 @@ class ThemePreferences with _$ThemePreferences {
}
extension ThemePreferencesExt on ThemePreferences {
+ /// Get wallpaper for existing theme
+ Widget? wallpaper() {
+ if (enableWallpaper) {
+ final assetName = 'assets/images/wallpaper/${colorPreference.name}.svg';
+ if (rootAssets.contains(assetName)) {
+ return SvgPicture.asset(assetName, fit: BoxFit.cover);
+ }
+ }
+ return null;
+ }
+
/// Get material 'ThemeData' for existing theme
ThemeData themeData() {
late final Brightness brightness;
@@ -87,7 +102,8 @@ extension ThemePreferencesExt on ThemePreferences {
scaleConfig: ScaleConfig(
useVisualIndicators: true,
preferBorders: false,
- borderRadiusScale: 1),
+ borderRadiusScale: 1,
+ wallpaperAlpha: 255),
primaryFront: Colors.black,
primaryBack: Colors.white,
secondaryFront: Colors.black,
@@ -106,7 +122,8 @@ extension ThemePreferencesExt on ThemePreferences {
scaleConfig: ScaleConfig(
useVisualIndicators: true,
preferBorders: true,
- borderRadiusScale: 0.2),
+ borderRadiusScale: 0.2,
+ wallpaperAlpha: 208),
primaryFront: const Color(0xFF000000),
primaryBack: const Color(0xFF00FF00),
secondaryFront: const Color(0xFF000000),
@@ -123,7 +140,8 @@ extension ThemePreferencesExt on ThemePreferences {
scaleConfig: ScaleConfig(
useVisualIndicators: true,
preferBorders: true,
- borderRadiusScale: 0.2),
+ borderRadiusScale: 0.2,
+ wallpaperAlpha: 192),
primaryFront: const Color(0xFF000000),
primaryBack: const Color(0xFF00FF00),
secondaryFront: const Color(0xFF000000),
diff --git a/lib/theme/models/theme_preference.freezed.dart b/lib/theme/models/theme_preference.freezed.dart
index 657d021..d96ed38 100644
--- a/lib/theme/models/theme_preference.freezed.dart
+++ b/lib/theme/models/theme_preference.freezed.dart
@@ -24,6 +24,7 @@ mixin _$ThemePreferences {
throw _privateConstructorUsedError;
ColorPreference get colorPreference => throw _privateConstructorUsedError;
double get displayScale => throw _privateConstructorUsedError;
+ bool get enableWallpaper => throw _privateConstructorUsedError;
/// Serializes this ThemePreferences to a JSON map.
Map toJson() => throw _privateConstructorUsedError;
@@ -44,7 +45,8 @@ abstract class $ThemePreferencesCopyWith<$Res> {
$Res call(
{BrightnessPreference brightnessPreference,
ColorPreference colorPreference,
- double displayScale});
+ double displayScale,
+ bool enableWallpaper});
}
/// @nodoc
@@ -65,6 +67,7 @@ class _$ThemePreferencesCopyWithImpl<$Res, $Val extends ThemePreferences>
Object? brightnessPreference = null,
Object? colorPreference = null,
Object? displayScale = null,
+ Object? enableWallpaper = null,
}) {
return _then(_value.copyWith(
brightnessPreference: null == brightnessPreference
@@ -79,6 +82,10 @@ class _$ThemePreferencesCopyWithImpl<$Res, $Val extends ThemePreferences>
? _value.displayScale
: displayScale // ignore: cast_nullable_to_non_nullable
as double,
+ enableWallpaper: null == enableWallpaper
+ ? _value.enableWallpaper
+ : enableWallpaper // ignore: cast_nullable_to_non_nullable
+ as bool,
) as $Val);
}
}
@@ -94,7 +101,8 @@ abstract class _$$ThemePreferencesImplCopyWith<$Res>
$Res call(
{BrightnessPreference brightnessPreference,
ColorPreference colorPreference,
- double displayScale});
+ double displayScale,
+ bool enableWallpaper});
}
/// @nodoc
@@ -113,6 +121,7 @@ class __$$ThemePreferencesImplCopyWithImpl<$Res>
Object? brightnessPreference = null,
Object? colorPreference = null,
Object? displayScale = null,
+ Object? enableWallpaper = null,
}) {
return _then(_$ThemePreferencesImpl(
brightnessPreference: null == brightnessPreference
@@ -127,6 +136,10 @@ class __$$ThemePreferencesImplCopyWithImpl<$Res>
? _value.displayScale
: displayScale // ignore: cast_nullable_to_non_nullable
as double,
+ enableWallpaper: null == enableWallpaper
+ ? _value.enableWallpaper
+ : enableWallpaper // ignore: cast_nullable_to_non_nullable
+ as bool,
));
}
}
@@ -137,7 +150,8 @@ class _$ThemePreferencesImpl implements _ThemePreferences {
const _$ThemePreferencesImpl(
{this.brightnessPreference = BrightnessPreference.system,
this.colorPreference = ColorPreference.vapor,
- this.displayScale = 1});
+ this.displayScale = 1,
+ this.enableWallpaper = true});
factory _$ThemePreferencesImpl.fromJson(Map json) =>
_$$ThemePreferencesImplFromJson(json);
@@ -151,10 +165,13 @@ class _$ThemePreferencesImpl implements _ThemePreferences {
@override
@JsonKey()
final double displayScale;
+ @override
+ @JsonKey()
+ final bool enableWallpaper;
@override
String toString() {
- return 'ThemePreferences(brightnessPreference: $brightnessPreference, colorPreference: $colorPreference, displayScale: $displayScale)';
+ return 'ThemePreferences(brightnessPreference: $brightnessPreference, colorPreference: $colorPreference, displayScale: $displayScale, enableWallpaper: $enableWallpaper)';
}
@override
@@ -167,13 +184,15 @@ class _$ThemePreferencesImpl implements _ThemePreferences {
(identical(other.colorPreference, colorPreference) ||
other.colorPreference == colorPreference) &&
(identical(other.displayScale, displayScale) ||
- other.displayScale == displayScale));
+ other.displayScale == displayScale) &&
+ (identical(other.enableWallpaper, enableWallpaper) ||
+ other.enableWallpaper == enableWallpaper));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
- int get hashCode => Object.hash(
- runtimeType, brightnessPreference, colorPreference, displayScale);
+ int get hashCode => Object.hash(runtimeType, brightnessPreference,
+ colorPreference, displayScale, enableWallpaper);
/// Create a copy of ThemePreferences
/// with the given fields replaced by the non-null parameter values.
@@ -196,7 +215,8 @@ abstract class _ThemePreferences implements ThemePreferences {
const factory _ThemePreferences(
{final BrightnessPreference brightnessPreference,
final ColorPreference colorPreference,
- final double displayScale}) = _$ThemePreferencesImpl;
+ final double displayScale,
+ final bool enableWallpaper}) = _$ThemePreferencesImpl;
factory _ThemePreferences.fromJson(Map json) =
_$ThemePreferencesImpl.fromJson;
@@ -207,6 +227,8 @@ abstract class _ThemePreferences implements ThemePreferences {
ColorPreference get colorPreference;
@override
double get displayScale;
+ @override
+ bool get enableWallpaper;
/// Create a copy of ThemePreferences
/// with the given fields replaced by the non-null parameter values.
diff --git a/lib/theme/models/theme_preference.g.dart b/lib/theme/models/theme_preference.g.dart
index 4cb2d71..23c3d38 100644
--- a/lib/theme/models/theme_preference.g.dart
+++ b/lib/theme/models/theme_preference.g.dart
@@ -16,6 +16,7 @@ _$ThemePreferencesImpl _$$ThemePreferencesImplFromJson(
? ColorPreference.vapor
: ColorPreference.fromJson(json['color_preference']),
displayScale: (json['display_scale'] as num?)?.toDouble() ?? 1,
+ enableWallpaper: json['enable_wallpaper'] as bool? ?? true,
);
Map _$$ThemePreferencesImplToJson(
@@ -24,4 +25,5 @@ Map _$$ThemePreferencesImplToJson(
'brightness_preference': instance.brightnessPreference.toJson(),
'color_preference': instance.colorPreference.toJson(),
'display_scale': instance.displayScale,
+ 'enable_wallpaper': instance.enableWallpaper,
};
diff --git a/lib/theme/views/styled_scaffold.dart b/lib/theme/views/styled_scaffold.dart
index 9ee3d36..9d02fab 100644
--- a/lib/theme/views/styled_scaffold.dart
+++ b/lib/theme/views/styled_scaffold.dart
@@ -9,8 +9,9 @@ class StyledScaffold extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
- final scale = theme.extension()!;
+ final scaleScheme = theme.extension()!;
final scaleConfig = theme.extension()!;
+ final scale = scaleScheme.scale(ScaleKind.primary);
final enableBorder = !isMobileSize(context);
@@ -18,13 +19,11 @@ class StyledScaffold extends StatelessWidget {
clipEnabled: enableBorder,
borderEnabled: scaleConfig.useVisualIndicators,
borderRadius: 16 * scaleConfig.borderRadiusScale,
- borderColor: scale.primaryScale.border,
+ borderColor: scale.border,
child: Scaffold(appBar: appBar, body: body, key: key));
if (!scaleConfig.useVisualIndicators) {
- scaffold = scaffold.withShadow(
- offset: const Offset(0, 16),
- shadowColor: scale.primaryScale.primary.withAlpha(0x3F).darken(60));
+ scaffold = scaffold.withThemedShadow(scaleConfig, scale);
}
return GestureDetector(
diff --git a/lib/theme/views/views.dart b/lib/theme/views/views.dart
index b5aa809..88f4a4a 100644
--- a/lib/theme/views/views.dart
+++ b/lib/theme/views/views.dart
@@ -12,4 +12,5 @@ export 'slider_tile.dart';
export 'styled_alert.dart';
export 'styled_dialog.dart';
export 'styled_scaffold.dart';
+export 'wallpaper_preferences.dart';
export 'widget_helpers.dart';
diff --git a/lib/theme/views/wallpaper_preferences.dart b/lib/theme/views/wallpaper_preferences.dart
new file mode 100644
index 0000000..1c2e9de
--- /dev/null
+++ b/lib/theme/views/wallpaper_preferences.dart
@@ -0,0 +1,32 @@
+import 'package:animated_theme_switcher/animated_theme_switcher.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_form_builder/flutter_form_builder.dart';
+import 'package:flutter_translate/flutter_translate.dart';
+
+import '../../settings/settings.dart';
+import '../models/models.dart';
+
+const String formFieldEnableWallpaper = 'enable_wallpaper';
+
+Widget buildSettingsPageWallpaperPreferences(
+ {required BuildContext context, required void Function() onChanged}) {
+ final preferencesRepository = PreferencesRepository.instance;
+ final themePreferences = preferencesRepository.value.themePreference;
+ return ThemeSwitcher.withTheme(
+ builder: (_, switcher, theme) => FormBuilderCheckbox(
+ name: formFieldEnableWallpaper,
+ title: Text(translate('settings_page.enable_wallpaper')),
+ initialValue: themePreferences.enableWallpaper,
+ onChanged: (value) async {
+ if (value != null) {
+ final newThemePrefs =
+ themePreferences.copyWith(enableWallpaper: value);
+ final newPrefs = preferencesRepository.value
+ .copyWith(themePreference: newThemePrefs);
+
+ await preferencesRepository.set(newPrefs);
+ switcher.changeTheme(theme: newThemePrefs.themeData());
+ onChanged();
+ }
+ }));
+}
diff --git a/lib/theme/views/widget_helpers.dart b/lib/theme/views/widget_helpers.dart
index f66af4b..1b768dd 100644
--- a/lib/theme/views/widget_helpers.dart
+++ b/lib/theme/views/widget_helpers.dart
@@ -19,6 +19,40 @@ extension BorderExt on Widget {
child: this);
}
+extension ShadowExt on Widget {
+ Container withThemedShadow(ScaleConfig scaleConfig, ScaleColor scale) =>
+ // ignore: use_decorated_box
+ Container(
+ decoration: BoxDecoration(
+ boxShadow: themedShadow(scaleConfig, scale),
+ ),
+ child: this,
+ );
+}
+
+List themedShadow(ScaleConfig scaleConfig, ScaleColor scale) => [
+ if (scaleConfig.useVisualIndicators && !scaleConfig.preferBorders)
+ BoxShadow(
+ color: scale.primary.darken(60),
+ spreadRadius: 2,
+ )
+ else if (scaleConfig.useVisualIndicators && scaleConfig.preferBorders)
+ BoxShadow(
+ color: scale.border,
+ spreadRadius: 2,
+ )
+ else
+ BoxShadow(
+ color: scale.primary.darken(60).withAlpha(0x7F),
+ blurRadius: 16,
+ spreadRadius: 2,
+ offset: const Offset(
+ 0,
+ 2,
+ ),
+ ),
+ ];
+
extension SizeToFixExt on Widget {
FittedBox fit({BoxFit? fit, Key? key}) => FittedBox(
key: key,
@@ -524,10 +558,10 @@ const grayColorFilter = ColorFilter.matrix([
0,
]);
-const dodgeFilter =
+const src96StencilFilter =
ColorFilter.mode(Color.fromARGB(96, 255, 255, 255), BlendMode.srcIn);
-const overlayFilter =
+const dst127StencilFilter =
ColorFilter.mode(Color.fromARGB(127, 255, 255, 255), BlendMode.dstIn);
Container clipBorder({
@@ -551,6 +585,6 @@ Container clipBorder({
child: ClipRRect(
clipBehavior: Clip.antiAliasWithSaveLayer,
borderRadius: clipEnabled
- ? BorderRadius.circular(borderRadius - 2)
+ ? BorderRadius.circular(borderRadius - 4)
: BorderRadius.zero,
child: child));
diff --git a/pubspec.yaml b/pubspec.yaml
index 94f8952..5f924ba 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -159,8 +159,20 @@ flutter:
- assets/i18n/en.json
# Launcher icon
- assets/launcher/icon.png
+ # Theme wallpaper
+ - assets/images/wallpaper/arctic.svg
+ - assets/images/wallpaper/babydoll.svg
+ - assets/images/wallpaper/eggplant.svg
+ - assets/images/wallpaper/elite.svg
+ - assets/images/wallpaper/forest.svg
+ - assets/images/wallpaper/garden.svg
+ - assets/images/wallpaper/gold.svg
+ - assets/images/wallpaper/grim.svg
+ - assets/images/wallpaper/lapis.svg
+ - assets/images/wallpaper/lime.svg
+ - assets/images/wallpaper/scarlet.svg
+ - assets/images/wallpaper/vapor.svg
# Vector Images
- - assets/images/grid.svg
- assets/images/icon.svg
- assets/images/splash.svg
- assets/images/title.svg