chore: bump dart sdk to 3.8 (#20355)

* chore: bump dart sdk to 3.8

* chore: make build

* make pigeon

* chore: format files

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
This commit is contained in:
shenlong 2025-07-29 00:34:03 +05:30 committed by GitHub
parent 9b3718120b
commit e52b9d15b5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
643 changed files with 32561 additions and 35292 deletions

View file

@ -34,27 +34,18 @@ class ImmichAppBarDialog extends HookConsumerWidget {
final user = ref.watch(currentUserProvider);
final isLoggingOut = useState(false);
useEffect(
() {
ref.read(backupProvider.notifier).updateDiskInfo();
ref.read(currentUserProvider.notifier).refresh();
return null;
},
[],
);
useEffect(() {
ref.read(backupProvider.notifier).updateDiskInfo();
ref.read(currentUserProvider.notifier).refresh();
return null;
}, []);
buildTopRow() {
return Stack(
children: [
Align(
alignment: Alignment.topLeft,
child: InkWell(
onTap: () => context.pop(),
child: const Icon(
Icons.close,
size: 20,
),
),
child: InkWell(onTap: () => context.pop(), child: const Icon(Icons.close, size: 20)),
),
Center(
child: Image.asset(
@ -66,29 +57,16 @@ class ImmichAppBarDialog extends HookConsumerWidget {
);
}
buildActionButton(
IconData icon,
String text,
Function() onTap, {
Widget? trailing,
}) {
buildActionButton(IconData icon, String text, Function() onTap, {Widget? trailing}) {
return ListTile(
dense: true,
visualDensity: VisualDensity.standard,
contentPadding: const EdgeInsets.only(left: 30, right: 30),
minLeadingWidth: 40,
leading: SizedBox(
child: Icon(
icon,
color: theme.textTheme.labelLarge?.color?.withAlpha(250),
size: 20,
),
),
leading: SizedBox(child: Icon(icon, color: theme.textTheme.labelLarge?.color?.withAlpha(250), size: 20)),
title: Text(
text,
style: theme.textTheme.labelLarge?.copyWith(
color: theme.textTheme.labelLarge?.color?.withAlpha(250),
),
style: theme.textTheme.labelLarge?.copyWith(color: theme.textTheme.labelLarge?.color?.withAlpha(250)),
).tr(),
onTap: onTap,
trailing: trailing,
@ -96,11 +74,7 @@ class ImmichAppBarDialog extends HookConsumerWidget {
}
buildSettingButton() {
return buildActionButton(
Icons.settings_outlined,
"settings",
() => context.pushRoute(const SettingsRoute()),
);
return buildActionButton(Icons.settings_outlined, "settings", () => context.pushRoute(const SettingsRoute()));
}
buildAppLogButton() {
@ -142,10 +116,7 @@ class ImmichAppBarDialog extends HookConsumerWidget {
);
},
trailing: isLoggingOut.value
? const SizedBox.square(
dimension: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
? const SizedBox.square(dimension: 20, child: CircularProgressIndicator(strokeWidth: 2))
: null,
);
}
@ -165,20 +136,13 @@ 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.colorScheme.surface,
),
decoration: BoxDecoration(color: context.colorScheme.surface),
child: ListTile(
minLeadingWidth: 50,
leading: Icon(
Icons.storage_rounded,
color: theme.primaryColor,
),
leading: Icon(Icons.storage_rounded, color: theme.primaryColor),
title: Text(
"backup_controller_page_server_storage",
style: context.textTheme.labelLarge?.copyWith(
fontWeight: FontWeight.w500,
),
style: context.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.w500),
).tr(),
isThreeLine: true,
subtitle: Padding(
@ -196,12 +160,9 @@ class ImmichAppBarDialog extends HookConsumerWidget {
),
Padding(
padding: const EdgeInsets.only(top: 12.0),
child: const Text('backup_controller_page_storage_format').tr(
namedArgs: {
'used': usedDiskSpace,
'total': totalDiskSpace,
},
),
child: const Text(
'backup_controller_page_storage_format',
).tr(namedArgs: {'used': usedDiskSpace, 'total': totalDiskSpace}),
),
],
),
@ -220,43 +181,19 @@ class ImmichAppBarDialog extends HookConsumerWidget {
InkWell(
onTap: () {
context.pop();
launchUrl(
Uri.parse('https://immich.app'),
mode: LaunchMode.externalApplication,
);
launchUrl(Uri.parse('https://immich.app'), mode: LaunchMode.externalApplication);
},
child: Text(
"documentation",
style: context.textTheme.bodySmall,
).tr(),
),
const SizedBox(
width: 20,
child: Text(
"",
textAlign: TextAlign.center,
),
child: Text("documentation", style: context.textTheme.bodySmall).tr(),
),
const SizedBox(width: 20, child: Text("", textAlign: TextAlign.center)),
InkWell(
onTap: () {
context.pop();
launchUrl(
Uri.parse('https://github.com/immich-app/immich'),
mode: LaunchMode.externalApplication,
);
launchUrl(Uri.parse('https://github.com/immich-app/immich'), mode: LaunchMode.externalApplication);
},
child: Text(
"profile_drawer_github",
style: context.textTheme.bodySmall,
).tr(),
),
const SizedBox(
width: 20,
child: Text(
"",
textAlign: TextAlign.center,
),
child: Text("profile_drawer_github", style: context.textTheme.bodySmall).tr(),
),
const SizedBox(width: 20, child: Text("", textAlign: TextAlign.center)),
InkWell(
onTap: () async {
context.pop();
@ -291,20 +228,13 @@ class ImmichAppBarDialog extends HookConsumerWidget {
right: horizontalPadding,
bottom: isHorizontal ? 20 : 100,
),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(20),
),
),
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))),
child: SizedBox(
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(20),
child: buildTopRow(),
),
Container(padding: const EdgeInsets.all(20), child: buildTopRow()),
const AppBarProfileInfoBox(),
buildStorageInformation(),
const AppBarServerInfo(),

View file

@ -10,9 +10,7 @@ import 'package:immich_mobile/widgets/common/immich_loading_indicator.dart';
import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';
class AppBarProfileInfoBox extends HookConsumerWidget {
const AppBarProfileInfoBox({
super.key,
});
const AppBarProfileInfoBox({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
@ -29,38 +27,24 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
);
}
final userImage = UserCircleAvatar(
radius: 22,
size: 44,
user: user,
);
final userImage = UserCircleAvatar(radius: 22, size: 44, user: user);
if (uploadProfileImageStatus == UploadProfileStatus.loading) {
return const SizedBox(
height: 40,
width: 40,
child: ImmichLoadingIndicator(borderRadius: 20),
);
return const SizedBox(height: 40, width: 40, child: ImmichLoadingIndicator(borderRadius: 20));
}
return userImage;
}
pickUserProfileImage() async {
final XFile? image = await ImagePicker().pickImage(
source: ImageSource.gallery,
maxHeight: 1024,
maxWidth: 1024,
);
final XFile? image = await ImagePicker().pickImage(source: ImageSource.gallery, maxHeight: 1024, maxWidth: 1024);
if (image != null) {
var success = await ref.watch(uploadProfileImageProvider.notifier).upload(image);
if (success) {
final profileImagePath = ref.read(uploadProfileImageProvider).profileImagePath;
ref.watch(authProvider.notifier).updateUserProfileImagePath(
profileImagePath,
);
ref.watch(authProvider.notifier).updateUserProfileImagePath(profileImagePath);
if (user != null) {
ref.read(currentUserProvider.notifier).refresh();
}
@ -74,10 +58,7 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
width: double.infinity,
decoration: BoxDecoration(
color: context.colorScheme.surface,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
borderRadius: const BorderRadius.only(topLeft: Radius.circular(10), topRight: Radius.circular(10)),
),
child: ListTile(
minLeadingWidth: 50,
@ -93,16 +74,10 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
child: Material(
color: context.colorScheme.surfaceContainerHighest,
elevation: 3,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(50.0)),
),
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(50.0))),
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Icon(
Icons.camera_alt_outlined,
color: context.primaryColor,
size: 14,
),
child: Icon(Icons.camera_alt_outlined, color: context.primaryColor, size: 14),
),
),
),
@ -111,16 +86,11 @@ class AppBarProfileInfoBox extends HookConsumerWidget {
),
title: Text(
authState.name,
style: context.textTheme.titleMedium?.copyWith(
color: context.primaryColor,
fontWeight: FontWeight.w500,
),
style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor, fontWeight: FontWeight.w500),
),
subtitle: Text(
authState.userEmail,
style: context.textTheme.bodySmall?.copyWith(
color: context.colorScheme.onSurfaceSecondary,
),
style: context.textTheme.bodySmall?.copyWith(color: context.colorScheme.onSurfaceSecondary),
),
),
),

