Compare commits
7 commits
main
...
refactor/m
Author | SHA1 | Date | |
---|---|---|---|
|
3b8656a603 | ||
|
85841e3349 | ||
|
966b38b99f | ||
|
748821d7a7 | ||
|
ecb1669ab8 | ||
|
780d3b174d | ||
|
d20801c65c |
66 changed files with 712 additions and 700 deletions
|
@ -1,5 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
const Color immichBackgroundColor = Color(0xFFf6f8fe);
|
||||
const Color immichDarkBackgroundColor = Color.fromARGB(255, 0, 0, 0);
|
||||
const Color immichDarkThemePrimaryColor = Color.fromARGB(255, 173, 203, 250);
|
|
@ -1,5 +1,7 @@
|
|||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_mobile/utils/immich_app_theme.dart';
|
||||
|
||||
extension ContextHelper on BuildContext {
|
||||
// Returns the current size from MediaQuery
|
||||
|
@ -32,6 +34,12 @@ extension ContextHelper on BuildContext {
|
|||
// Current ColorScheme used
|
||||
ColorScheme get colorScheme => themeData.colorScheme;
|
||||
|
||||
// Red Accent harmonized with primary color
|
||||
Color get redColor => redAccent.harmonizeWith(primaryColor);
|
||||
|
||||
// Orange Accent harmonized with primary color
|
||||
Color get orangeColor => orangeAccent.harmonizeWith(primaryColor);
|
||||
|
||||
// Pop-out from the current context with optional result
|
||||
void pop<T>([T? result]) => Navigator.of(this).pop(result);
|
||||
|
||||
|
|
27
mobile/lib/extensions/color_extensions.dart
Normal file
27
mobile/lib/extensions/color_extensions.dart
Normal file
|
@ -0,0 +1,27 @@
|
|||
import 'dart:ui';
|
||||
|
||||
extension LightenDarken on Color {
|
||||
/// Darken a color by [percent] amount (100 = black)
|
||||
Color darken([int percent = 10]) {
|
||||
assert(1 <= percent && percent <= 100);
|
||||
var f = 1 - percent / 100;
|
||||
return Color.fromARGB(
|
||||
alpha,
|
||||
(red * f).round(),
|
||||
(green * f).round(),
|
||||
(blue * f).round(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Lighten a color by [percent] amount (100 = white)
|
||||
Color lighten([int percent = 10]) {
|
||||
assert(1 <= percent && percent <= 100);
|
||||
var p = percent / 100;
|
||||
return Color.fromARGB(
|
||||
alpha,
|
||||
red + ((255 - red) * p).round(),
|
||||
green + ((255 - green) * p).round(),
|
||||
blue + ((255 - blue) * p).round(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/shared/providers/theme.provider.dart';
|
||||
import 'package:timezone/data/latest.dart';
|
||||
import 'package:immich_mobile/constants/locales.dart';
|
||||
import 'package:immich_mobile/modules/backup/background_service/background.service.dart';
|
||||
|
|
|
@ -50,9 +50,8 @@ class ActivitiesPage extends HookConsumerWidget {
|
|||
);
|
||||
|
||||
buildTitleWithTimestamp(Activity activity, {bool leftAlign = true}) {
|
||||
final textColor = context.isDarkTheme ? Colors.white : Colors.black;
|
||||
final textStyle = context.textTheme.bodyMedium
|
||||
?.copyWith(color: textColor.withOpacity(0.6));
|
||||
final textStyle = context.textTheme.bodyMedium?.copyWith(
|
||||
color: context.textTheme.bodyMedium?.color?.withOpacity(0.6),);
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: leftAlign
|
||||
|
@ -150,14 +149,14 @@ class ActivitiesPage extends HookConsumerWidget {
|
|||
},
|
||||
),
|
||||
),
|
||||
suffixIconColor: liked ? Colors.red[700] : null,
|
||||
suffixIconColor: liked ? context.redColor : null,
|
||||
hintText: isReadOnly
|
||||
? 'shared_album_activities_input_disable'.tr()
|
||||
: 'shared_album_activities_input_hint'.tr(),
|
||||
hintStyle: TextStyle(
|
||||
fontWeight: FontWeight.normal,
|
||||
fontSize: 14,
|
||||
color: Colors.grey[600],
|
||||
color: context.themeData.hintColor,
|
||||
),
|
||||
),
|
||||
onEditingComplete: () async {
|
||||
|
@ -200,27 +199,31 @@ class ActivitiesPage extends HookConsumerWidget {
|
|||
onDismissed: (direction) async =>
|
||||
await ref.read(provider.notifier).removeActivity(activity.id),
|
||||
background: Container(
|
||||
color: canDelete ? Colors.red[400] : Colors.grey[600],
|
||||
color: canDelete
|
||||
? context.colorScheme.error
|
||||
: context.themeData.disabledColor,
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
child: canDelete
|
||||
? const Padding(
|
||||
padding: EdgeInsets.all(15),
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: Icon(
|
||||
Icons.delete_sweep_rounded,
|
||||
color: Colors.black,
|
||||
color: context.colorScheme.surface,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
secondaryBackground: Container(
|
||||
color: canDelete ? Colors.red[400] : Colors.grey[600],
|
||||
color: canDelete
|
||||
? context.colorScheme.error
|
||||
: context.themeData.disabledColor,
|
||||
alignment: AlignmentDirectional.centerEnd,
|
||||
child: canDelete
|
||||
? const Padding(
|
||||
padding: EdgeInsets.all(15),
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: Icon(
|
||||
Icons.delete_sweep_rounded,
|
||||
color: Colors.black,
|
||||
color: context.colorScheme.surface,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
|
@ -285,7 +288,7 @@ class ActivitiesPage extends HookConsumerWidget {
|
|||
alignment: Alignment.center,
|
||||
child: Icon(
|
||||
Icons.favorite_rounded,
|
||||
color: Colors.red[700],
|
||||
color: context.redColor,
|
||||
),
|
||||
),
|
||||
title: buildTitleWithTimestamp(activity),
|
||||
|
|
|
@ -23,32 +23,38 @@ class AlbumThumbnailCard extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var isDarkTheme = context.isDarkTheme;
|
||||
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
var cardSize = constraints.maxWidth;
|
||||
|
||||
buildEmptyThumbnail() {
|
||||
return Container(
|
||||
return SizedBox(
|
||||
height: cardSize,
|
||||
width: cardSize,
|
||||
decoration: BoxDecoration(
|
||||
color: isDarkTheme ? Colors.grey[800] : Colors.grey[200],
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.no_photography,
|
||||
size: cardSize * .15,
|
||||
child: Card(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20.0)),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.no_photography,
|
||||
size: cardSize * .15,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
buildAlbumThumbnail() => ImmichImage(
|
||||
album.thumbnail.value,
|
||||
width: cardSize,
|
||||
height: cardSize,
|
||||
buildAlbumThumbnail() => Card(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
child: ImmichImage(
|
||||
album.thumbnail.value,
|
||||
width: cardSize,
|
||||
height: cardSize,
|
||||
),
|
||||
);
|
||||
|
||||
buildAlbumTextRow() {
|
||||
|
@ -62,9 +68,9 @@ class AlbumThumbnailCard extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
return RichText(
|
||||
return Text.rich(
|
||||
overflow: TextOverflow.fade,
|
||||
text: TextSpan(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: album.assetCount == 1
|
||||
|
@ -78,7 +84,9 @@ class AlbumThumbnailCard extends StatelessWidget {
|
|||
if (owner != null)
|
||||
TextSpan(
|
||||
text: owner,
|
||||
style: context.textTheme.bodyMedium,
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
color: context.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -87,40 +95,36 @@ class AlbumThumbnailCard extends StatelessWidget {
|
|||
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Flex(
|
||||
direction: Axis.vertical,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: cardSize,
|
||||
height: cardSize,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: album.thumbnail.value == null
|
||||
? buildEmptyThumbnail()
|
||||
: buildAlbumThumbnail(),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: SizedBox(
|
||||
width: cardSize,
|
||||
child: Text(
|
||||
album.name,
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
color: context.primaryColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
buildAlbumTextRow(),
|
||||
],
|
||||
SizedBox(
|
||||
width: cardSize,
|
||||
height: cardSize,
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
child: album.thumbnail.value == null
|
||||
? buildEmptyThumbnail()
|
||||
: buildAlbumThumbnail(),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0, left: 8.0),
|
||||
child: SizedBox(
|
||||
width: cardSize,
|
||||
child: Text(
|
||||
album.name,
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
color: context.primaryColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 2.0, left: 8.0),
|
||||
child: buildAlbumTextRow(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -23,36 +23,46 @@ class AlbumThumbnailListTile extends StatelessWidget {
|
|||
var cardSize = 68.0;
|
||||
|
||||
buildEmptyThumbnail() {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: context.isDarkTheme ? Colors.grey[800] : Colors.grey[200],
|
||||
),
|
||||
child: SizedBox(
|
||||
height: cardSize,
|
||||
width: cardSize,
|
||||
child: const Center(
|
||||
child: Icon(Icons.no_photography),
|
||||
return SizedBox(
|
||||
height: cardSize,
|
||||
width: cardSize,
|
||||
child: Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.no_photography,
|
||||
size: cardSize * .15,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
buildAlbumThumbnail() {
|
||||
return CachedNetworkImage(
|
||||
width: cardSize,
|
||||
height: cardSize,
|
||||
fit: BoxFit.cover,
|
||||
fadeInDuration: const Duration(milliseconds: 200),
|
||||
imageUrl: getAlbumThumbnailUrl(
|
||||
album,
|
||||
type: ThumbnailFormat.JPEG,
|
||||
return Card(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: CachedNetworkImage(
|
||||
width: cardSize,
|
||||
height: cardSize,
|
||||
fit: BoxFit.cover,
|
||||
fadeInDuration: const Duration(milliseconds: 200),
|
||||
imageUrl: getAlbumThumbnailUrl(
|
||||
album,
|
||||
type: ThumbnailFormat.JPEG,
|
||||
),
|
||||
httpHeaders: {
|
||||
"Authorization": "Bearer ${Store.get(StoreKey.accessToken)}",
|
||||
},
|
||||
cacheKey:
|
||||
getAlbumThumbNailCacheKey(album, type: ThumbnailFormat.JPEG),
|
||||
errorWidget: (context, url, error) =>
|
||||
const Icon(Icons.image_not_supported_outlined),
|
||||
),
|
||||
httpHeaders: {
|
||||
"Authorization": "Bearer ${Store.get(StoreKey.accessToken)}",
|
||||
},
|
||||
cacheKey: getAlbumThumbNailCacheKey(album, type: ThumbnailFormat.JPEG),
|
||||
errorWidget: (context, url, error) =>
|
||||
const Icon(Icons.image_not_supported_outlined),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,6 @@ class AlbumTitleTextField extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final isDarkTheme = context.isDarkTheme;
|
||||
|
||||
return TextField(
|
||||
onChanged: (v) {
|
||||
if (v.isEmpty) {
|
||||
|
@ -33,9 +31,8 @@ class AlbumTitleTextField extends ConsumerWidget {
|
|||
ref.watch(albumTitleProvider.notifier).setAlbumTitle(v);
|
||||
},
|
||||
focusNode: albumTitleTextFieldFocusNode,
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
fontSize: 28,
|
||||
color: isDarkTheme ? Colors.grey[300] : Colors.grey[700],
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
controller: albumTitleController,
|
||||
|
@ -70,15 +67,10 @@ class AlbumTitleTextField extends ConsumerWidget {
|
|||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
hintText: 'share_add_title'.tr(),
|
||||
hintStyle: TextStyle(
|
||||
hintStyle: const TextStyle(
|
||||
fontSize: 28,
|
||||
color: isDarkTheme ? Colors.grey[300] : Colors.grey[700],
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
focusColor: Colors.grey[300],
|
||||
fillColor: isDarkTheme
|
||||
? const Color.fromARGB(255, 32, 33, 35)
|
||||
: Colors.grey[200],
|
||||
filled: isAlbumTitleTextFieldFocus.value,
|
||||
),
|
||||
);
|
||||
|
|
|
@ -108,7 +108,7 @@ class AlbumViewerAppbar extends HookConsumerWidget
|
|||
'Confirm',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: !context.isDarkTheme ? Colors.red : Colors.red[300],
|
||||
color: context.colorScheme.error,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -78,15 +78,10 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
|
|||
borderSide: const BorderSide(color: Colors.transparent),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
focusColor: Colors.grey[300],
|
||||
fillColor: context.isDarkTheme
|
||||
? const Color.fromARGB(255, 32, 33, 35)
|
||||
: Colors.grey[200],
|
||||
filled: titleFocusNode.hasFocus,
|
||||
hintText: 'share_add_title'.tr(),
|
||||
hintStyle: TextStyle(
|
||||
hintStyle: const TextStyle(
|
||||
fontSize: 28,
|
||||
color: context.isDarkTheme ? Colors.grey[300] : Colors.grey[700],
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/color_extensions.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart';
|
||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
|
@ -129,7 +130,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
|||
),
|
||||
subtitle: Text(
|
||||
album.owner.value?.email ?? "",
|
||||
style: TextStyle(color: Colors.grey[600]),
|
||||
style: TextStyle(color: context.colorScheme.onSurface.darken(40)),
|
||||
),
|
||||
trailing: Text(
|
||||
"shared_album_section_people_owner_label",
|
||||
|
@ -157,7 +158,7 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
|||
),
|
||||
subtitle: Text(
|
||||
user.email,
|
||||
style: TextStyle(color: Colors.grey[600]),
|
||||
style: TextStyle(color: context.colorScheme.onSurface.darken(40)),
|
||||
),
|
||||
trailing: userId == user.id || isOwner
|
||||
? const Icon(Icons.more_horiz_rounded)
|
||||
|
@ -201,9 +202,6 @@ class AlbumOptionsPage extends HookConsumerWidget {
|
|||
album.activityEnabled = value;
|
||||
}
|
||||
},
|
||||
activeColor: activityEnabled.value
|
||||
? context.primaryColor
|
||||
: context.themeData.disabledColor,
|
||||
dense: true,
|
||||
title: Text(
|
||||
"shared_album_activity_setting_title",
|
||||
|
|
|
@ -148,7 +148,7 @@ class AlbumViewerPage extends HookConsumerWidget {
|
|||
titleFocusNode: titleFocusNode,
|
||||
)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
padding: const EdgeInsets.only(left: 8.0, bottom: 20.0),
|
||||
child: Text(
|
||||
album.name,
|
||||
style: context.textTheme.headlineMedium,
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/color_extensions.dart';
|
||||
import 'package:immich_mobile/modules/album/providers/album.provider.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/album_thumbnail_card.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
|
@ -21,7 +22,6 @@ class LibraryPage extends HookConsumerWidget {
|
|||
final trashEnabled =
|
||||
ref.watch(serverInfoProvider.select((v) => v.serverFeatures.trash));
|
||||
final albums = ref.watch(albumProvider);
|
||||
var isDarkTheme = context.isDarkTheme;
|
||||
var settings = ref.watch(appSettingsServiceProvider);
|
||||
|
||||
useEffect(
|
||||
|
@ -102,9 +102,8 @@ class LibraryPage extends HookConsumerWidget {
|
|||
),
|
||||
Text(
|
||||
option,
|
||||
style: TextStyle(
|
||||
style: context.textTheme.displaySmall?.copyWith(
|
||||
color: selected ? context.primaryColor : null,
|
||||
fontSize: 12.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -135,49 +134,50 @@ class LibraryPage extends HookConsumerWidget {
|
|||
}
|
||||
|
||||
Widget buildCreateAlbumButton() {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
context.autoPush(CreateAlbumRoute(isSharedAlbum: false));
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return GestureDetector(
|
||||
onTap: () =>
|
||||
context.autoPush(CreateAlbumRoute(isSharedAlbum: false)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 32),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox.square(
|
||||
dimension: constraints.maxWidth,
|
||||
child: Card(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
color: context.themeData.cardColor.lighten(5),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.add_rounded,
|
||||
size: 28,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8.0,
|
||||
bottom: 16,
|
||||
left: 8.0,
|
||||
),
|
||||
child: Text(
|
||||
'library_page_new_album',
|
||||
style: context.textTheme.labelLarge,
|
||||
).tr(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 32),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: isDarkTheme
|
||||
? const Color.fromARGB(255, 53, 53, 53)
|
||||
: const Color.fromARGB(255, 203, 203, 203),
|
||||
),
|
||||
color: isDarkTheme ? Colors.grey[900] : Colors.grey[50],
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.add_rounded,
|
||||
size: 28,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8.0,
|
||||
bottom: 16,
|
||||
),
|
||||
child: Text(
|
||||
'library_page_new_album',
|
||||
style: context.textTheme.labelLarge,
|
||||
).tr(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -187,30 +187,17 @@ class LibraryPage extends HookConsumerWidget {
|
|||
Function() onClick,
|
||||
) {
|
||||
return Expanded(
|
||||
child: OutlinedButton.icon(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: onClick,
|
||||
label: Padding(
|
||||
padding: const EdgeInsets.only(left: 8.0),
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: context.isDarkTheme
|
||||
? Colors.white
|
||||
: Colors.black.withAlpha(200),
|
||||
),
|
||||
),
|
||||
child: Text(label),
|
||||
),
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
backgroundColor: isDarkTheme ? Colors.grey[900] : Colors.grey[50],
|
||||
side: BorderSide(
|
||||
color: isDarkTheme ? Colors.grey[800]! : Colors.grey[300]!,
|
||||
),
|
||||
style: context.themeData.elevatedButtonTheme.style?.copyWith(
|
||||
alignment: Alignment.centerLeft,
|
||||
),
|
||||
icon: Icon(
|
||||
icon,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -224,7 +211,7 @@ class LibraryPage extends HookConsumerWidget {
|
|||
return trashEnabled
|
||||
? InkWell(
|
||||
onTap: () => context.autoPush(const TrashRoute()),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||
child: const Icon(
|
||||
Icons.delete_rounded,
|
||||
size: 25,
|
||||
|
@ -250,8 +237,8 @@ class LibraryPage extends HookConsumerWidget {
|
|||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
buildLibraryNavButton(
|
||||
"library_page_favorites".tr(), Icons.favorite_border, () {
|
||||
buildLibraryNavButton("library_page_favorites".tr(),
|
||||
Icons.favorite_outline_rounded, () {
|
||||
context.autoNavigate(const FavoritesRoute());
|
||||
}),
|
||||
const SizedBox(width: 12.0),
|
||||
|
@ -290,7 +277,6 @@ class LibraryPage extends HookConsumerWidget {
|
|||
sliver: SliverGrid(
|
||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 250,
|
||||
mainAxisSpacing: 12,
|
||||
crossAxisSpacing: 12,
|
||||
childAspectRatio: .7,
|
||||
),
|
||||
|
|
|
@ -29,9 +29,11 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||
if (sharedUsersList.value.contains(user)) {
|
||||
return CircleAvatar(
|
||||
backgroundColor: context.primaryColor,
|
||||
child: const Icon(
|
||||
radius: 22,
|
||||
child: Icon(
|
||||
Icons.check_rounded,
|
||||
size: 25,
|
||||
color: context.colorScheme.surface,
|
||||
size: 24,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
|
@ -49,14 +51,10 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Chip(
|
||||
backgroundColor: context.primaryColor.withOpacity(0.15),
|
||||
backgroundColor: context.colorScheme.primaryContainer,
|
||||
label: Text(
|
||||
user.email,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: context.textTheme.displaySmall,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -72,9 +70,9 @@ class SelectAdditionalUserForSharingPage extends HookConsumerWidget {
|
|||
padding: const EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'select_additional_user_for_sharing_page_suggestions'.tr(),
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
color: context.themeData.hintColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -56,9 +56,11 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
|||
if (sharedUsersList.value.contains(user)) {
|
||||
return CircleAvatar(
|
||||
backgroundColor: context.primaryColor,
|
||||
child: const Icon(
|
||||
radius: 22,
|
||||
child: Icon(
|
||||
Icons.check_rounded,
|
||||
size: 25,
|
||||
color: context.colorScheme.surface,
|
||||
size: 24,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
|
@ -76,14 +78,10 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
|||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Chip(
|
||||
backgroundColor: context.primaryColor.withOpacity(0.15),
|
||||
backgroundColor: context.colorScheme.primaryContainer,
|
||||
label: Text(
|
||||
user.email,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: context.textTheme.displaySmall,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -97,11 +95,11 @@ class SelectUserForSharingPage extends HookConsumerWidget {
|
|||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: const Text(
|
||||
child: Text(
|
||||
'select_user_for_sharing_page_share_suggestions',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
color: context.themeData.hintColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
).tr(),
|
||||
|
|
|
@ -128,6 +128,9 @@ class SharingPage extends HookConsumerWidget {
|
|||
Icons.photo_album_outlined,
|
||||
size: 20,
|
||||
),
|
||||
style: context.themeData.elevatedButtonTheme.style?.copyWith(
|
||||
alignment: Alignment.centerLeft,
|
||||
),
|
||||
label: const Text(
|
||||
"sharing_silver_appbar_create_shared_album",
|
||||
maxLines: 1,
|
||||
|
@ -146,6 +149,9 @@ class SharingPage extends HookConsumerWidget {
|
|||
Icons.link,
|
||||
size: 20,
|
||||
),
|
||||
style: context.themeData.elevatedButtonTheme.style?.copyWith(
|
||||
alignment: Alignment.centerLeft,
|
||||
),
|
||||
label: const Text(
|
||||
"sharing_silver_appbar_shared_links",
|
||||
style: TextStyle(
|
||||
|
@ -169,8 +175,8 @@ class SharingPage extends HookConsumerWidget {
|
|||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
side: const BorderSide(
|
||||
color: Colors.grey,
|
||||
side: BorderSide(
|
||||
color: context.themeData.hintColor,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
|
@ -191,7 +197,8 @@ class SharingPage extends HookConsumerWidget {
|
|||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
'sharing_page_empty_list',
|
||||
style: context.textTheme.displaySmall,
|
||||
style: context.textTheme.displaySmall
|
||||
?.copyWith(color: context.primaryColor),
|
||||
).tr(),
|
||||
),
|
||||
Padding(
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/ui/drag_sheet.dart';
|
||||
|
||||
class AdvancedBottomSheet extends HookConsumerWidget {
|
||||
final Asset assetDetail;
|
||||
|
@ -24,33 +25,47 @@ class AdvancedBottomSheet extends HookConsumerWidget {
|
|||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
builder: (ctx, constraints) {
|
||||
// One column
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(height: 32.0),
|
||||
const Align(
|
||||
child: Text(
|
||||
"ADVANCED INFO",
|
||||
style: TextStyle(fontSize: 12.0),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 15, bottom: 10),
|
||||
child: CustomDraggingHandle(),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: TextButton.icon(
|
||||
label: Text(
|
||||
"ADVANCED INFO",
|
||||
style: context.textTheme.displaySmall,
|
||||
),
|
||||
icon: Icon(
|
||||
Icons.copy,
|
||||
size: 16.0,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
onPressed: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(text: assetDetail.toString()),
|
||||
).then((_) {
|
||||
ScaffoldMessenger.of(ctx).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text("Copied to clipboard"),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 32.0),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: context.isDarkTheme
|
||||
? Colors.grey[900]
|
||||
: Colors.grey[200],
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 16.0,
|
||||
left: 16,
|
||||
top: 8,
|
||||
bottom: 16,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
|
|
|
@ -20,7 +20,6 @@ class DescriptionInput extends HookConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final textColor = context.isDarkTheme ? Colors.white : Colors.black;
|
||||
final controller = useTextEditingController();
|
||||
final focusNode = useFocusNode();
|
||||
final isFocus = useState(false);
|
||||
|
@ -68,7 +67,7 @@ class DescriptionInput extends HookConsumerWidget {
|
|||
},
|
||||
icon: Icon(
|
||||
Icons.cancel_rounded,
|
||||
color: Colors.grey[500],
|
||||
color: context.themeData.hintColor,
|
||||
),
|
||||
splashRadius: 10,
|
||||
);
|
||||
|
@ -98,7 +97,7 @@ class DescriptionInput extends HookConsumerWidget {
|
|||
hintText: 'description_input_hint_text'.tr(),
|
||||
border: InputBorder.none,
|
||||
hintStyle: context.textTheme.labelLarge?.copyWith(
|
||||
color: textColor.withOpacity(0.5),
|
||||
color: context.colorScheme.onSurface.withOpacity(0.5),
|
||||
),
|
||||
suffixIcon: suffixIcon,
|
||||
),
|
||||
|
|
|
@ -112,7 +112,7 @@ class ExifBottomSheet extends HookConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final assetWithExif = ref.watch(assetDetailProvider(asset));
|
||||
final exifInfo = (assetWithExif.value ?? asset).exifInfo;
|
||||
var textColor = context.isDarkTheme ? Colors.white : Colors.black;
|
||||
var textColor = context.colorScheme.onSurface;
|
||||
|
||||
buildMap() {
|
||||
return Padding(
|
||||
|
|
|
@ -61,7 +61,7 @@ class CurrentUploadingAssetInfoBox extends HookConsumerWidget {
|
|||
Widget buildAssetInfoTable() {
|
||||
return Table(
|
||||
border: TableBorder.all(
|
||||
color: context.themeData.primaryColorLight,
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
width: 1,
|
||||
),
|
||||
children: [
|
||||
|
|
|
@ -2,7 +2,6 @@ import 'package:easy_localization/easy_localization.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/immich_colors.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
|
||||
import 'package:immich_mobile/modules/backup/ui/album_info_card.dart';
|
||||
|
@ -135,13 +134,12 @@ class BackupAlbumSelectionPage extends HookConsumerWidget {
|
|||
album.name,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: isDarkTheme ? Colors.black : immichBackgroundColor,
|
||||
color: context.colorScheme.surface,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
backgroundColor: Colors.red[300],
|
||||
deleteIconColor:
|
||||
isDarkTheme ? Colors.black : immichBackgroundColor,
|
||||
deleteIconColor: context.colorScheme.surface,
|
||||
deleteIcon: const Icon(
|
||||
Icons.cancel_rounded,
|
||||
size: 15,
|
||||
|
|
|
@ -674,7 +674,6 @@ class BackupControllerPage extends HookConsumerWidget {
|
|||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
title: const Text(
|
||||
"backup_controller_page_backup",
|
||||
).tr(),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
|
||||
class DisableMultiSelectButton extends ConsumerWidget {
|
||||
class DisableMultiSelectButton extends StatelessWidget {
|
||||
const DisableMultiSelectButton({
|
||||
Key? key,
|
||||
required this.onPressed,
|
||||
|
@ -12,25 +12,29 @@ class DisableMultiSelectButton extends ConsumerWidget {
|
|||
final int selectedItemCount;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0, top: 16.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
onPressed();
|
||||
},
|
||||
icon: const Icon(Icons.close_rounded),
|
||||
label: Text(
|
||||
'$selectedItemCount',
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 18,
|
||||
),
|
||||
padding: const EdgeInsets.only(left: 16.0, top: 16.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: ElevatedButton.icon(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: context.colorScheme.surface,
|
||||
backgroundColor: context.primaryColor,
|
||||
),
|
||||
onPressed: () {
|
||||
onPressed();
|
||||
},
|
||||
icon: const Icon(Icons.close_rounded),
|
||||
label: Text(
|
||||
'$selectedItemCount',
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
|
|||
/// Build the Scroll Thumb and label using the current configuration
|
||||
typedef ScrollThumbBuilder = Widget Function(
|
||||
Color backgroundColor,
|
||||
Color foregroundColor,
|
||||
Animation<double> thumbAnimation,
|
||||
Animation<double> labelAnimation,
|
||||
double height, {
|
||||
|
@ -33,6 +34,9 @@ class DraggableScrollbar extends StatefulWidget {
|
|||
/// The background color of the label and thumb
|
||||
final Color backgroundColor;
|
||||
|
||||
/// The foreground color of the arrows in the thumb
|
||||
final Color foregroundColor;
|
||||
|
||||
/// The amount of padding that should surround the thumb
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
|
@ -66,6 +70,7 @@ class DraggableScrollbar extends StatefulWidget {
|
|||
required this.scrollStateListener,
|
||||
this.heightScrollThumb = 48.0,
|
||||
this.backgroundColor = Colors.white,
|
||||
this.foregroundColor = Colors.white,
|
||||
this.padding,
|
||||
this.scrollbarAnimationDuration = const Duration(milliseconds: 300),
|
||||
this.scrollbarTimeToFade = const Duration(milliseconds: 600),
|
||||
|
@ -85,6 +90,7 @@ class DraggableScrollbar extends StatefulWidget {
|
|||
static buildScrollThumbAndLabel({
|
||||
required Widget scrollThumb,
|
||||
required Color backgroundColor,
|
||||
required Color foregroundColor,
|
||||
required Animation<double>? thumbAnimation,
|
||||
required Animation<double>? labelAnimation,
|
||||
required Text? labelText,
|
||||
|
@ -123,6 +129,7 @@ class DraggableScrollbar extends StatefulWidget {
|
|||
) {
|
||||
return (
|
||||
Color backgroundColor,
|
||||
Color foregroundColor,
|
||||
Animation<double> thumbAnimation,
|
||||
Animation<double> labelAnimation,
|
||||
double height, {
|
||||
|
@ -131,7 +138,7 @@ class DraggableScrollbar extends StatefulWidget {
|
|||
}) {
|
||||
final scrollThumb = CustomPaint(
|
||||
key: scrollThumbKey,
|
||||
foregroundPainter: ArrowCustomPainter(Colors.white),
|
||||
foregroundPainter: ArrowCustomPainter(foregroundColor),
|
||||
child: Material(
|
||||
elevation: 4.0,
|
||||
color: backgroundColor,
|
||||
|
@ -150,6 +157,7 @@ class DraggableScrollbar extends StatefulWidget {
|
|||
return buildScrollThumbAndLabel(
|
||||
scrollThumb: scrollThumb,
|
||||
backgroundColor: backgroundColor,
|
||||
foregroundColor: foregroundColor,
|
||||
thumbAnimation: thumbAnimation,
|
||||
labelAnimation: labelAnimation,
|
||||
labelText: labelText,
|
||||
|
@ -286,6 +294,7 @@ class DraggableScrollbarState extends State<DraggableScrollbar>
|
|||
padding: widget.padding,
|
||||
child: widget.scrollThumbBuilder(
|
||||
widget.backgroundColor,
|
||||
widget.foregroundColor,
|
||||
_thumbAnimation,
|
||||
_labelAnimation,
|
||||
widget.heightScrollThumb,
|
||||
|
|
|
@ -74,9 +74,9 @@ class GroupDividerTitle extends HookConsumerWidget {
|
|||
Icons.check_circle_rounded,
|
||||
color: context.primaryColor,
|
||||
)
|
||||
: const Icon(
|
||||
: Icon(
|
||||
Icons.check_circle_outline_rounded,
|
||||
color: Colors.grey,
|
||||
color: context.colorScheme.onBackground.withAlpha(100),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -222,9 +222,8 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
|
|||
padding: const EdgeInsets.only(left: 12.0, top: 24.0),
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.w500,
|
||||
style: context.textTheme.displayLarge?.copyWith(
|
||||
color: context.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -243,7 +242,7 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
|
|||
bottom: widget.margin,
|
||||
right: i + 1 == num ? 0.0 : widget.margin,
|
||||
),
|
||||
color: Colors.grey,
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -328,8 +327,8 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
|
|||
|
||||
return Text(
|
||||
DateFormat.yMMMM().format(date),
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
style: TextStyle(
|
||||
color: context.colorScheme.onPrimary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
);
|
||||
|
@ -372,7 +371,8 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
|
|||
scrollStateListener: dragScrolling,
|
||||
itemPositionsListener: _itemPositionsListener,
|
||||
controller: _itemScrollController,
|
||||
backgroundColor: context.themeData.hintColor,
|
||||
backgroundColor: context.primaryColor,
|
||||
foregroundColor: context.colorScheme.onPrimary,
|
||||
labelTextBuilder: _labelBuilder,
|
||||
labelConstraints: const BoxConstraints(maxHeight: 28),
|
||||
scrollbarAnimationDuration: const Duration(milliseconds: 300),
|
||||
|
|
|
@ -43,9 +43,6 @@ class ThumbnailImage extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final assetContainerColor = context.isDarkTheme
|
||||
? Colors.blueGrey
|
||||
: context.themeData.primaryColorLight;
|
||||
// Assets from response DTOs do not have an isar id, querying which would give us the default autoIncrement id
|
||||
final isFromDto = asset.id == Isar.autoIncrement;
|
||||
|
||||
|
@ -54,11 +51,13 @@ class ThumbnailImage extends StatelessWidget {
|
|||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: assetContainerColor,
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.check_circle_rounded,
|
||||
color: context.primaryColor,
|
||||
color: onDeselect == null
|
||||
? context.primaryColor.withAlpha(120)
|
||||
: context.primaryColor,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
|
@ -132,9 +131,10 @@ class ThumbnailImage extends StatelessWidget {
|
|||
}
|
||||
|
||||
Widget buildImage() {
|
||||
final image = SizedBox(
|
||||
final image = Container(
|
||||
width: 300,
|
||||
height: 300,
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
child: Hero(
|
||||
tag: isFromDto
|
||||
? '${asset.remoteId}-$heroOffset'
|
||||
|
@ -153,9 +153,9 @@ class ThumbnailImage extends StatelessWidget {
|
|||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
width: 0,
|
||||
color: onDeselect == null ? Colors.grey : assetContainerColor,
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
),
|
||||
color: onDeselect == null ? Colors.grey : assetContainerColor,
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.only(
|
||||
|
@ -203,10 +203,8 @@ class ThumbnailImage extends StatelessWidget {
|
|||
decoration: BoxDecoration(
|
||||
border: multiselectEnabled && isSelected
|
||||
? Border.all(
|
||||
color: onDeselect == null
|
||||
? Colors.grey
|
||||
: assetContainerColor,
|
||||
width: 8,
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
width: 12,
|
||||
)
|
||||
: const Border(),
|
||||
),
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/extensions/color_extensions.dart';
|
||||
import 'package:immich_mobile/modules/album/ui/add_to_album_sliverlist.dart';
|
||||
import 'package:immich_mobile/modules/home/models/selection_state.dart';
|
||||
import 'package:immich_mobile/modules/home/ui/delete_dialog.dart';
|
||||
|
@ -128,9 +129,8 @@ class ControlBottomAppBar extends ConsumerWidget {
|
|||
ScrollController scrollController,
|
||||
) {
|
||||
return Card(
|
||||
color: context.isDarkTheme ? Colors.grey[900] : Colors.grey[100],
|
||||
surfaceTintColor: Colors.transparent,
|
||||
elevation: 18.0,
|
||||
elevation: 2,
|
||||
color: context.colorScheme.surface.lighten(15),
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(12),
|
||||
|
|
|
@ -306,7 +306,8 @@ class LoginForm extends HookConsumerWidget {
|
|||
children: [
|
||||
Text(
|
||||
sanitizeUrl(serverEndpointController.text),
|
||||
style: context.textTheme.displaySmall,
|
||||
style: context.textTheme.displaySmall
|
||||
?.copyWith(color: context.primaryColor),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (isPasswordLoginEnable.value) ...[
|
||||
|
@ -616,7 +617,7 @@ class LoadingIcon extends StatelessWidget {
|
|||
height: 24,
|
||||
child: FittedBox(
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
strokeWidth: 5,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -125,8 +125,11 @@ class MapPageState extends ConsumerState<MapPage> {
|
|||
final refetchMarkers = useState(true);
|
||||
final isLoading =
|
||||
ref.watch(mapStateNotifier.select((state) => state.isLoading));
|
||||
final mapStyle =
|
||||
ref.watch(mapStateNotifier.select((state) => state.mapStyle));
|
||||
final maxZoom = ref.read(mapStateNotifier.notifier).maxZoom;
|
||||
final zoomLevel = math.min(maxZoom, 14.0);
|
||||
final themeData = isDarkTheme ? immichDarkTheme : immichLightTheme;
|
||||
|
||||
if (refetchMarkers.value) {
|
||||
mapMarkerData.value = ref.watch(mapMarkersProvider).when(
|
||||
|
@ -195,7 +198,7 @@ class MapPageState extends ConsumerState<MapPage> {
|
|||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => Theme(
|
||||
data: isDarkTheme ? immichDarkTheme : immichLightTheme,
|
||||
data: themeData,
|
||||
child: LocationServiceDisabledDialog(),
|
||||
),
|
||||
);
|
||||
|
@ -209,7 +212,7 @@ class MapPageState extends ConsumerState<MapPage> {
|
|||
shouldRequestPermission = await showDialog(
|
||||
context: context,
|
||||
builder: (context) => Theme(
|
||||
data: isDarkTheme ? immichDarkTheme : immichLightTheme,
|
||||
data: themeData,
|
||||
child: LocationPermissionDisabledDialog(),
|
||||
),
|
||||
);
|
||||
|
@ -426,7 +429,7 @@ class MapPageState extends ConsumerState<MapPage> {
|
|||
),
|
||||
child: Theme(
|
||||
// Override app theme based on map theme
|
||||
data: isDarkTheme ? immichDarkTheme : immichLightTheme,
|
||||
data: themeData,
|
||||
child: Scaffold(
|
||||
appBar: MapAppBar(
|
||||
isDarkTheme: isDarkTheme,
|
||||
|
@ -439,7 +442,7 @@ class MapPageState extends ConsumerState<MapPage> {
|
|||
extendBodyBehindAppBar: true,
|
||||
body: Stack(
|
||||
children: [
|
||||
if (!isLoading)
|
||||
if (!isLoading || mapStyle != null)
|
||||
FlutterMap(
|
||||
mapController: mapController,
|
||||
options: MapOptions(
|
||||
|
@ -464,7 +467,7 @@ class MapPageState extends ConsumerState<MapPage> {
|
|||
markerLayer,
|
||||
],
|
||||
),
|
||||
if (!isLoading)
|
||||
if (!isLoading || mapStyle != null)
|
||||
MapPageBottomSheet(
|
||||
mapPageEventStream: mapPageEventSC.stream,
|
||||
bottomSheetEventSC: bottomSheetEventSC,
|
||||
|
@ -473,10 +476,13 @@ class MapPageState extends ConsumerState<MapPage> {
|
|||
isDarkTheme: isDarkTheme,
|
||||
),
|
||||
if (showLoadingIndicator.value || isLoading)
|
||||
Positioned(
|
||||
top: context.height * 0.35,
|
||||
left: context.width * 0.425,
|
||||
child: const ImmichLoadingIndicator(),
|
||||
IgnorePointer(
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
color: context.colorScheme.surface.withAlpha(70),
|
||||
child: const Center(child: ImmichLoadingIndicator()),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -6,7 +6,7 @@ part of 'person.service.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$personServiceHash() => r'3fc3dcf4603c7b55c0deae65f39f6c212eea492b';
|
||||
String _$personServiceHash() => r'cde0a9c029d16ddde2adcd58ae8c863bf8cc1fed';
|
||||
|
||||
/// See also [personService].
|
||||
@ProviderFor(personService)
|
||||
|
|
|
@ -31,8 +31,8 @@ class CuratedPeopleRow extends StatelessWidget {
|
|||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: SizedBox(
|
||||
width: imageSize,
|
||||
height: imageSize,
|
||||
width: 120,
|
||||
height: 120,
|
||||
child: ThumbnailWithInfo(
|
||||
textInfo: '',
|
||||
onTap: () {},
|
||||
|
@ -69,6 +69,7 @@ class CuratedPeopleRow extends StatelessWidget {
|
|||
elevation: 3,
|
||||
child: CircleAvatar(
|
||||
maxRadius: imageSize / 2,
|
||||
backgroundColor: context.colorScheme.surfaceVariant,
|
||||
backgroundImage: NetworkImage(
|
||||
getFaceThumbnailUrl(person.id),
|
||||
headers: headers,
|
||||
|
|
|
@ -52,13 +52,13 @@ class CuratedPlacesRow extends CuratedRow {
|
|||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: Colors.black,
|
||||
color: context.colorScheme.shadow,
|
||||
gradient: LinearGradient(
|
||||
begin: FractionalOffset.topCenter,
|
||||
end: FractionalOffset.bottomCenter,
|
||||
colors: [
|
||||
Colors.blueGrey.withOpacity(0.0),
|
||||
Colors.black.withOpacity(0.4),
|
||||
context.colorScheme.shadow.withOpacity(0.1),
|
||||
context.colorScheme.shadow.withOpacity(0.2),
|
||||
],
|
||||
stops: const [0.0, 0.4],
|
||||
),
|
||||
|
|
|
@ -23,8 +23,6 @@ class ThumbnailWithInfo extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var textAndIconColor =
|
||||
context.isDarkTheme ? Colors.grey[100] : Colors.grey[700];
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
onTap();
|
||||
|
@ -35,7 +33,7 @@ class ThumbnailWithInfo extends StatelessWidget {
|
|||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(borderRadius),
|
||||
color: context.isDarkTheme ? Colors.grey[900] : Colors.grey[100],
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
),
|
||||
child: imageUrl != null
|
||||
? ClipRRect(
|
||||
|
@ -44,6 +42,16 @@ class ThumbnailWithInfo extends StatelessWidget {
|
|||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
placeholder: (context, url) {
|
||||
return SizedBox.square(
|
||||
dimension: 250,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
imageUrl: imageUrl!,
|
||||
httpHeaders: {
|
||||
"Authorization":
|
||||
|
@ -56,22 +64,22 @@ class ThumbnailWithInfo extends StatelessWidget {
|
|||
: Center(
|
||||
child: Icon(
|
||||
noImageIcon ?? Icons.not_listed_location,
|
||||
color: textAndIconColor,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(borderRadius),
|
||||
color: Colors.white,
|
||||
color: context.colorScheme.inverseSurface,
|
||||
gradient: LinearGradient(
|
||||
begin: FractionalOffset.topCenter,
|
||||
end: FractionalOffset.bottomCenter,
|
||||
colors: [
|
||||
Colors.grey.withOpacity(0.0),
|
||||
context.colorScheme.shadow.withOpacity(0),
|
||||
textInfo == ''
|
||||
? Colors.black.withOpacity(0.1)
|
||||
: Colors.black.withOpacity(0.5),
|
||||
? context.colorScheme.shadow.withOpacity(0.1)
|
||||
: context.colorScheme.shadow.withOpacity(0.2),
|
||||
],
|
||||
stops: const [0.0, 1.0],
|
||||
),
|
||||
|
|
|
@ -7,7 +7,7 @@ part of 'app_settings.provider.dart';
|
|||
// **************************************************************************
|
||||
|
||||
String _$appSettingsServiceHash() =>
|
||||
r'957a65af6967701112f3076b507f9738fec4b7be';
|
||||
r'45ea609a91d250290431a7a08a14d16b37c7515d';
|
||||
|
||||
/// See also [appSettingsService].
|
||||
@ProviderFor(appSettingsService)
|
||||
|
|
|
@ -81,7 +81,6 @@ class AdvancedSettings extends HookConsumerWidget {
|
|||
min: 1.0,
|
||||
divisions: 7,
|
||||
label: logLevel,
|
||||
activeColor: context.primaryColor,
|
||||
),
|
||||
),
|
||||
SettingsSwitchListTile(
|
||||
|
|
|
@ -51,7 +51,6 @@ class LayoutSettings extends HookConsumerWidget {
|
|||
return Column(
|
||||
children: [
|
||||
SwitchListTile.adaptive(
|
||||
activeColor: context.primaryColor,
|
||||
title: Text(
|
||||
"asset_list_layout_settings_dynamic_layout_title",
|
||||
style: context.textTheme.labelLarge,
|
||||
|
@ -73,7 +72,6 @@ class LayoutSettings extends HookConsumerWidget {
|
|||
).tr(),
|
||||
),
|
||||
RadioListTile(
|
||||
activeColor: context.primaryColor,
|
||||
title: Text(
|
||||
"asset_list_layout_settings_group_by_month_day",
|
||||
style: context.textTheme.labelLarge,
|
||||
|
@ -84,7 +82,6 @@ class LayoutSettings extends HookConsumerWidget {
|
|||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
),
|
||||
RadioListTile(
|
||||
activeColor: context.primaryColor,
|
||||
title: Text(
|
||||
"asset_list_layout_settings_group_by_month",
|
||||
style: context.textTheme.labelLarge,
|
||||
|
@ -95,7 +92,6 @@ class LayoutSettings extends HookConsumerWidget {
|
|||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
),
|
||||
RadioListTile(
|
||||
activeColor: context.primaryColor,
|
||||
title: Text(
|
||||
"asset_list_layout_settings_group_automatically",
|
||||
style: context.textTheme.labelLarge,
|
||||
|
|
|
@ -34,7 +34,6 @@ class StorageIndicator extends HookConsumerWidget {
|
|||
);
|
||||
|
||||
return SwitchListTile.adaptive(
|
||||
activeColor: context.primaryColor,
|
||||
title: Text(
|
||||
"theme_setting_asset_list_storage_indicator_title",
|
||||
style: context.textTheme.labelLarge,
|
||||
|
|
|
@ -49,7 +49,6 @@ class TilesPerRow extends HookConsumerWidget {
|
|||
max: 6,
|
||||
divisions: 4,
|
||||
label: "${itemsValue.value.toInt()}",
|
||||
activeColor: context.primaryColor,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
|
|
@ -143,7 +143,6 @@ class NotificationSetting extends HookConsumerWidget {
|
|||
max: 5.0,
|
||||
divisions: 5,
|
||||
label: formattedValue,
|
||||
activeColor: context.primaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -35,17 +35,21 @@ class SettingsSwitchListTile extends StatelessWidget {
|
|||
onChanged!(value);
|
||||
}
|
||||
},
|
||||
activeColor:
|
||||
enabled ? context.primaryColor : context.themeData.disabledColor,
|
||||
activeColor: enabled ? null : context.themeData.disabledColor,
|
||||
dense: true,
|
||||
title: Text(
|
||||
title,
|
||||
style: context.textTheme.titleSmall,
|
||||
style: context.textTheme.labelLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: enabled ? null : context.themeData.disabledColor,
|
||||
),
|
||||
),
|
||||
subtitle: subtitle != null
|
||||
? Text(
|
||||
subtitle!,
|
||||
style: context.textTheme.bodyMedium,
|
||||
style: TextStyle(
|
||||
color: enabled ? null : context.themeData.disabledColor,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
);
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/utils/immich_app_theme.dart';
|
||||
import 'package:immich_mobile/shared/providers/theme.provider.dart';
|
||||
|
||||
class ThemeSetting extends HookConsumerWidget {
|
||||
const ThemeSetting({
|
||||
|
@ -35,7 +35,6 @@ class ThemeSetting extends HookConsumerWidget {
|
|||
).tr(),
|
||||
children: [
|
||||
SwitchListTile.adaptive(
|
||||
activeColor: context.primaryColor,
|
||||
title: Text(
|
||||
'theme_setting_system_theme_switch',
|
||||
style: context.textTheme.labelLarge
|
||||
|
@ -48,20 +47,26 @@ class ThemeSetting extends HookConsumerWidget {
|
|||
|
||||
if (isSystem) {
|
||||
currentTheme.value = ThemeMode.system;
|
||||
ref.watch(immichThemeProvider.notifier).state = ThemeMode.system;
|
||||
ref
|
||||
.watch(immichThemeProvider.notifier)
|
||||
.updateTheme(ThemeMode.system);
|
||||
ref
|
||||
.watch(appSettingsServiceProvider)
|
||||
.setSetting(AppSettingsEnum.themeMode, "system");
|
||||
} else {
|
||||
if (currentSystemBrightness == Brightness.light) {
|
||||
currentTheme.value = ThemeMode.light;
|
||||
ref.watch(immichThemeProvider.notifier).state = ThemeMode.light;
|
||||
ref
|
||||
.watch(immichThemeProvider.notifier)
|
||||
.updateTheme(ThemeMode.light);
|
||||
ref
|
||||
.watch(appSettingsServiceProvider)
|
||||
.setSetting(AppSettingsEnum.themeMode, "light");
|
||||
} else if (currentSystemBrightness == Brightness.dark) {
|
||||
currentTheme.value = ThemeMode.dark;
|
||||
ref.watch(immichThemeProvider.notifier).state = ThemeMode.dark;
|
||||
ref
|
||||
.watch(immichThemeProvider.notifier)
|
||||
.updateTheme(ThemeMode.dark);
|
||||
ref
|
||||
.watch(appSettingsServiceProvider)
|
||||
.setSetting(AppSettingsEnum.themeMode, "dark");
|
||||
|
@ -71,7 +76,6 @@ class ThemeSetting extends HookConsumerWidget {
|
|||
),
|
||||
if (currentTheme.value != ThemeMode.system)
|
||||
SwitchListTile.adaptive(
|
||||
activeColor: context.primaryColor,
|
||||
title: Text(
|
||||
'theme_setting_dark_mode_switch',
|
||||
style: context.textTheme.labelLarge
|
||||
|
@ -80,12 +84,16 @@ class ThemeSetting extends HookConsumerWidget {
|
|||
value: ref.watch(immichThemeProvider) == ThemeMode.dark,
|
||||
onChanged: (bool isDark) {
|
||||
if (isDark) {
|
||||
ref.watch(immichThemeProvider.notifier).state = ThemeMode.dark;
|
||||
ref
|
||||
.watch(immichThemeProvider.notifier)
|
||||
.updateTheme(ThemeMode.dark);
|
||||
ref
|
||||
.watch(appSettingsServiceProvider)
|
||||
.setSetting(AppSettingsEnum.themeMode, "dark");
|
||||
} else {
|
||||
ref.watch(immichThemeProvider.notifier).state = ThemeMode.light;
|
||||
ref
|
||||
.watch(immichThemeProvider.notifier)
|
||||
.updateTheme(ThemeMode.light);
|
||||
ref
|
||||
.watch(appSettingsServiceProvider)
|
||||
.setSetting(AppSettingsEnum.themeMode, "light");
|
||||
|
|
|
@ -21,14 +21,6 @@ class TabNavigationObserver extends AutoRouterObserver {
|
|||
required this.ref,
|
||||
});
|
||||
|
||||
@override
|
||||
void didInitTabRoute(TabPageRoute route, TabPageRoute? previousRoute) {
|
||||
// Perform tasks on first navigation to SearchRoute
|
||||
if (route.name == 'SearchRoute') {
|
||||
// ref.refresh(getCuratedLocationProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> didChangeTabRoute(
|
||||
TabPageRoute route,
|
||||
|
|
2
mobile/lib/shared/providers/api.provider.g.dart
generated
2
mobile/lib/shared/providers/api.provider.g.dart
generated
|
@ -6,7 +6,7 @@ part of 'api.provider.dart';
|
|||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$apiServiceHash() => r'03cbd33147a7058d56175e532ac47e1aa4858c6d';
|
||||
String _$apiServiceHash() => r'5b8beddb448316bdae5e3963ff77601653715729';
|
||||
|
||||
/// See also [apiService].
|
||||
@ProviderFor(apiService)
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
class ImmichLoadingOverlayNotifier extends StateNotifier<bool> {
|
||||
ImmichLoadingOverlayNotifier() : super(false);
|
||||
|
||||
void show() => state = true;
|
||||
|
||||
void hide() => state = false;
|
||||
}
|
||||
|
||||
final immichLoadingOverlayController =
|
||||
StateNotifierProvider.autoDispose<ImmichLoadingOverlayNotifier, bool>(
|
||||
(_) => ImmichLoadingOverlayNotifier(),
|
||||
);
|
27
mobile/lib/shared/providers/theme.provider.dart
Normal file
27
mobile/lib/shared/providers/theme.provider.dart
Normal file
|
@ -0,0 +1,27 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'theme.provider.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class ImmichTheme extends _$ImmichTheme {
|
||||
@override
|
||||
ThemeMode build() {
|
||||
final themeMode = ref
|
||||
.watch(appSettingsServiceProvider)
|
||||
.getSetting(AppSettingsEnum.themeMode);
|
||||
|
||||
switch (themeMode) {
|
||||
case "light":
|
||||
return ThemeMode.light;
|
||||
case "dark":
|
||||
return ThemeMode.dark;
|
||||
default:
|
||||
return ThemeMode.system;
|
||||
}
|
||||
}
|
||||
|
||||
void updateTheme(ThemeMode newMode) => state = newMode;
|
||||
}
|
24
mobile/lib/shared/providers/theme.provider.g.dart
generated
Normal file
24
mobile/lib/shared/providers/theme.provider.g.dart
generated
Normal file
|
@ -0,0 +1,24 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'theme.provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$immichThemeHash() => r'22952207d5d5e6289a8244589e703c816501d6e3';
|
||||
|
||||
/// See also [ImmichTheme].
|
||||
@ProviderFor(ImmichTheme)
|
||||
final immichThemeProvider = NotifierProvider<ImmichTheme, ThemeMode>.internal(
|
||||
ImmichTheme.new,
|
||||
name: r'immichThemeProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$immichThemeHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$ImmichTheme = Notifier<ThemeMode>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
|
@ -135,11 +135,7 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
|||
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 3),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: context.isDarkTheme
|
||||
? context.scaffoldBackgroundColor
|
||||
: const Color.fromARGB(255, 225, 229, 240),
|
||||
),
|
||||
decoration: BoxDecoration(color: context.colorScheme.surface),
|
||||
child: ListTile(
|
||||
minLeadingWidth: 50,
|
||||
leading: Icon(
|
||||
|
@ -163,7 +159,8 @@ class ImmichAppBarDialog extends HookConsumerWidget {
|
|||
child: LinearProgressIndicator(
|
||||
minHeight: 5.0,
|
||||
value: backupState.serverInfo.diskUsagePercentage / 100.0,
|
||||
backgroundColor: Colors.grey,
|
||||
backgroundColor:
|
||||
context.colorScheme.onSurface.withAlpha(50),
|
||||
color: theme.primaryColor,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -77,9 +77,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
|
|||
child: Container(
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: context.isDarkTheme
|
||||
? context.scaffoldBackgroundColor
|
||||
: const Color.fromARGB(255, 225, 229, 240),
|
||||
color: context.colorScheme.surface,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(10),
|
||||
topRight: Radius.circular(10),
|
||||
|
@ -97,9 +95,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
|
|||
bottom: -5,
|
||||
right: -8,
|
||||
child: Material(
|
||||
color: context.isDarkTheme
|
||||
? Colors.blueGrey[800]
|
||||
: Colors.white,
|
||||
color: context.colorScheme.primaryContainer,
|
||||
elevation: 3,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(50.0),
|
||||
|
|
|
@ -42,9 +42,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
|||
padding: const EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: context.isDarkTheme
|
||||
? context.scaffoldBackgroundColor
|
||||
: const Color.fromARGB(255, 225, 229, 240),
|
||||
color: context.colorScheme.surface,
|
||||
borderRadius: const BorderRadius.only(
|
||||
bottomLeft: Radius.circular(10),
|
||||
bottomRight: Radius.circular(10),
|
||||
|
@ -186,8 +184,7 @@ class AppBarServerInfo extends HookConsumerWidget {
|
|||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
textStyle: TextStyle(
|
||||
color:
|
||||
context.isDarkTheme ? Colors.black : Colors.white,
|
||||
color: context.colorScheme.onPrimary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
message: getServerUrl() ?? '--',
|
||||
|
|
|
@ -44,7 +44,7 @@ class ConfirmDialog extends ConsumerWidget {
|
|||
child: Text(
|
||||
ok,
|
||||
style: TextStyle(
|
||||
color: Colors.red[400],
|
||||
color: context.colorScheme.error,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
).tr(),
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
|
||||
class CustomDraggingHandle extends StatelessWidget {
|
||||
const CustomDraggingHandle({super.key});
|
||||
|
@ -9,7 +10,7 @@ class CustomDraggingHandle extends StatelessWidget {
|
|||
height: 5,
|
||||
width: 30,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[500],
|
||||
color: context.themeData.dividerColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -25,7 +25,6 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
backupState.backgroundBackup || backupState.autoBackup;
|
||||
final ServerInfo serverInfoState = ref.watch(serverInfoProvider);
|
||||
final user = Store.tryGet(StoreKey.currentUser);
|
||||
final isDarkTheme = context.isDarkTheme;
|
||||
const widgetSize = 30.0;
|
||||
|
||||
buildProfileIndicator() {
|
||||
|
@ -42,9 +41,9 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
color: Colors.black,
|
||||
borderRadius: BorderRadius.circular(widgetSize / 2),
|
||||
),
|
||||
child: const Icon(
|
||||
child: Icon(
|
||||
Icons.info,
|
||||
color: Color.fromARGB(255, 243, 188, 106),
|
||||
color: context.orangeColor,
|
||||
size: widgetSize / 2,
|
||||
),
|
||||
),
|
||||
|
@ -68,9 +67,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
);
|
||||
}
|
||||
|
||||
getBackupBadgeIcon() {
|
||||
final iconColor = isDarkTheme ? Colors.white : Colors.black;
|
||||
|
||||
Widget? getBackupBadgeIcon() {
|
||||
if (isEnableAutoBackup) {
|
||||
if (backupState.backupProgress == BackUpProgressEnum.inProgress) {
|
||||
return Container(
|
||||
|
@ -78,7 +75,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
strokeCap: StrokeCap.round,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(iconColor),
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
);
|
||||
} else if (backupState.backupProgress !=
|
||||
|
@ -87,7 +84,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
return Icon(
|
||||
Icons.check_outlined,
|
||||
size: 9,
|
||||
color: iconColor,
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -96,14 +93,14 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
return Icon(
|
||||
Icons.cloud_off_rounded,
|
||||
size: 9,
|
||||
color: iconColor,
|
||||
color: context.colorScheme.onSurfaceVariant,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
buildBackupIndicator() {
|
||||
final indicatorIcon = getBackupBadgeIcon();
|
||||
final badgeBackground = isDarkTheme ? Colors.blueGrey[800] : Colors.white;
|
||||
|
||||
return InkWell(
|
||||
onTap: () => context.autoPush(const BackupControllerRoute()),
|
||||
|
@ -113,11 +110,11 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
width: widgetSize / 2,
|
||||
height: widgetSize / 2,
|
||||
decoration: BoxDecoration(
|
||||
color: badgeBackground,
|
||||
border: Border.all(
|
||||
color: isDarkTheme ? Colors.black : Colors.grey,
|
||||
color: context.colorScheme.surface,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(widgetSize / 2),
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
),
|
||||
child: indicatorIcon,
|
||||
),
|
||||
|
@ -125,16 +122,16 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
alignment: Alignment.bottomRight,
|
||||
isLabelVisible: indicatorIcon != null,
|
||||
offset: const Offset(2, 2),
|
||||
child: Icon(
|
||||
child: const Icon(
|
||||
Icons.backup_rounded,
|
||||
size: widgetSize,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return AppBar(
|
||||
elevation: 0.0,
|
||||
backgroundColor: context.themeData.appBarTheme.backgroundColor,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
|
@ -157,12 +154,13 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
|
|||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(left: 10),
|
||||
child: const Text(
|
||||
child: Text(
|
||||
'IMMICH',
|
||||
style: TextStyle(
|
||||
fontFamily: 'SnowburstOne',
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 24,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -33,8 +33,8 @@ class ImmichImage extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
if (this.asset == null) {
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.grey,
|
||||
decoration: BoxDecoration(
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
),
|
||||
child: SizedBox(
|
||||
width: width,
|
||||
|
@ -61,10 +61,12 @@ class ImmichImage extends StatelessWidget {
|
|||
return Stack(
|
||||
children: [
|
||||
if (useGrayBoxPlaceholder)
|
||||
const SizedBox.square(
|
||||
SizedBox.square(
|
||||
dimension: 250,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(color: Colors.grey),
|
||||
decoration: BoxDecoration(
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (useProgressIndicator)
|
||||
|
@ -110,10 +112,12 @@ class ImmichImage extends StatelessWidget {
|
|||
return Stack(
|
||||
children: [
|
||||
if (useGrayBoxPlaceholder)
|
||||
const SizedBox.square(
|
||||
SizedBox.square(
|
||||
dimension: 250,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(color: Colors.grey),
|
||||
decoration: BoxDecoration(
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (useProgressIndicator)
|
||||
|
|
|
@ -15,12 +15,12 @@ class ImmichLoadingIndicator extends StatelessWidget {
|
|||
height: 60,
|
||||
width: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: context.primaryColor.withAlpha(200),
|
||||
color: context.primaryColor,
|
||||
borderRadius: BorderRadius.circular(borderRadius ?? 10),
|
||||
),
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: const CircularProgressIndicator(
|
||||
color: Colors.white,
|
||||
child: CircularProgressIndicator(
|
||||
color: context.colorScheme.onPrimary,
|
||||
strokeWidth: 3,
|
||||
),
|
||||
);
|
||||
|
|
|
@ -9,7 +9,7 @@ class ImmichToast {
|
|||
required BuildContext context,
|
||||
required String msg,
|
||||
ToastType toastType = ToastType.info,
|
||||
ToastGravity gravity = ToastGravity.TOP,
|
||||
ToastGravity gravity = ToastGravity.BOTTOM,
|
||||
int durationInSecond = 3,
|
||||
}) {
|
||||
final fToast = FToast();
|
||||
|
@ -18,7 +18,7 @@ class ImmichToast {
|
|||
Color getColor(ToastType type, BuildContext context) {
|
||||
switch (type) {
|
||||
case ToastType.info:
|
||||
return context.primaryColor;
|
||||
return const Color.fromARGB(255, 48, 111, 220);
|
||||
case ToastType.success:
|
||||
return const Color.fromARGB(255, 78, 140, 124);
|
||||
case ToastType.error:
|
||||
|
@ -26,41 +26,39 @@ class ImmichToast {
|
|||
}
|
||||
}
|
||||
|
||||
Icon getIcon(ToastType type) {
|
||||
IconData getIcon(ToastType type) {
|
||||
switch (type) {
|
||||
case ToastType.info:
|
||||
return Icon(
|
||||
Icons.info_outline_rounded,
|
||||
color: context.primaryColor,
|
||||
);
|
||||
return Icons.info_outline_rounded;
|
||||
case ToastType.success:
|
||||
return const Icon(
|
||||
Icons.check_circle_rounded,
|
||||
color: Color.fromARGB(255, 78, 140, 124),
|
||||
);
|
||||
return Icons.check_circle_outline_rounded;
|
||||
case ToastType.error:
|
||||
return const Icon(
|
||||
Icons.error_outline_rounded,
|
||||
color: Color.fromARGB(255, 240, 162, 156),
|
||||
);
|
||||
return Icons.report_problem_outlined;
|
||||
}
|
||||
}
|
||||
|
||||
fToast.showToast(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
color: context.isDarkTheme ? Colors.grey[900] : Colors.grey[50],
|
||||
color: context.colorScheme.inverseSurface,
|
||||
border: Border.all(
|
||||
color: Colors.black12,
|
||||
color: context.colorScheme.inverseSurface,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
getIcon(toastType),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 15, right: 5),
|
||||
child: Icon(
|
||||
getIcon(toastType),
|
||||
color: getColor(toastType, context),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12.0,
|
||||
),
|
||||
|
@ -68,8 +66,7 @@ class ImmichToast {
|
|||
child: Text(
|
||||
msg,
|
||||
style: TextStyle(
|
||||
color: getColor(toastType, context),
|
||||
fontWeight: FontWeight.bold,
|
||||
color: context.colorScheme.onInverseSurface,
|
||||
fontSize: 15,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
|
||||
class PhotoViewDefaultError extends StatelessWidget {
|
||||
const PhotoViewDefaultError({Key? key, required this.decoration})
|
||||
|
@ -13,7 +14,7 @@ class PhotoViewDefaultError extends StatelessWidget {
|
|||
child: Center(
|
||||
child: Icon(
|
||||
Icons.broken_image,
|
||||
color: Colors.grey[400],
|
||||
color: context.primaryColor,
|
||||
size: 40.0,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:math';
|
|||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/shared/models/store.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
import 'package:immich_mobile/shared/ui/transparent_image.dart';
|
||||
|
@ -22,7 +23,6 @@ class UserCircleAvatar extends ConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
bool isDarkTheme = Theme.of(context).brightness == Brightness.dark;
|
||||
final profileImageUrl =
|
||||
'${Store.get(StoreKey.serverEndpoint)}/user/profile-image/${user.id}?d=${Random().nextInt(1024)}';
|
||||
|
||||
|
@ -30,10 +30,8 @@ class UserCircleAvatar extends ConsumerWidget {
|
|||
user.name[0].toUpperCase(),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
color: isDarkTheme && user.avatarColor == AvatarColorEnum.primary
|
||||
? Colors.black
|
||||
: Colors.white,
|
||||
fontSize: 16,
|
||||
color: context.colorScheme.surface,
|
||||
),
|
||||
);
|
||||
return CircleAvatar(
|
||||
|
|
|
@ -11,8 +11,6 @@ class AppLogDetailPage extends HookConsumerWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
var isDarkTheme = context.isDarkTheme;
|
||||
|
||||
buildStackMessage(String stackTrace) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
|
@ -60,7 +58,7 @@ class AppLogDetailPage extends HookConsumerWidget {
|
|||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: isDarkTheme ? Colors.grey[900] : Colors.grey[200],
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
child: Padding(
|
||||
|
@ -126,7 +124,7 @@ class AppLogDetailPage extends HookConsumerWidget {
|
|||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: isDarkTheme ? Colors.grey[900] : Colors.grey[200],
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
child: Padding(
|
||||
|
@ -165,7 +163,7 @@ class AppLogDetailPage extends HookConsumerWidget {
|
|||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: isDarkTheme ? Colors.grey[900] : Colors.grey[200],
|
||||
color: context.colorScheme.surfaceVariant,
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
child: Padding(
|
||||
|
|
|
@ -16,7 +16,6 @@ class AppLogPage extends HookConsumerWidget {
|
|||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final immichLogger = ImmichLogger();
|
||||
final logMessages = useState(immichLogger.messages);
|
||||
final isDarkTheme = context.isDarkTheme;
|
||||
|
||||
Widget colorStatusIndicator(Color color) {
|
||||
return Column(
|
||||
|
@ -39,12 +38,13 @@ class AppLogPage extends HookConsumerWidget {
|
|||
case LogLevel.INFO:
|
||||
return colorStatusIndicator(context.primaryColor);
|
||||
case LogLevel.SEVERE:
|
||||
return colorStatusIndicator(Colors.redAccent);
|
||||
|
||||
return colorStatusIndicator(context.redColor);
|
||||
case LogLevel.WARNING:
|
||||
return colorStatusIndicator(Colors.orangeAccent);
|
||||
return colorStatusIndicator(context.orangeColor);
|
||||
default:
|
||||
return colorStatusIndicator(Colors.grey);
|
||||
return colorStatusIndicator(
|
||||
context.colorScheme.onSurfaceVariant.withAlpha(150),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,15 +53,11 @@ class AppLogPage extends HookConsumerWidget {
|
|||
case LogLevel.INFO:
|
||||
return Colors.transparent;
|
||||
case LogLevel.SEVERE:
|
||||
return isDarkTheme
|
||||
? Colors.redAccent.withOpacity(0.25)
|
||||
: Colors.redAccent.withOpacity(0.075);
|
||||
return context.redColor.withAlpha(50);
|
||||
case LogLevel.WARNING:
|
||||
return isDarkTheme
|
||||
? Colors.orangeAccent.withOpacity(0.25)
|
||||
: Colors.orangeAccent.withOpacity(0.075);
|
||||
return context.orangeColor.withAlpha(50);
|
||||
default:
|
||||
return context.primaryColor.withOpacity(0.1);
|
||||
return context.primaryColor.withAlpha(50);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,7 +112,7 @@ class AppLogPage extends HookConsumerWidget {
|
|||
separatorBuilder: (context, index) {
|
||||
return Divider(
|
||||
height: 0,
|
||||
color: isDarkTheme ? Colors.white70 : Colors.grey[600],
|
||||
color: context.themeData.dividerColor,
|
||||
);
|
||||
},
|
||||
itemCount: logMessages.value.length,
|
||||
|
@ -139,7 +135,7 @@ class AppLogPage extends HookConsumerWidget {
|
|||
TextSpan(
|
||||
text: "#$index ",
|
||||
style: TextStyle(
|
||||
color: isDarkTheme ? Colors.white70 : Colors.grey[600],
|
||||
color: context.themeData.dividerColor,
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
|
@ -158,7 +154,7 @@ class AppLogPage extends HookConsumerWidget {
|
|||
"[${logMessage.context1}] Logged on ${DateFormat("HH:mm:ss.SSS").format(logMessage.createdAt)}",
|
||||
style: TextStyle(
|
||||
fontSize: 12.0,
|
||||
color: Colors.grey[600],
|
||||
color: context.themeData.hintColor,
|
||||
),
|
||||
),
|
||||
leading: buildLeadingIcon(logMessage.level),
|
||||
|
|
|
@ -7,8 +7,9 @@ final _loadingEntry = OverlayEntry(
|
|||
builder: (context) => SizedBox.square(
|
||||
dimension: double.infinity,
|
||||
child: DecoratedBox(
|
||||
decoration:
|
||||
BoxDecoration(color: context.colorScheme.surface.withAlpha(200)),
|
||||
decoration: BoxDecoration(
|
||||
color: context.colorScheme.surface.withAlpha(70),
|
||||
),
|
||||
child: const Center(child: ImmichLoadingIndicator()),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -92,8 +92,11 @@ class SplashScreenPage extends HookConsumerWidget {
|
|||
[],
|
||||
);
|
||||
|
||||
return const Scaffold(
|
||||
body: Center(
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
elevation: 0.0,
|
||||
),
|
||||
body: const Center(
|
||||
child: Image(
|
||||
image: AssetImage('assets/immich-logo-no-outline.png'),
|
||||
width: 80,
|
||||
|
|
|
@ -25,7 +25,7 @@ class TabControllerPage extends HookConsumerWidget {
|
|||
children: [
|
||||
icon,
|
||||
Positioned(
|
||||
right: -14,
|
||||
right: -16,
|
||||
child: SizedBox(
|
||||
height: 12,
|
||||
width: 12,
|
||||
|
@ -115,9 +115,8 @@ class TabControllerPage extends HookConsumerWidget {
|
|||
Icons.photo_library_outlined,
|
||||
),
|
||||
selectedIcon: buildIcon(
|
||||
Icon(
|
||||
const Icon(
|
||||
Icons.photo_library,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -126,9 +125,8 @@ class TabControllerPage extends HookConsumerWidget {
|
|||
icon: const Icon(
|
||||
Icons.search_rounded,
|
||||
),
|
||||
selectedIcon: Icon(
|
||||
selectedIcon: const Icon(
|
||||
Icons.search,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
NavigationDestination(
|
||||
|
@ -136,9 +134,8 @@ class TabControllerPage extends HookConsumerWidget {
|
|||
icon: const Icon(
|
||||
Icons.group_outlined,
|
||||
),
|
||||
selectedIcon: Icon(
|
||||
selectedIcon: const Icon(
|
||||
Icons.group,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
NavigationDestination(
|
||||
|
@ -147,9 +144,8 @@ class TabControllerPage extends HookConsumerWidget {
|
|||
Icons.photo_album_outlined,
|
||||
),
|
||||
selectedIcon: buildIcon(
|
||||
Icon(
|
||||
const Icon(
|
||||
Icons.photo_album_rounded,
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,278 +1,177 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/immich_colors.dart';
|
||||
import 'package:immich_mobile/modules/settings/providers/app_settings.provider.dart';
|
||||
import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
|
||||
|
||||
final immichThemeProvider = StateProvider<ThemeMode>((ref) {
|
||||
var themeMode = ref
|
||||
.watch(appSettingsServiceProvider)
|
||||
.getSetting(AppSettingsEnum.themeMode);
|
||||
|
||||
debugPrint("Current themeMode $themeMode");
|
||||
|
||||
if (themeMode == "light") {
|
||||
return ThemeMode.light;
|
||||
} else if (themeMode == "dark") {
|
||||
return ThemeMode.dark;
|
||||
} else {
|
||||
return ThemeMode.system;
|
||||
}
|
||||
});
|
||||
|
||||
ThemeData base = ThemeData(
|
||||
chipTheme: const ChipThemeData(
|
||||
side: BorderSide.none,
|
||||
),
|
||||
sliderTheme: const SliderThemeData(
|
||||
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7),
|
||||
trackHeight: 2.0,
|
||||
),
|
||||
);
|
||||
|
||||
ThemeData immichLightTheme = ThemeData(
|
||||
useMaterial3: true,
|
||||
ColorScheme _lightColorScheme = const ColorScheme(
|
||||
brightness: Brightness.light,
|
||||
primarySwatch: Colors.indigo,
|
||||
primaryColor: Colors.indigo,
|
||||
hintColor: Colors.indigo,
|
||||
focusColor: Colors.indigo,
|
||||
splashColor: Colors.indigo.withOpacity(0.15),
|
||||
fontFamily: 'Overpass',
|
||||
scaffoldBackgroundColor: immichBackgroundColor,
|
||||
snackBarTheme: const SnackBarThemeData(
|
||||
contentTextStyle: TextStyle(
|
||||
fontFamily: 'Overpass',
|
||||
color: Colors.indigo,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
backgroundColor: Colors.white,
|
||||
),
|
||||
appBarTheme: const AppBarTheme(
|
||||
titleTextStyle: TextStyle(
|
||||
fontFamily: 'Overpass',
|
||||
color: Colors.indigo,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
),
|
||||
backgroundColor: immichBackgroundColor,
|
||||
foregroundColor: Colors.indigo,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
centerTitle: true,
|
||||
),
|
||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
||||
type: BottomNavigationBarType.fixed,
|
||||
backgroundColor: immichBackgroundColor,
|
||||
selectedItemColor: Colors.indigo,
|
||||
),
|
||||
cardTheme: const CardTheme(
|
||||
surfaceTintColor: Colors.transparent,
|
||||
),
|
||||
drawerTheme: const DrawerThemeData(
|
||||
backgroundColor: immichBackgroundColor,
|
||||
),
|
||||
textTheme: const TextTheme(
|
||||
displayLarge: TextStyle(
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.indigo,
|
||||
),
|
||||
displayMedium: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
displaySmall: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.indigo,
|
||||
),
|
||||
titleSmall: TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
titleMedium: TextStyle(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
titleLarge: TextStyle(
|
||||
fontSize: 26.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.indigo,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
chipTheme: base.chipTheme,
|
||||
sliderTheme: base.sliderTheme,
|
||||
popupMenuTheme: const PopupMenuThemeData(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
surfaceTintColor: Colors.transparent,
|
||||
color: Colors.white,
|
||||
),
|
||||
navigationBarTheme: NavigationBarThemeData(
|
||||
indicatorColor: Colors.indigo.withOpacity(0.15),
|
||||
iconTheme: MaterialStatePropertyAll(
|
||||
IconThemeData(color: Colors.grey[700]),
|
||||
),
|
||||
backgroundColor: immichBackgroundColor,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
labelTextStyle: MaterialStatePropertyAll(
|
||||
TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.grey[800],
|
||||
),
|
||||
),
|
||||
),
|
||||
dialogTheme: const DialogTheme(
|
||||
surfaceTintColor: Colors.transparent,
|
||||
),
|
||||
inputDecorationTheme: const InputDecorationTheme(
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.indigo,
|
||||
),
|
||||
),
|
||||
labelStyle: TextStyle(
|
||||
color: Colors.indigo,
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
textSelectionTheme: const TextSelectionThemeData(
|
||||
cursorColor: Colors.indigo,
|
||||
),
|
||||
primary: Color(0xff4755b5),
|
||||
onPrimary: Color(0xffffffff),
|
||||
primaryContainer: Color(0xffdfe0ff),
|
||||
onPrimaryContainer: Color(0xff000d60),
|
||||
secondary: Color(0xff5b5d72),
|
||||
onSecondary: Color(0xffffffff),
|
||||
secondaryContainer: Color(0xFFD6D8FF),
|
||||
onSecondaryContainer: Color(0xff181a2c),
|
||||
tertiary: Color(0xff77536c),
|
||||
onTertiary: Color(0xffffffff),
|
||||
tertiaryContainer: Color(0xffffd7f0),
|
||||
onTertiaryContainer: Color(0xff2d1127),
|
||||
error: Color(0xffba1a1a),
|
||||
onError: Color(0xffffffff),
|
||||
errorContainer: Color(0xffffdad6),
|
||||
onErrorContainer: Color(0xff410002),
|
||||
background: Color(0xfff9f6fc),
|
||||
onBackground: Color(0xff1b1b1f),
|
||||
surface: Color(0xfff9f6fc),
|
||||
onSurface: Color(0xff1b1b1f),
|
||||
surfaceVariant: Color(0xffdeddea),
|
||||
onSurfaceVariant: Color(0xff46464f),
|
||||
outline: Color(0xff777680),
|
||||
outlineVariant: Color(0xffc7c5d0),
|
||||
shadow: Color(0xff000000),
|
||||
scrim: Color(0xff000000),
|
||||
inverseSurface: Color(0xff303137),
|
||||
onInverseSurface: Color(0xfff3f0f4),
|
||||
inversePrimary: Color(0xffbcc3ff),
|
||||
surfaceTint: Color(0xff4755b5),
|
||||
);
|
||||
|
||||
ThemeData immichDarkTheme = ThemeData(
|
||||
useMaterial3: true,
|
||||
ColorScheme _darkColorScheme = const ColorScheme(
|
||||
brightness: Brightness.dark,
|
||||
primarySwatch: Colors.indigo,
|
||||
primaryColor: immichDarkThemePrimaryColor,
|
||||
scaffoldBackgroundColor: immichDarkBackgroundColor,
|
||||
hintColor: Colors.grey[600],
|
||||
fontFamily: 'Overpass',
|
||||
snackBarTheme: SnackBarThemeData(
|
||||
contentTextStyle: const TextStyle(
|
||||
fontFamily: 'Overpass',
|
||||
color: immichDarkThemePrimaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
backgroundColor: Colors.grey[900],
|
||||
),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: immichDarkThemePrimaryColor,
|
||||
),
|
||||
),
|
||||
appBarTheme: const AppBarTheme(
|
||||
titleTextStyle: TextStyle(
|
||||
fontFamily: 'Overpass',
|
||||
color: immichDarkThemePrimaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
),
|
||||
backgroundColor: Color.fromARGB(255, 32, 33, 35),
|
||||
foregroundColor: immichDarkThemePrimaryColor,
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
centerTitle: true,
|
||||
),
|
||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
||||
type: BottomNavigationBarType.fixed,
|
||||
backgroundColor: Color.fromARGB(255, 35, 36, 37),
|
||||
selectedItemColor: immichDarkThemePrimaryColor,
|
||||
),
|
||||
drawerTheme: DrawerThemeData(
|
||||
backgroundColor: immichDarkBackgroundColor,
|
||||
scrimColor: Colors.white.withOpacity(0.1),
|
||||
),
|
||||
textTheme: const TextTheme(
|
||||
displayLarge: TextStyle(
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color.fromARGB(255, 255, 255, 255),
|
||||
),
|
||||
displayMedium: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color.fromARGB(255, 255, 255, 255),
|
||||
),
|
||||
displaySmall: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: immichDarkThemePrimaryColor,
|
||||
),
|
||||
titleSmall: TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
titleMedium: TextStyle(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
titleLarge: TextStyle(
|
||||
fontSize: 26.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
cardColor: Colors.grey[900],
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.black87,
|
||||
backgroundColor: immichDarkThemePrimaryColor,
|
||||
),
|
||||
),
|
||||
chipTheme: base.chipTheme,
|
||||
sliderTheme: base.sliderTheme,
|
||||
popupMenuTheme: const PopupMenuThemeData(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
surfaceTintColor: Colors.transparent,
|
||||
),
|
||||
navigationBarTheme: NavigationBarThemeData(
|
||||
indicatorColor: immichDarkThemePrimaryColor.withOpacity(0.4),
|
||||
iconTheme: MaterialStatePropertyAll(
|
||||
IconThemeData(color: Colors.grey[500]),
|
||||
),
|
||||
backgroundColor: Colors.grey[900],
|
||||
surfaceTintColor: Colors.transparent,
|
||||
labelTextStyle: MaterialStatePropertyAll(
|
||||
TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.grey[300],
|
||||
),
|
||||
),
|
||||
),
|
||||
dialogTheme: const DialogTheme(
|
||||
surfaceTintColor: Colors.transparent,
|
||||
),
|
||||
inputDecorationTheme: const InputDecorationTheme(
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: immichDarkThemePrimaryColor,
|
||||
),
|
||||
),
|
||||
labelStyle: TextStyle(
|
||||
color: immichDarkThemePrimaryColor,
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
textSelectionTheme: const TextSelectionThemeData(
|
||||
cursorColor: immichDarkThemePrimaryColor,
|
||||
),
|
||||
primary: Color(0xffa4c8ff),
|
||||
onPrimary: Color(0xff00315e),
|
||||
primaryContainer: Color(0xFF182C40),
|
||||
onPrimaryContainer: Color(0xffd4e3ff),
|
||||
secondary: Color(0xffbcc7dc),
|
||||
onSecondary: Color(0xff37474f),
|
||||
secondaryContainer: Color(0xff3d4758),
|
||||
onSecondaryContainer: Color(0xffd8e3f8),
|
||||
tertiary: Color(0xffdabde2),
|
||||
onTertiary: Color(0xff3d2946),
|
||||
tertiaryContainer: Color(0xff543f5e),
|
||||
onTertiaryContainer: Color(0xfff6d9ff),
|
||||
error: Color(0xffffb4ab),
|
||||
onError: Color(0xff690005),
|
||||
errorContainer: Color(0xff93000a),
|
||||
onErrorContainer: Color(0xffffb4ab),
|
||||
background: Color(0xff101214),
|
||||
onBackground: Color(0xffe2e2e5),
|
||||
surface: Color(0xff101214),
|
||||
onSurface: Color(0xffe2e2e5),
|
||||
surfaceVariant: Color(0xff363c42),
|
||||
onSurfaceVariant: Color(0xffc1c7ce),
|
||||
outline: Color(0xff8b9198),
|
||||
outlineVariant: Color(0xff41474d),
|
||||
shadow: Color(0xff000000),
|
||||
scrim: Color(0xff000000),
|
||||
inverseSurface: Color(0xffeeeef1),
|
||||
onInverseSurface: Color(0xff2e3133),
|
||||
inversePrimary: Color(0xff1c5fa5),
|
||||
surfaceTint: Color(0xff90caf9),
|
||||
);
|
||||
|
||||
ThemeData getThemeForScheme(ColorScheme scheme) {
|
||||
return ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: scheme.brightness,
|
||||
colorScheme: scheme,
|
||||
primaryColor: scheme.primary,
|
||||
scaffoldBackgroundColor: scheme.background,
|
||||
fontFamily: 'Overpass',
|
||||
appBarTheme: AppBarTheme(
|
||||
iconTheme: IconThemeData(color: scheme.primary),
|
||||
titleTextStyle: TextStyle(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: scheme.primary,
|
||||
),
|
||||
centerTitle: true,
|
||||
scrolledUnderElevation: 4.0,
|
||||
elevation: 4.0,
|
||||
),
|
||||
snackBarTheme: const SnackBarThemeData(
|
||||
contentTextStyle: TextStyle(
|
||||
fontFamily: 'Overpass',
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
||||
type: BottomNavigationBarType.fixed,
|
||||
),
|
||||
cardTheme: const CardTheme(elevation: 2.0),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
visualDensity: VisualDensity.standard,
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 11,
|
||||
),
|
||||
shadowColor: scheme.shadow,
|
||||
foregroundColor: scheme.onPrimary,
|
||||
backgroundColor: scheme.primary,
|
||||
),
|
||||
),
|
||||
navigationBarTheme: NavigationBarThemeData(
|
||||
iconTheme: MaterialStateProperty.resolveWith<IconThemeData?>((states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
return IconThemeData(color: scheme.primary);
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
labelTextStyle: const MaterialStatePropertyAll(
|
||||
TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
textTheme: TextTheme(
|
||||
displayLarge: TextStyle(
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: scheme.primary,
|
||||
),
|
||||
displayMedium: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
displaySmall: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
titleSmall: const TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
titleMedium: const TextStyle(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
titleLarge: TextStyle(
|
||||
fontSize: 26.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: scheme.primary,
|
||||
),
|
||||
),
|
||||
chipTheme: const ChipThemeData(
|
||||
side: BorderSide.none,
|
||||
),
|
||||
sliderTheme: const SliderThemeData(
|
||||
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7),
|
||||
trackHeight: 2.0,
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
labelStyle: TextStyle(
|
||||
color: scheme.primary,
|
||||
),
|
||||
hintStyle: const TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final ThemeData immichLightTheme = getThemeForScheme(_lightColorScheme);
|
||||
final ThemeData immichDarkTheme = getThemeForScheme(_darkColorScheme);
|
||||
|
||||
const redAccent = Colors.redAccent;
|
||||
const orangeAccent = Colors.orangeAccent;
|
||||
|
|
|
@ -369,6 +369,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
dynamic_color:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dynamic_color
|
||||
sha256: "8b8bd1d798bd393e11eddeaa8ae95b12ff028bf7d5998fc5d003488cd5f4ce2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.8"
|
||||
easy_image_viewer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
@ -58,6 +58,7 @@ dependencies:
|
|||
wakelock_plus: ^1.1.1
|
||||
flutter_local_notifications: ^15.1.0+1
|
||||
timezone: ^0.9.2
|
||||
dynamic_color: ^1.6.8
|
||||
|
||||
openapi:
|
||||
path: openapi
|
||||
|
|
Loading…
Reference in a new issue