feat(mobile): dynamic multi-line album name (#26040)

* feat(mobile): dynamic multi-line album name

Album names are currently limited to a single line, and scroll on
overflow. It would be better if album names were multi-line, and even
better if the font size was dynamic depending on how many lines there
are. The album name should then overflow with an ellipsis.

This is actually quite similar to how Google Photos handles album names.

* lint

---------

Co-authored-by: timonrieger <mail@timonrieger.de>
This commit is contained in:
Thomas
2026-02-15 16:53:45 +00:00
committed by GitHub
parent 75e3b0467a
commit 5f87047490

View File

@@ -3,7 +3,7 @@ import 'dart:io';
import 'dart:ui';
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:easy_localization/easy_localization.dart' hide TextDirection;
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
@@ -254,22 +254,9 @@ class _ExpandedBackgroundState extends ConsumerState<_ExpandedBackground> with S
),
GestureDetector(
onTap: widget.onEditTitle,
child: SizedBox(
width: double.infinity,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Text(
currentAlbum.name,
maxLines: 1,
style: const TextStyle(
color: Colors.white,
fontSize: 36,
fontWeight: FontWeight.bold,
letterSpacing: 0.5,
shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black54)],
),
),
),
child: LayoutBuilder(
builder: (context, constraints) =>
_DynamicText(text: currentAlbum.name, maxWidth: constraints.maxWidth),
),
),
if (currentAlbum.description.isNotEmpty)
@@ -549,3 +536,46 @@ class _RandomAssetBackgroundState extends State<_RandomAssetBackground> with Tic
);
}
}
class _DynamicText extends StatelessWidget {
final String text;
final double maxWidth;
const _DynamicText({required this.text, required this.maxWidth});
static const _baseTextStyle = TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
letterSpacing: 0.5,
shadows: [Shadow(offset: Offset(0, 2), blurRadius: 12, color: Colors.black54)],
overflow: TextOverflow.ellipsis,
);
int _lineCount(double fontSize) {
final textPainter = TextPainter(
text: TextSpan(
text: text,
style: _baseTextStyle.copyWith(fontSize: fontSize),
),
maxLines: 3,
textDirection: TextDirection.ltr,
)..layout(maxWidth: maxWidth);
return textPainter.computeLineMetrics().length;
}
double _fontSize() {
final fontSizes = [44.0, 36.0];
for (final fontSize in fontSizes) {
final lineCount = _lineCount(fontSize);
if (lineCount == 1) {
return fontSize;
}
}
return 28;
}
@override
Widget build(BuildContext context) {
return Text(text, style: _baseTextStyle.copyWith(fontSize: _fontSize()), maxLines: 3);
}
}