View file

@ -11,9 +11,7 @@ import 'package:immich_mobile/utils/url_helper.dart';
import 'package:package_info_plus/package_info_plus.dart';
class AppBarServerInfo extends HookConsumerWidget {
const AppBarServerInfo({
super.key,
});
const AppBarServerInfo({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
@ -27,29 +25,20 @@ class AppBarServerInfo extends HookConsumerWidget {
getPackageInfo() async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
appInfo.value = {
"version": packageInfo.version,
"buildNumber": packageInfo.buildNumber,
};
appInfo.value = {"version": packageInfo.version, "buildNumber": packageInfo.buildNumber};
}
useEffect(
() {
getPackageInfo();
return null;
},
[],
);
useEffect(() {
getPackageInfo();
return null;
}, []);
return Padding(
padding: const EdgeInsets.only(left: 10.0, right: 10.0, bottom: 10.0),
child: Container(
decoration: BoxDecoration(
color: context.colorScheme.surface,
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10),
),
borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(10), bottomRight: Radius.circular(10)),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8),
@ -63,17 +52,10 @@ class AppBarServerInfo extends HookConsumerWidget {
? serverInfoState.versionMismatchErrorMessage
: "profile_drawer_client_server_up_to_date".tr(),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 11,
color: context.primaryColor,
fontWeight: FontWeight.w500,
),
style: TextStyle(fontSize: 11, color: context.primaryColor, fontWeight: FontWeight.w500),
),
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Divider(thickness: 1),
),
const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -106,10 +88,7 @@ class AppBarServerInfo extends HookConsumerWidget {
),
],
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Divider(thickness: 1),
),
const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -144,10 +123,7 @@ class AppBarServerInfo extends HookConsumerWidget {
),
],
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Divider(thickness: 1),
),
const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -197,10 +173,7 @@ class AppBarServerInfo extends HookConsumerWidget {
),
],
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Divider(thickness: 1),
),
const Padding(padding: EdgeInsets.symmetric(horizontal: 10), child: Divider(thickness: 1)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -212,11 +185,7 @@ class AppBarServerInfo extends HookConsumerWidget {
if (serverInfoState.isNewReleaseAvailable)
const Padding(
padding: EdgeInsets.only(right: 5.0),
child: Icon(
Icons.info,
color: Color.fromARGB(255, 243, 188, 106),
size: 12,
),
child: Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: 12),
),
Text(
"latest_version".tr(),

View file

@ -26,9 +26,7 @@ class ConfirmDialog extends StatelessWidget {
}
return AlertDialog(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
),
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))),
title: Text(title).tr(),
content: Text(content).tr(),
actions: [
@ -36,20 +34,14 @@ class ConfirmDialog extends StatelessWidget {
onPressed: () => context.pop(false),
child: Text(
cancel,
style: TextStyle(
color: context.primaryColor,
fontWeight: FontWeight.bold,
),
style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold),
).tr(),
),
TextButton(
onPressed: onOkPressed,
child: Text(
ok,
style: TextStyle(
color: context.colorScheme.error,
fontWeight: FontWeight.bold,
),
style: TextStyle(color: context.colorScheme.error, fontWeight: FontWeight.bold),
).tr(),
),
],

View file

@ -16,11 +16,8 @@ Future<String?> showDateTimePicker({
}) {
return showDialog<String?>(
context: context,
builder: (context) => _DateTimePicker(
initialDateTime: initialDateTime,
initialTZ: initialTZ,
initialTZOffset: initialTZOffset,
),
builder: (context) =>
_DateTimePicker(initialDateTime: initialDateTime, initialTZ: initialTZ, initialTZOffset: initialTZOffset),
);
}
@ -33,18 +30,12 @@ class _DateTimePicker extends HookWidget {
final String? initialTZ;
final Duration? initialTZOffset;
const _DateTimePicker({
this.initialDateTime,
this.initialTZ,
this.initialTZOffset,
});
const _DateTimePicker({this.initialDateTime, this.initialTZ, this.initialTZOffset});
_TimeZoneOffset _getInitiationLocation() {
if (initialTZ != null) {
try {
return _TimeZoneOffset.fromLocation(
tz.timeZoneDatabase.get(initialTZ!),
);
return _TimeZoneOffset.fromLocation(tz.timeZoneDatabase.get(initialTZ!));
} on LocationNotFoundException {
// no-op
}
@ -59,10 +50,8 @@ class _DateTimePicker extends HookWidget {
(location) => location.currentTimeZone.offset == offsetInMilli,
);
// Prefer locations with abbreviation first
final location = locations.firstWhereOrNull(
(e) => !e.currentTimeZone.abbreviation.contains("0"),
) ??
locations.firstOrNull;
final location =
locations.firstWhereOrNull((e) => !e.currentTimeZone.abbreviation.contains("0")) ?? locations.firstOrNull;
if (location != null) {
return _TimeZoneOffset.fromLocation(location);
}
@ -86,11 +75,7 @@ class _DateTimePicker extends HookWidget {
(timezone) => DropdownMenuEntry<_TimeZoneOffset>(
value: timezone,
label: timezone.display,
style: ButtonStyle(
textStyle: WidgetStatePropertyAll(
context.textTheme.bodyMedium,
),
),
style: ButtonStyle(textStyle: WidgetStatePropertyAll(context.textTheme.bodyMedium)),
),
)
.toList();
@ -109,10 +94,7 @@ class _DateTimePicker extends HookWidget {
return;
}
final newTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.fromDateTime(date.value),
);
final newTime = await showTimePicker(context: context, initialTime: TimeOfDay.fromDateTime(date.value));
if (newTime == null) {
return;
@ -145,10 +127,7 @@ class _DateTimePicker extends HookWidget {
onPressed: popWithDateTime,
child: Text(
"action_common_update",
style: context.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w600,
color: context.primaryColor,
),
style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor),
).tr(),
),
],
@ -156,46 +135,22 @@ class _DateTimePicker extends HookWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"date_and_time",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
).tr(),
const Text("date_and_time", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)).tr(),
const SizedBox(height: 32),
ListTile(
tileColor: context.colorScheme.surfaceContainerHighest,
shape: ShapeBorder.lerp(
const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))),
const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))),
1,
),
trailing: Icon(
Icons.edit_outlined,
size: 18,
color: context.primaryColor,
),
title: Text(
DateFormat("dd-MM-yyyy hh:mm a").format(date.value),
style: context.textTheme.bodyMedium,
).tr(),
trailing: Icon(Icons.edit_outlined, size: 18, color: context.primaryColor),
title: Text(DateFormat("dd-MM-yyyy hh:mm a").format(date.value), style: context.textTheme.bodyMedium).tr(),
onTap: pickDate,
),
const SizedBox(height: 24),
DropdownSearchMenu(
trailingIcon: Icon(
Icons.arrow_drop_down,
color: context.primaryColor,
),
trailingIcon: Icon(Icons.arrow_drop_down, color: context.primaryColor),
hintText: "timezone".tr(),
label: const Text('timezone').tr(),
textStyle: context.textTheme.bodyMedium,
@ -213,26 +168,17 @@ class _TimeZoneOffset implements Comparable<_TimeZoneOffset> {
final String display;
final Location location;
const _TimeZoneOffset({
required this.display,
required this.location,
});
const _TimeZoneOffset({required this.display, required this.location});
_TimeZoneOffset copyWith({
String? display,
Location? location,
}) {
return _TimeZoneOffset(
display: display ?? this.display,
location: location ?? this.location,
);
_TimeZoneOffset copyWith({String? display, Location? location}) {
return _TimeZoneOffset(display: display ?? this.display, location: location ?? this.location);
}
int get offsetInMilliseconds => location.currentTimeZone.offset;
_TimeZoneOffset.fromLocation(tz.Location l)
: display = _getFormattedOffset(l.currentTimeZone.offset, l),
location = l;
: display = _getFormattedOffset(l.currentTimeZone.offset, l),
location = l;
@override
int compareTo(_TimeZoneOffset other) {

View file

@ -11,12 +11,7 @@ class DelayedLoadingIndicator extends StatelessWidget {
/// An optional fade in duration to animate the loading
final Duration? fadeInDuration;
const DelayedLoadingIndicator({
super.key,
this.delay = const Duration(seconds: 3),
this.child,
this.fadeInDuration,
});
const DelayedLoadingIndicator({super.key, this.delay = const Duration(seconds: 3), this.child, this.fadeInDuration});
@override
Widget build(BuildContext context) {
@ -25,18 +20,12 @@ class DelayedLoadingIndicator extends StatelessWidget {
builder: (context, snapshot) {
late Widget c;
if (snapshot.connectionState == ConnectionState.done) {
c = child ??
const ImmichLoadingIndicator(
key: ValueKey('loading'),
);
c = child ?? const ImmichLoadingIndicator(key: ValueKey('loading'));
} else {
c = Container(key: const ValueKey('hiding'));
}
return AnimatedSwitcher(
duration: fadeInDuration ?? Duration.zero,
child: c,
);
return AnimatedSwitcher(duration: fadeInDuration ?? Duration.zero, child: c);
},
);
}

View file

@ -18,13 +18,7 @@ class CustomDraggingHandle extends StatelessWidget {
}
class ControlBoxButton extends StatelessWidget {
const ControlBoxButton({
super.key,
required this.label,
required this.iconData,
this.onPressed,
this.onLongPressed,
});
const ControlBoxButton({super.key, required this.label, required this.iconData, this.onPressed, this.onLongPressed});
final String label;
final IconData iconData;
@ -37,9 +31,7 @@ class ControlBoxButton extends StatelessWidget {
return MaterialButton(
padding: const EdgeInsets.all(10),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20)),
),
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20))),
onPressed: onPressed,
onLongPress: onLongPressed,
minWidth: minWidth,

View file

@ -34,13 +34,8 @@ class DropdownSearchMenu<T> extends HookWidget {
);
final showTimeZoneDropdown = useState<bool>(false);
final effectiveConstraints = menuConstraints ??
const BoxConstraints(
minWidth: 280,
maxWidth: 280,
minHeight: 0,
maxHeight: 280,
);
final effectiveConstraints =
menuConstraints ?? const BoxConstraints(minWidth: 280, maxWidth: 280, minHeight: 0, maxHeight: 280);
final inputDecoration = InputDecoration(
contentPadding: const EdgeInsets.fromLTRB(12, 4, 12, 4),
@ -58,12 +53,7 @@ class DropdownSearchMenu<T> extends HookWidget {
child: InputDecorator(
decoration: inputDecoration,
child: selectedItem.value != null
? Text(
selectedItem.value!.label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: textStyle,
)
? Text(selectedItem.value!.label, maxLines: 1, overflow: TextOverflow.ellipsis, style: textStyle)
: null,
),
),
@ -89,9 +79,7 @@ class DropdownSearchMenu<T> extends HookWidget {
autofocus: true,
focusNode: focusNode,
controller: textEditingController,
decoration: inputDecoration.copyWith(
hintText: "search_timezone".tr(),
),
decoration: inputDecoration.copyWith(hintText: "search_timezone".tr()),
maxLines: 1,
style: context.textTheme.bodyMedium,
expands: false,
@ -125,23 +113,14 @@ class DropdownSearchMenu<T> extends HookWidget {
builder: (BuildContext context) {
final bool highlight = AutocompleteHighlightedOption.of(context) == index;
if (highlight) {
SchedulerBinding.instance.addPostFrameCallback(
(Duration timeStamp) {
Scrollable.ensureVisible(
context,
alignment: 0.5,
);
},
debugLabel: 'AutocompleteOptions.ensureVisible',
);
SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
Scrollable.ensureVisible(context, alignment: 0.5);
}, debugLabel: 'AutocompleteOptions.ensureVisible');
}
return Container(
color: highlight ? Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.12) : null,
padding: const EdgeInsets.all(16.0),
child: Text(
option.label,
style: textStyle,
),
child: Text(option.label, style: textStyle),
);
},
),

View file

@ -22,12 +22,7 @@ class FadeInPlaceholderImage extends StatelessWidget {
fit: StackFit.expand,
children: [
placeholder,
FadeInImage(
fadeInDuration: duration,
image: image,
fit: fit,
placeholder: MemoryImage(kTransparentImage),
),
FadeInImage(fadeInDuration: duration, image: image, fit: fit, placeholder: MemoryImage(kTransparentImage)),
],
),
);

View file

@ -36,23 +36,13 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
buildProfileIndicator() {
return InkWell(
onTap: () => showDialog(
context: context,
useRootNavigator: false,
builder: (ctx) => const ImmichAppBarDialog(),
),
onTap: () =>
showDialog(context: context, useRootNavigator: false, builder: (ctx) => const ImmichAppBarDialog()),
borderRadius: const BorderRadius.all(Radius.circular(12)),
child: Badge(
label: Container(
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(widgetSize / 2),
),
child: const Icon(
Icons.info,
color: Color.fromARGB(255, 243, 188, 106),
size: widgetSize / 2,
),
decoration: BoxDecoration(color: Colors.black, borderRadius: BorderRadius.circular(widgetSize / 2)),
child: const Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: widgetSize / 2),
),
backgroundColor: Colors.transparent,
alignment: Alignment.bottomRight,
@ -60,17 +50,10 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
serverInfoState.isVersionMismatch || ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable),
offset: const Offset(-2, -12),
child: user == null
? const Icon(
Icons.face_outlined,
size: widgetSize,
)
? const Icon(Icons.face_outlined, size: widgetSize)
: Semantics(
label: "logged_in_as".tr(namedArgs: {"user": user.name}),
child: UserCircleAvatar(
radius: 17,
size: 31,
user: user,
),
child: UserCircleAvatar(radius: 17, size: 31, user: user),
),
),
);
@ -124,9 +107,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
height: widgetSize / 2,
decoration: BoxDecoration(
color: badgeBackground,
border: Border.all(
color: context.colorScheme.outline.withValues(alpha: .3),
),
border: Border.all(color: context.colorScheme.outline.withValues(alpha: .3)),
borderRadius: BorderRadius.circular(widgetSize / 2),
),
child: indicatorIcon,
@ -135,22 +116,14 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
alignment: Alignment.bottomRight,
isLabelVisible: indicatorIcon != null,
offset: const Offset(-2, -12),
child: Icon(
Icons.backup_rounded,
size: widgetSize,
color: context.primaryColor,
),
child: Icon(Icons.backup_rounded, size: widgetSize, color: context.primaryColor),
),
);
}
return AppBar(
backgroundColor: context.themeData.appBarTheme.backgroundColor,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))),
automaticallyImplyLeading: false,
centerTitle: false,
title: Builder(
@ -176,12 +149,7 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
),
actions: [
if (actions != null)
...actions!.map(
(action) => Padding(
padding: const EdgeInsets.only(right: 16),
child: action,
),
),
...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)),
if (kDebugMode || kProfileMode)
IconButton(
icon: const Icon(Icons.science_rounded),
@ -192,25 +160,13 @@ class ImmichAppBar extends ConsumerWidget implements PreferredSizeWidget {
padding: const EdgeInsets.only(right: 12),
child: IconButton(
onPressed: () {
showDialog(
context: context,
builder: (context) => const CastDialog(),
);
showDialog(context: context, builder: (context) => const CastDialog());
},
icon: Icon(
isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded,
),
icon: Icon(isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded),
),
),
if (showUploadButton)
Padding(
padding: const EdgeInsets.only(right: 20),
child: buildBackupIndicator(),
),
Padding(
padding: const EdgeInsets.only(right: 20),
child: buildProfileIndicator(),
),
if (showUploadButton) Padding(padding: const EdgeInsets.only(right: 20), child: buildBackupIndicator()),
Padding(padding: const EdgeInsets.only(right: 20), child: buildProfileIndicator()),
],
);
}

View file

@ -28,32 +28,19 @@ class ImmichImage extends StatelessWidget {
// either by using the asset ID or the asset itself
/// [asset] is the Asset to request, or else use [assetId] to get a remote
/// image provider
static ImageProvider imageProvider({
Asset? asset,
String? assetId,
double width = 1080,
double height = 1920,
}) {
static ImageProvider imageProvider({Asset? asset, String? assetId, double width = 1080, double height = 1920}) {
if (asset == null && assetId == null) {
throw Exception('Must supply either asset or assetId');
}
if (asset == null) {
return ImmichRemoteImageProvider(
assetId: assetId!,
);
return ImmichRemoteImageProvider(assetId: assetId!);
}
if (useLocal(asset)) {
return ImmichLocalImageProvider(
asset: asset,
width: width,
height: height,
);
return ImmichLocalImageProvider(asset: asset, width: width, height: height);
} else {
return ImmichRemoteImageProvider(
assetId: asset.remoteId!,
);
return ImmichRemoteImageProvider(assetId: asset.remoteId!);
}
}
@ -68,17 +55,11 @@ class ImmichImage extends StatelessWidget {
color: Colors.grey,
width: width,
height: height,
child: const Center(
child: Icon(Icons.no_photography),
),
child: const Center(child: Icon(Icons.no_photography)),
);
}
final imageProviderInstance = ImmichImage.imageProvider(
asset: asset,
width: context.width,
height: context.height,
);
final imageProviderInstance = ImmichImage.imageProvider(asset: asset, width: context.width, height: context.height);
return OctoImage(
fadeInDuration: const Duration(milliseconds: 0),
@ -96,11 +77,7 @@ class ImmichImage extends StatelessWidget {
errorBuilder: (context, error, stackTrace) {
imageProviderInstance.evict();
return Icon(
Icons.image_not_supported_outlined,
size: 32,
color: Colors.red[200],
);
return Icon(Icons.image_not_supported_outlined, size: 32, color: Colors.red[200]);
},
);
}

View file

@ -5,22 +5,15 @@ import 'package:immich_mobile/widgets/common/immich_logo.dart';
class ImmichLoadingIndicator extends HookWidget {
final double? borderRadius;
const ImmichLoadingIndicator({
super.key,
this.borderRadius,
});
const ImmichLoadingIndicator({super.key, this.borderRadius});
@override
Widget build(BuildContext context) {
final logoAnimationController = useAnimationController(
duration: const Duration(seconds: 6),
)
final logoAnimationController = useAnimationController(duration: const Duration(seconds: 6))
..reverse()
..repeat();
final borderAnimationController = useAnimationController(
duration: const Duration(seconds: 6),
)..repeat();
final borderAnimationController = useAnimationController(duration: const Duration(seconds: 6))..repeat();
return Container(
height: 80,
@ -34,10 +27,7 @@ class ImmichLoadingIndicator extends HookWidget {
animation: borderAnimationController,
builder: (context, child) {
return CustomPaint(
painter: GradientBorderPainter(
animation: borderAnimationController.value,
strokeWidth: 3,
),
painter: GradientBorderPainter(animation: borderAnimationController.value, strokeWidth: 3),
child: child,
);
},
@ -45,9 +35,7 @@ class ImmichLoadingIndicator extends HookWidget {
padding: const EdgeInsets.all(15),
child: RotationTransition(
turns: logoAnimationController,
child: const ImmichLogo(
heroTag: 'logo',
),
child: const ImmichLogo(heroTag: 'logo'),
),
),
),
@ -67,10 +55,7 @@ class GradientBorderPainter extends CustomPainter {
const Color(0xFF18C249),
];
GradientBorderPainter({
required this.animation,
required this.strokeWidth,
});
GradientBorderPainter({required this.animation, required this.strokeWidth});
@override
void paint(Canvas canvas, Size size) {
@ -96,10 +81,7 @@ class GradientBorderPainter extends CustomPainter {
colors.first.withValues(alpha: opacity),
],
// Add evenly distributed stops
stops: List.generate(
colors.length + 1,
(index) => index / colors.length,
),
stops: List.generate(colors.length + 1, (index) => index / colors.length),
tileMode: TileMode.clamp,
// Use transformations to rotate the gradient
transform: GradientRotation(-animation * 2 * 3.14159),

View file

@ -4,11 +4,7 @@ class ImmichLogo extends StatelessWidget {
final double size;
final dynamic heroTag;
const ImmichLogo({
super.key,
this.size = 100,
this.heroTag,
});
const ImmichLogo({super.key, this.size = 100, this.heroTag});
@override
Widget build(BuildContext context) {

View file

@ -52,11 +52,7 @@ class ImmichSliverAppBar extends ConsumerWidget {
pinned: pinned,
snap: snap,
expandedHeight: expandedHeight,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))),
automaticallyImplyLeading: false,
centerTitle: false,
title: title ?? const _ImmichLogoWithText(),
@ -66,38 +62,21 @@ class ImmichSliverAppBar extends ConsumerWidget {
padding: const EdgeInsets.only(right: 12),
child: IconButton(
onPressed: () {
showDialog(
context: context,
builder: (context) => const CastDialog(),
);
showDialog(context: context, builder: (context) => const CastDialog());
},
icon: Icon(
isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded,
),
icon: Icon(isCasting ? Icons.cast_connected_rounded : Icons.cast_rounded),
),
),
const _SyncStatusIndicator(),
if (actions != null)
...actions!.map(
(action) => Padding(
padding: const EdgeInsets.only(right: 16),
child: action,
),
),
...actions!.map((action) => Padding(padding: const EdgeInsets.only(right: 16), child: action)),
if (kDebugMode || kProfileMode)
IconButton(
icon: const Icon(Icons.science_rounded),
onPressed: () => context.pushRoute(const FeatInDevRoute()),
),
if (showUploadButton)
const Padding(
padding: EdgeInsets.only(right: 20),
child: _BackupIndicator(),
),
const Padding(
padding: EdgeInsets.only(right: 20),
child: _ProfileIndicator(),
),
if (showUploadButton) const Padding(padding: EdgeInsets.only(right: 20), child: _BackupIndicator()),
const Padding(padding: EdgeInsets.only(right: 20), child: _ProfileIndicator()),
],
),
);
@ -159,23 +138,12 @@ class _ProfileIndicator extends ConsumerWidget {
const widgetSize = 30.0;
return InkWell(
onTap: () => showDialog(
context: context,
useRootNavigator: false,
builder: (ctx) => const ImmichAppBarDialog(),
),
onTap: () => showDialog(context: context, useRootNavigator: false, builder: (ctx) => const ImmichAppBarDialog()),
borderRadius: const BorderRadius.all(Radius.circular(12)),
child: Badge(
label: Container(
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(widgetSize / 2),
),
child: const Icon(
Icons.info,
color: Color.fromARGB(255, 243, 188, 106),
size: widgetSize / 2,
),
decoration: BoxDecoration(color: Colors.black, borderRadius: BorderRadius.circular(widgetSize / 2)),
child: const Icon(Icons.info, color: Color.fromARGB(255, 243, 188, 106), size: widgetSize / 2),
),
backgroundColor: Colors.transparent,
alignment: Alignment.bottomRight,
@ -183,17 +151,10 @@ class _ProfileIndicator extends ConsumerWidget {
serverInfoState.isVersionMismatch || ((user?.isAdmin ?? false) && serverInfoState.isNewReleaseAvailable),
offset: const Offset(-2, -12),
child: user == null
? const Icon(
Icons.face_outlined,
size: widgetSize,
)
? const Icon(Icons.face_outlined, size: widgetSize)
: Semantics(
label: "logged_in_as".tr(namedArgs: {"user": user.name}),
child: UserCircleAvatar(
radius: 17,
size: 31,
user: user,
),
child: UserCircleAvatar(radius: 17, size: 31, user: user),
),
),
);
@ -218,9 +179,7 @@ class _BackupIndicator extends ConsumerWidget {
height: widgetSize / 2,
decoration: BoxDecoration(
color: badgeBackground,
border: Border.all(
color: context.colorScheme.outline.withValues(alpha: .3),
),
border: Border.all(color: context.colorScheme.outline.withValues(alpha: .3)),
borderRadius: BorderRadius.circular(widgetSize / 2),
),
child: indicatorIcon,
@ -229,11 +188,7 @@ class _BackupIndicator extends ConsumerWidget {
alignment: Alignment.bottomRight,
isLabelVisible: indicatorIcon != null,
offset: const Offset(-2, -12),
child: Icon(
Icons.backup_rounded,
size: widgetSize,
color: context.primaryColor,
),
child: Icon(Icons.backup_rounded, size: widgetSize, color: context.primaryColor),
),
);
}
@ -263,8 +218,9 @@ class _BackupIndicator extends ConsumerWidget {
return Container(
padding: const EdgeInsets.all(3.5),
child: Theme(
data: context.themeData
.copyWith(progressIndicatorTheme: context.themeData.progressIndicatorTheme.copyWith(year2023: true)),
data: context.themeData.copyWith(
progressIndicatorTheme: context.themeData.progressIndicatorTheme.copyWith(year2023: true),
),
child: CircularProgressIndicator(
strokeWidth: 2,
strokeCap: StrokeCap.round,
@ -302,27 +258,13 @@ class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> with
@override
void initState() {
super.initState();
_rotationController = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_dismissalController = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: this,
);
_rotationAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(_rotationController);
_rotationController = AnimationController(duration: const Duration(seconds: 2), vsync: this);
_dismissalController = AnimationController(duration: const Duration(milliseconds: 300), vsync: this);
_rotationAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(_rotationController);
_dismissalAnimation = Tween<double>(
begin: 1.0,
end: 0.0,
).animate(
CurvedAnimation(
parent: _dismissalController,
curve: Curves.easeOutQuart,
),
);
).animate(CurvedAnimation(parent: _dismissalController, curve: Curves.easeOutQuart));
}
@override
@ -366,11 +308,7 @@ class _SyncStatusIndicatorState extends ConsumerState<_SyncStatusIndicator> with
opacity: isSyncing ? 1.0 : _dismissalAnimation.value,
child: Transform.rotate(
angle: _rotationAnimation.value * 2 * 3.14159 * -1, // Rotate counter-clockwise
child: Icon(
Icons.sync,
size: 24,
color: context.primaryColor,
),
child: Icon(Icons.sync, size: 24, color: context.primaryColor),
),
),
),

View file

@ -13,13 +13,7 @@ import 'package:octo_image/octo_image.dart';
import 'package:immich_mobile/providers/user.provider.dart';
class ImmichThumbnail extends HookConsumerWidget {
const ImmichThumbnail({
this.asset,
this.width = 250,
this.height = 250,
this.fit = BoxFit.cover,
super.key,
});
const ImmichThumbnail({this.asset, this.width = 250, this.height = 250, this.fit = BoxFit.cover, super.key});
final Asset? asset;
final double width;
@ -30,35 +24,19 @@ class ImmichThumbnail extends HookConsumerWidget {
/// either by using the asset ID or the asset itself
/// [asset] is the Asset to request, or else use [assetId] to get a remote
/// image provider
static ImageProvider imageProvider({
Asset? asset,
String? assetId,
String? userId,
int thumbnailSize = 256,
}) {
static ImageProvider imageProvider({Asset? asset, String? assetId, String? userId, int thumbnailSize = 256}) {
if (asset == null && assetId == null) {
throw Exception('Must supply either asset or assetId');
}
if (asset == null) {
return ImmichRemoteThumbnailProvider(
assetId: assetId!,
);
return ImmichRemoteThumbnailProvider(assetId: assetId!);
}
if (ImmichImage.useLocal(asset)) {
return ImmichLocalThumbnailProvider(
asset: asset,
height: thumbnailSize,
width: thumbnailSize,
userId: userId,
);
return ImmichLocalThumbnailProvider(asset: asset, height: thumbnailSize, width: thumbnailSize, userId: userId);
} else {
return ImmichRemoteThumbnailProvider(
assetId: asset.remoteId!,
height: thumbnailSize,
width: thumbnailSize,
);
return ImmichRemoteThumbnailProvider(assetId: asset.remoteId!, height: thumbnailSize, width: thumbnailSize);
}
}
@ -72,23 +50,13 @@ class ImmichThumbnail extends HookConsumerWidget {
color: Colors.grey,
width: width,
height: height,
child: const Center(
child: Icon(Icons.no_photography),
),
child: const Center(child: Icon(Icons.no_photography)),
);
}
final assetAltText = getAltText(
asset!.exifInfo,
asset!.fileCreatedAt,
asset!.type,
[],
);
final assetAltText = getAltText(asset!.exifInfo, asset!.fileCreatedAt, asset!.type, []);
final thumbnailProviderInstance = ImmichThumbnail.imageProvider(
asset: asset,
userId: userId,
);
final thumbnailProviderInstance = ImmichThumbnail.imageProvider(asset: asset, userId: userId);
customErrorBuilder(BuildContext ctx, Object error, StackTrace? stackTrace) {
thumbnailProviderInstance.evict();

View file

@ -5,18 +5,12 @@ class ImmichTitleText extends StatelessWidget {
final double fontSize;
final Color? color;
const ImmichTitleText({
super.key,
this.fontSize = 48,
this.color,
});
const ImmichTitleText({super.key, this.fontSize = 48, this.color});
@override
Widget build(BuildContext context) {
return Image(
image: AssetImage(
context.isDarkTheme ? 'assets/immich-text-dark.png' : 'assets/immich-text-light.png',
),
image: AssetImage(context.isDarkTheme ? 'assets/immich-text-dark.png' : 'assets/immich-text-light.png'),
width: fontSize * 4,
filterQuality: FilterQuality.high,
color: context.primaryColor,

View file

@ -16,25 +16,16 @@ class ImmichToast {
fToast.init(context);
Color getColor(ToastType type, BuildContext context) => switch (type) {
ToastType.info => context.primaryColor,
ToastType.success => const Color.fromARGB(255, 78, 140, 124),
ToastType.error => const Color.fromARGB(255, 220, 48, 85),
};
ToastType.info => context.primaryColor,
ToastType.success => const Color.fromARGB(255, 78, 140, 124),
ToastType.error => const Color.fromARGB(255, 220, 48, 85),
};
Icon getIcon(ToastType type) => switch (type) {
ToastType.info => Icon(
Icons.info_outline_rounded,
color: context.primaryColor,
),
ToastType.success => const Icon(
Icons.check_circle_rounded,
color: Color.fromARGB(255, 78, 140, 124),
),
ToastType.error => const Icon(
Icons.error_outline_rounded,
color: Color.fromARGB(255, 240, 162, 156),
),
};
ToastType.info => Icon(Icons.info_outline_rounded, color: context.primaryColor),
ToastType.success => const Icon(Icons.check_circle_rounded, color: Color.fromARGB(255, 78, 140, 124)),
ToastType.error => const Icon(Icons.error_outline_rounded, color: Color.fromARGB(255, 240, 162, 156)),
};
fToast.showToast(
child: Container(
@ -42,26 +33,17 @@ class ImmichToast {
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(16.0)),
color: context.colorScheme.surfaceContainer,
border: Border.all(
color: context.colorScheme.outline.withValues(alpha: .5),
width: 1,
),
border: Border.all(color: context.colorScheme.outline.withValues(alpha: .5), width: 1),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
getIcon(toastType),
const SizedBox(
width: 12.0,
),
const SizedBox(width: 12.0),
Flexible(
child: Text(
msg,
style: TextStyle(
color: getColor(toastType, context),
fontWeight: FontWeight.w600,
fontSize: 14,
),
style: TextStyle(color: getColor(toastType, context), fontWeight: FontWeight.w600, fontSize: 14),
),
),
],

View file

@ -12,14 +12,10 @@ class LocalAlbumsSliverAppBar extends StatelessWidget {
pinned: true,
snap: false,
backgroundColor: context.colorScheme.surfaceContainer,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(5)),
),
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))),
automaticallyImplyLeading: true,
centerTitle: true,
title: Text(
"on_this_device".t(context: context),
),
title: Text("on_this_device".t(context: context)),
);
}
}

View file

@ -9,16 +9,11 @@ import 'package:immich_mobile/widgets/map/map_thumbnail.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:maplibre_gl/maplibre_gl.dart';
Future<LatLng?> showLocationPicker({
required BuildContext context,
LatLng? initialLatLng,
}) {
Future<LatLng?> showLocationPicker({required BuildContext context, LatLng? initialLatLng}) {
return showDialog<LatLng?>(
context: context,
useRootNavigator: false,
builder: (ctx) => _LocationPicker(
initialLatLng: initialLatLng,
),
builder: (ctx) => _LocationPicker(initialLatLng: initialLatLng),
);
}
@ -27,9 +22,7 @@ enum _LocationPickerMode { map, manual }
class _LocationPicker extends HookWidget {
final LatLng? initialLatLng;
const _LocationPicker({
this.initialLatLng,
});
const _LocationPicker({this.initialLatLng});
@override
Widget build(BuildContext context) {
@ -39,9 +32,7 @@ class _LocationPicker extends HookWidget {
final pickerMode = useState(_LocationPickerMode.map);
Future<void> onMapTap() async {
final newLatLng = await context.pushRoute<LatLng?>(
MapLocationPickerRoute(initialLatLng: latlng),
);
final newLatLng = await context.pushRoute<LatLng?>(MapLocationPickerRoute(initialLatLng: latlng));
if (newLatLng != null) {
latitude.value = newLatLng.latitude;
longitude.value = newLatLng.longitude;
@ -81,10 +72,7 @@ class _LocationPicker extends HookWidget {
onPressed: () => context.maybePop(latlng),
child: Text(
"action_common_update",
style: context.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w600,
color: context.primaryColor,
),
style: context.textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor),
).tr(),
),
],
@ -129,10 +117,7 @@ class _ManualPickerInput extends HookWidget {
autofocus: false,
decoration: InputDecoration(
labelText: decorationText.tr(),
labelStyle: TextStyle(
fontWeight: FontWeight.bold,
color: context.primaryColor,
),
labelStyle: TextStyle(fontWeight: FontWeight.bold, color: context.primaryColor),
floatingLabelBehavior: FloatingLabelBehavior.auto,
border: const OutlineInputBorder(),
hintText: hintText.tr(),
@ -188,10 +173,7 @@ class _ManualPicker extends HookWidget {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
"edit_location_dialog_title",
textAlign: TextAlign.center,
).tr(),
const Text("edit_location_dialog_title", textAlign: TextAlign.center).tr(),
const SizedBox(height: 12),
TextButton.icon(
icon: const Text("location_picker_choose_on_map").tr(),
@ -228,27 +210,17 @@ class _MapPicker extends StatelessWidget {
final Function() onModeSwitch;
final Function() onMapTap;
const _MapPicker({
required this.latlng,
required this.onModeSwitch,
required this.onMapTap,
super.key,
});
const _MapPicker({required this.latlng, required this.onModeSwitch, required this.onMapTap, super.key});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
"edit_location_dialog_title",
textAlign: TextAlign.center,
).tr(),
const Text("edit_location_dialog_title", textAlign: TextAlign.center).tr(),
const SizedBox(height: 12),
TextButton.icon(
icon: Text(
"${latlng.latitude.toStringAsFixed(4)}, ${latlng.longitude.toStringAsFixed(4)}",
),
icon: Text("${latlng.latitude.toStringAsFixed(4)}, ${latlng.longitude.toStringAsFixed(4)}"),
label: const Icon(Icons.edit_outlined, size: 16),
onPressed: onModeSwitch,
),

View file

@ -14,11 +14,7 @@ import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
class MesmerizingSliverAppBar extends ConsumerStatefulWidget {
const MesmerizingSliverAppBar({
super.key,
required this.title,
this.icon = Icons.camera,
});
const MesmerizingSliverAppBar({super.key, required this.title, this.icon = Icons.camera});
final String title;
final IconData icon;
@ -62,23 +58,11 @@ class _MesmerizingSliverAppBarState extends ConsumerState<MesmerizingSliverAppBa
leading: IconButton(
icon: Icon(
Platform.isIOS ? Icons.arrow_back_ios_new_rounded : Icons.arrow_back,
color: Color.lerp(
Colors.white,
context.primaryColor,
_scrollProgress,
),
color: Color.lerp(Colors.white, context.primaryColor, _scrollProgress),
shadows: [
_scrollProgress < 0.95
? Shadow(
offset: const Offset(0, 2),
blurRadius: 5,
color: Colors.black.withValues(alpha: 0.5),
)
: const Shadow(
offset: Offset(0, 2),
blurRadius: 0,
color: Colors.transparent,
),
? Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5))
: const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent),
],
),
onPressed: () {
@ -106,11 +90,7 @@ class _MesmerizingSliverAppBarState extends ConsumerState<MesmerizingSliverAppBa
child: scrollProgress > 0.95
? Text(
widget.title,
style: TextStyle(
color: context.primaryColor,
fontWeight: FontWeight.w600,
fontSize: 18,
),
style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18),
)
: null,
),
@ -131,11 +111,7 @@ class _ExpandedBackground extends ConsumerStatefulWidget {
final String title;
final IconData icon;
const _ExpandedBackground({
required this.scrollProgress,
required this.title,
required this.icon,
});
const _ExpandedBackground({required this.scrollProgress, required this.title, required this.icon});
@override
ConsumerState<_ExpandedBackground> createState() => _ExpandedBackgroundState();
@ -149,20 +125,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
void initState() {
super.initState();
_slideController = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
);
_slideController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this);
_slideAnimation = Tween<Offset>(
begin: const Offset(0, 1.5),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: _slideController,
curve: Curves.easeOutCubic,
),
);
).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic));
Future.delayed(const Duration(milliseconds: 100), () {
if (mounted) {
@ -188,10 +156,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
offset: Offset(0, widget.scrollProgress * 50),
child: Transform.scale(
scale: 1.4 - (widget.scrollProgress * 0.2),
child: _RandomAssetBackground(
timelineService: timelineService,
icon: widget.icon,
),
child: _RandomAssetBackground(timelineService: timelineService, icon: widget.icon),
),
),
Container(
@ -202,9 +167,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
colors: [
Colors.transparent,
Colors.transparent,
Colors.black.withValues(
alpha: 0.6 + (widget.scrollProgress * 0.2),
),
Colors.black.withValues(alpha: 0.6 + (widget.scrollProgress * 0.2)),
],
stops: const [0.0, 0.65, 1.0],
),
@ -232,21 +195,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
fontSize: 36,
fontWeight: FontWeight.bold,
letterSpacing: 0.5,
shadows: [
Shadow(
offset: Offset(0, 2),
blurRadius: 12,
color: Colors.black45,
),
],
shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black45)],
),
),
),
),
AnimatedContainer(
duration: const Duration(milliseconds: 300),
child: const _ItemCountText(),
),
AnimatedContainer(duration: const Duration(milliseconds: 300), child: const _ItemCountText()),
],
),
),
@ -280,26 +234,15 @@ class _ItemCountTextState extends ConsumerState<_ItemCountText> {
@override
Widget build(BuildContext context) {
final assetCount = ref.watch(
timelineServiceProvider.select((s) => s.totalAssets),
);
final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets));
return Text(
'items_count'.t(
context: context,
args: {"count": assetCount},
),
'items_count'.t(context: context, args: {"count": assetCount}),
style: context.textTheme.labelLarge?.copyWith(
// letterSpacing: 0.2,
fontWeight: FontWeight.bold,
color: Colors.white,
shadows: [
const Shadow(
offset: Offset(0, 1),
blurRadius: 6,
color: Colors.black45,
),
],
shadows: [const Shadow(offset: Offset(0, 1), blurRadius: 6, color: Colors.black45)],
),
);
}
@ -309,10 +252,7 @@ class _RandomAssetBackground extends StatefulWidget {
final TimelineService timelineService;
final IconData icon;
const _RandomAssetBackground({
required this.timelineService,
required this.icon,
});
const _RandomAssetBackground({required this.timelineService, required this.icon});
@override
State<_RandomAssetBackground> createState() => _RandomAssetBackgroundState();
@ -332,50 +272,26 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic
void initState() {
super.initState();
_zoomController = AnimationController(
duration: const Duration(seconds: 12),
vsync: this,
);
_zoomController = AnimationController(duration: const Duration(seconds: 12), vsync: this);
_crossFadeController = AnimationController(
duration: const Duration(milliseconds: 1200),
vsync: this,
);
_crossFadeController = AnimationController(duration: const Duration(milliseconds: 1200), vsync: this);
_zoomAnimation = Tween<double>(
begin: 1.0,
end: 1.2,
).animate(
CurvedAnimation(
parent: _zoomController,
curve: Curves.easeInOut,
),
);
).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut));
_panAnimation = Tween<Offset>(
begin: Offset.zero,
end: const Offset(0.5, -0.5),
).animate(
CurvedAnimation(
parent: _zoomController,
curve: Curves.easeInOut,
),
);
).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut));
_crossFadeAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(
CurvedAnimation(
parent: _crossFadeController,
curve: Curves.easeInOutCubic,
),
);
).animate(CurvedAnimation(parent: _crossFadeController, curve: Curves.easeInOutCubic));
Future.delayed(
Durations.medium1,
() => _loadFirstAsset(),
);
Future.delayed(Durations.medium1, () => _loadFirstAsset());
}
@override
@ -465,9 +381,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic
}
return AnimatedBuilder(
animation: Listenable.merge(
[_zoomAnimation, _panAnimation, _crossFadeAnimation],
),
animation: Listenable.merge([_zoomAnimation, _panAnimation, _crossFadeAnimation]),
builder: (context, child) {
return Transform.scale(
scale: _zoomAnimation.value,
@ -499,11 +413,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic
return SizedBox(
width: double.infinity,
height: double.infinity,
child: Icon(
Icons.error_outline_rounded,
size: 24,
color: Colors.red[300],
),
child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]),
);
},
),
@ -530,11 +440,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic
return SizedBox(
width: double.infinity,
height: double.infinity,
child: Icon(
Icons.error_outline_rounded,
size: 24,
color: Colors.red[300],
),
child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]),
);
},
),

View file

@ -63,25 +63,13 @@ class _MesmerizingSliverAppBarState extends ConsumerState<RemoteAlbumSliverAppBa
return const SliverToBoxAdapter(child: SizedBox.shrink());
}
Color? actionIconColor = Color.lerp(
Colors.white,
context.primaryColor,
_scrollProgress,
);
Color? actionIconColor = Color.lerp(Colors.white, context.primaryColor, _scrollProgress);
List<Shadow> actionIconShadows = [
if (_scrollProgress < 0.95)
Shadow(
offset: const Offset(0, 2),
blurRadius: 5,
color: Colors.black.withValues(alpha: 0.5),
)
Shadow(offset: const Offset(0, 2), blurRadius: 5, color: Colors.black.withValues(alpha: 0.5))
else
const Shadow(
offset: Offset(0, 2),
blurRadius: 0,
color: Colors.transparent,
),
const Shadow(offset: Offset(0, 2), blurRadius: 0, color: Colors.transparent),
];
return isMultiSelectEnabled
@ -111,20 +99,12 @@ class _MesmerizingSliverAppBarState extends ConsumerState<RemoteAlbumSliverAppBa
actions: [
if (widget.onToggleAlbumOrder != null)
IconButton(
icon: Icon(
Icons.swap_vert_rounded,
color: actionIconColor,
shadows: actionIconShadows,
),
icon: Icon(Icons.swap_vert_rounded, color: actionIconColor, shadows: actionIconShadows),
onPressed: widget.onToggleAlbumOrder,
),
if (widget.onShowOptions != null)
IconButton(
icon: Icon(
Icons.more_vert,
color: actionIconColor,
shadows: actionIconShadows,
),
icon: Icon(Icons.more_vert, color: actionIconColor, shadows: actionIconShadows),
onPressed: widget.onShowOptions,
),
],
@ -149,11 +129,7 @@ class _MesmerizingSliverAppBarState extends ConsumerState<RemoteAlbumSliverAppBa
child: scrollProgress > 0.95
? Text(
currentAlbum.name,
style: TextStyle(
color: context.primaryColor,
fontWeight: FontWeight.w600,
fontSize: 18,
),
style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w600, fontSize: 18),
)
: null,
),
@ -174,11 +150,7 @@ class _ExpandedBackground extends ConsumerStatefulWidget {
final IconData icon;
final void Function()? onEditTitle;
const _ExpandedBackground({
required this.scrollProgress,
required this.icon,
this.onEditTitle,
});
const _ExpandedBackground({required this.scrollProgress, required this.icon, this.onEditTitle});
@override
ConsumerState<_ExpandedBackground> createState() => _ExpandedBackgroundState();
@ -192,20 +164,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
void initState() {
super.initState();
_slideController = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
);
_slideController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this);
_slideAnimation = Tween<Offset>(
begin: const Offset(0, 1.5),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: _slideController,
curve: Curves.easeOutCubic,
),
);
).animate(CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic));
Future.delayed(const Duration(milliseconds: 100), () {
if (mounted) {
@ -229,9 +193,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
return const SizedBox.shrink();
}
final dateRange = ref.watch(
remoteAlbumDateRangeProvider(currentAlbum.id),
);
final dateRange = ref.watch(remoteAlbumDateRangeProvider(currentAlbum.id));
return Stack(
fit: StackFit.expand,
children: [
@ -239,18 +201,12 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
offset: Offset(0, widget.scrollProgress * 50),
child: Transform.scale(
scale: 1.4 - (widget.scrollProgress * 0.2),
child: _RandomAssetBackground(
timelineService: timelineService,
icon: widget.icon,
),
child: _RandomAssetBackground(timelineService: timelineService, icon: widget.icon),
),
),
ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: widget.scrollProgress * 2.0,
sigmaY: widget.scrollProgress * 2.0,
),
filter: ImageFilter.blur(sigmaX: widget.scrollProgress * 2.0, sigmaY: widget.scrollProgress * 2.0),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
@ -260,9 +216,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
Colors.black.withValues(alpha: 0.05),
Colors.transparent,
Colors.black.withValues(alpha: 0.3),
Colors.black.withValues(
alpha: 0.6 + (widget.scrollProgress * 0.25),
),
Colors.black.withValues(alpha: 0.6 + (widget.scrollProgress * 0.25)),
],
stops: const [0.0, 0.15, 0.55, 1.0],
),
@ -291,32 +245,17 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
),
style: const TextStyle(
color: Colors.white,
shadows: [
Shadow(
offset: Offset(0, 2),
blurRadius: 12,
color: Colors.black87,
),
],
shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)],
),
),
const Text(
"",
style: TextStyle(
color: Colors.white,
shadows: [
Shadow(
offset: Offset(0, 2),
blurRadius: 12,
color: Colors.black87,
),
],
shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)],
),
),
AnimatedContainer(
duration: const Duration(milliseconds: 300),
child: const _ItemCountText(),
),
AnimatedContainer(duration: const Duration(milliseconds: 300), child: const _ItemCountText()),
],
),
GestureDetector(
@ -333,13 +272,7 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
fontSize: 36,
fontWeight: FontWeight.bold,
letterSpacing: 0.5,
shadows: [
Shadow(
offset: Offset(0, 2),
blurRadius: 12,
color: Colors.black54,
),
],
shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black54)],
),
),
),
@ -349,31 +282,20 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
GestureDetector(
onTap: widget.onEditTitle,
child: ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 80,
),
constraints: const BoxConstraints(maxHeight: 80),
child: SingleChildScrollView(
child: Text(
currentAlbum.description,
style: const TextStyle(
color: Colors.white,
fontSize: 14,
shadows: [
Shadow(
offset: Offset(0, 2),
blurRadius: 8,
color: Colors.black54,
),
],
shadows: [Shadow(offset: Offset(0, 2), blurRadius: 8, color: Colors.black54)],
),
),
),
),
),
const Padding(
padding: EdgeInsets.only(top: 8.0),
child: RemoteAlbumSharedUserIcons(),
),
const Padding(padding: EdgeInsets.only(top: 8.0), child: RemoteAlbumSharedUserIcons()),
],
),
),
@ -407,24 +329,13 @@ class _ItemCountTextState extends ConsumerState<_ItemCountText> {
@override
Widget build(BuildContext context) {
final assetCount = ref.watch(
timelineServiceProvider.select((s) => s.totalAssets),
);
final assetCount = ref.watch(timelineServiceProvider.select((s) => s.totalAssets));
return Text(
'items_count'.t(
context: context,
args: {"count": assetCount},
),
'items_count'.t(context: context, args: {"count": assetCount}),
style: context.textTheme.labelLarge?.copyWith(
color: Colors.white,
shadows: [
const Shadow(
offset: Offset(0, 2),
blurRadius: 12,
color: Colors.black87,
),
],
shadows: [const Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black87)],
),
);
}
@ -434,10 +345,7 @@ class _RandomAssetBackground extends StatefulWidget {
final TimelineService timelineService;
final IconData icon;
const _RandomAssetBackground({
required this.timelineService,
required this.icon,
});
const _RandomAssetBackground({required this.timelineService, required this.icon});
@override
State<_RandomAssetBackground> createState() => _RandomAssetBackgroundState();
@ -457,50 +365,26 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic
void initState() {
super.initState();
_zoomController = AnimationController(
duration: const Duration(seconds: 12),
vsync: this,
);
_zoomController = AnimationController(duration: const Duration(seconds: 12), vsync: this);
_crossFadeController = AnimationController(
duration: const Duration(milliseconds: 1200),
vsync: this,
);
_crossFadeController = AnimationController(duration: const Duration(milliseconds: 1200), vsync: this);
_zoomAnimation = Tween<double>(
begin: 1.0,
end: 1.2,
).animate(
CurvedAnimation(
parent: _zoomController,
curve: Curves.easeInOut,
),
);
).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut));
_panAnimation = Tween<Offset>(
begin: Offset.zero,
end: const Offset(0.5, -0.5),
).animate(
CurvedAnimation(
parent: _zoomController,
curve: Curves.easeInOut,
),
);
).animate(CurvedAnimation(parent: _zoomController, curve: Curves.easeInOut));
_crossFadeAnimation = Tween<double>(
begin: 0.0,
end: 1.0,
).animate(
CurvedAnimation(
parent: _crossFadeController,
curve: Curves.easeInOutCubic,
),
);
).animate(CurvedAnimation(parent: _crossFadeController, curve: Curves.easeInOutCubic));
Future.delayed(
Durations.medium1,
() => _loadFirstAsset(),
);
Future.delayed(Durations.medium1, () => _loadFirstAsset());
}
@override
@ -590,9 +474,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic
}
return AnimatedBuilder(
animation: Listenable.merge(
[_zoomAnimation, _panAnimation, _crossFadeAnimation],
),
animation: Listenable.merge([_zoomAnimation, _panAnimation, _crossFadeAnimation]),
builder: (context, child) {
return Transform.scale(
scale: _zoomAnimation.value,
@ -624,11 +506,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic
return SizedBox(
width: double.infinity,
height: double.infinity,
child: Icon(
Icons.error_outline_rounded,
size: 24,
color: Colors.red[300],
),
child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]),
);
},
),
@ -655,11 +533,7 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic
return SizedBox(
width: double.infinity,
height: double.infinity,
child: Icon(
Icons.error_outline_rounded,
size: 24,
color: Colors.red[300],
),
child: Icon(Icons.error_outline_rounded, size: 24, color: Colors.red[300]),
);
},
),

View file

@ -15,11 +15,7 @@ class ScaffoldErrorBody extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"scaffold_body_error_occurred",
style: context.textTheme.displayMedium,
textAlign: TextAlign.center,
).tr(),
Text("scaffold_body_error_occurred", style: context.textTheme.displayMedium, textAlign: TextAlign.center).tr(),
if (withIcon)
Center(
child: Padding(
@ -34,11 +30,7 @@ class ScaffoldErrorBody extends StatelessWidget {
if (withIcon && errorMsg != null)
Padding(
padding: const EdgeInsets.all(20),
child: Text(
errorMsg!,
style: context.textTheme.displaySmall,
textAlign: TextAlign.center,
),
child: Text(errorMsg!, style: context.textTheme.displaySmall, textAlign: TextAlign.center),
),
],
);

View file

@ -43,40 +43,22 @@ class SearchField extends StatelessWidget {
contentPadding: contentPadding,
filled: filled,
fillColor: context.primaryColor.withValues(alpha: 0.1),
hintStyle: context.textTheme.bodyLarge?.copyWith(
color: context.themeData.colorScheme.onSurfaceSecondary,
),
hintStyle: context.textTheme.bodyLarge?.copyWith(color: context.themeData.colorScheme.onSurfaceSecondary),
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(
Radius.circular(25),
),
borderSide: BorderSide(
color: context.colorScheme.surfaceDim,
),
borderRadius: const BorderRadius.all(Radius.circular(25)),
borderSide: BorderSide(color: context.colorScheme.surfaceDim),
),
enabledBorder: OutlineInputBorder(
borderRadius: const BorderRadius.all(
Radius.circular(25),
),
borderSide: BorderSide(
color: context.colorScheme.surfaceContainer,
),
borderRadius: const BorderRadius.all(Radius.circular(25)),
borderSide: BorderSide(color: context.colorScheme.surfaceContainer),
),
disabledBorder: OutlineInputBorder(
borderRadius: const BorderRadius.all(
Radius.circular(25),
),
borderSide: BorderSide(
color: context.colorScheme.surfaceDim,
),
borderRadius: const BorderRadius.all(Radius.circular(25)),
borderSide: BorderSide(color: context.colorScheme.surfaceDim),
),
focusedBorder: OutlineInputBorder(
borderRadius: const BorderRadius.all(
Radius.circular(25),
),
borderSide: BorderSide(
color: context.colorScheme.primary.withAlpha(100),
),
borderRadius: const BorderRadius.all(Radius.circular(25)),
borderSide: BorderSide(color: context.colorScheme.primary.withAlpha(100)),
),
prefixIcon: prefixIcon,
suffixIcon: suffixIcon,

View file

@ -7,9 +7,7 @@ import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
class SelectionSliverAppBar extends ConsumerStatefulWidget {
const SelectionSliverAppBar({
super.key,
});
const SelectionSliverAppBar({super.key});
@override
ConsumerState<SelectionSliverAppBar> createState() => _SelectionSliverAppBarState();
@ -18,13 +16,9 @@ class SelectionSliverAppBar extends ConsumerStatefulWidget {
class _SelectionSliverAppBarState extends ConsumerState<SelectionSliverAppBar> {
@override
Widget build(BuildContext context) {
final selection = ref.watch(
multiSelectProvider.select((s) => s.selectedAssets),
);
final selection = ref.watch(multiSelectProvider.select((s) => s.selectedAssets));
final toExclude = ref.watch(
multiSelectProvider.select((s) => s.lockedSelectionAssets),
);
final toExclude = ref.watch(multiSelectProvider.select((s) => s.lockedSelectionAssets));
final filteredAssets = selection.where((asset) {
return !toExclude.contains(asset);
@ -40,9 +34,7 @@ class _SelectionSliverAppBarState extends ConsumerState<SelectionSliverAppBar> {
pinned: true,
snap: false,
backgroundColor: context.colorScheme.surfaceContainer,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(5)),
),
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(5))),
automaticallyImplyLeading: false,
leading: IconButton(
icon: const Icon(Icons.close_rounded),
@ -52,22 +44,13 @@ class _SelectionSliverAppBarState extends ConsumerState<SelectionSliverAppBar> {
},
),
centerTitle: true,
title: Text(
"Select {count}".t(
context: context,
args: {
'count': filteredAssets.length.toString(),
},
),
),
title: Text("Select {count}".t(context: context, args: {'count': filteredAssets.length.toString()})),
actions: [
TextButton(
onPressed: () => onDone(filteredAssets),
child: Text(
'done'.t(context: context),
style: context.textTheme.titleSmall?.copyWith(
color: context.colorScheme.primary,
),
style: context.textTheme.titleSmall?.copyWith(color: context.colorScheme.primary),
),
),
],

View file

@ -11,10 +11,7 @@ class ShareDialog extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
const CircularProgressIndicator(),
Container(
margin: const EdgeInsets.only(top: 12),
child: const Text('share_dialog_preparing').tr(),
),
Container(margin: const EdgeInsets.only(top: 12), child: const Text('share_dialog_preparing').tr()),
],
),
);

View file

@ -6,21 +6,14 @@ import 'package:octo_image/octo_image.dart';
/// Simple set to show [OctoPlaceholder.circularProgressIndicator] as
/// placeholder and [OctoError.icon] as error.
OctoSet blurHashOrPlaceholder(
Uint8List? blurhash, {
BoxFit? fit,
Text? errorMessage,
}) {
OctoSet blurHashOrPlaceholder(Uint8List? blurhash, {BoxFit? fit, Text? errorMessage}) {
return OctoSet(
placeholderBuilder: blurHashPlaceholderBuilder(blurhash, fit: fit),
errorBuilder: blurHashErrorBuilder(blurhash, fit: fit, message: errorMessage),
);
}
OctoPlaceholderBuilder blurHashPlaceholderBuilder(
Uint8List? blurhash, {
BoxFit? fit,
}) {
OctoPlaceholderBuilder blurHashPlaceholderBuilder(Uint8List? blurhash, {BoxFit? fit}) {
return (context) => blurhash == null
? const ThumbnailPlaceholder()
: FadeInPlaceholderImage(

View file

@ -16,13 +16,7 @@ class UserCircleAvatar extends ConsumerWidget {
double size;
bool hasBorder;
UserCircleAvatar({
super.key,
this.radius = 22,
this.size = 44,
this.hasBorder = false,
required this.user,
});
UserCircleAvatar({super.key, this.radius = 22, this.size = 44, this.hasBorder = false, required this.user});
@override
Widget build(BuildContext context, WidgetRef ref) {
@ -43,12 +37,7 @@ class UserCircleAvatar extends ConsumerWidget {
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: hasBorder
? Border.all(
color: Colors.grey[500]!,
width: 1,
)
: null,
border: hasBorder ? Border.all(color: Colors.grey[500]!, width: 1) : null,
),
child: CircleAvatar(
backgroundColor: userAvatarColor,