perf(library): build DateFormat and RegExp once instead of per row

The month-year DateFormat was rebuilt for every loaded row and every
fast-scroller drag label; it is now cached per locale tag. Constant-pattern
RegExp objects built per call (or per word) are now static or top-level.
This commit is contained in:
zarzet committed 2026-10-18 21:56:46 +07:00
1 parent 0810ee7847
commit acbc614f42
5 files changed
+27 -14

No files matched your search

+2 -1
View File
@@ -269,6 +269,7 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen>
}
static final _qualitySeparators = RegExp(r'[\s_-]+');
static final _releaseYearPattern = RegExp(r'(\d{4})');
static (String, bool?) _qualitySignature(Track track) => (
(track.audioQuality ?? '').trim().toLowerCase().replaceAll(
@@ -360,7 +361,7 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen>
String? _releaseYear(String? date) {
if (date == null || date.isEmpty) return null;
final match = RegExp(r'(\d{4})').firstMatch(date);
final match = _releaseYearPattern.firstMatch(date);
return match?.group(1);
}
+12 -7
View File
@@ -13,6 +13,15 @@ class _LibrarySection {
int get length => end - start;
}
final _letterPattern = RegExp(r'[\p{L}]', unicode: true);
final _monthYearFormats = <String, DateFormat>{};
DateFormat _monthYearFormat(BuildContext context) {
final locale = Localizations.localeOf(context).toLanguageTag();
return _monthYearFormats.putIfAbsent(locale, () => DateFormat.yMMMM(locale));
}
/// First letter of [text] for an alphabetical index; digits and symbols
/// share `#`.
String _sectionInitial(String text) {
@@ -20,7 +29,7 @@ String _sectionInitial(String text) {
for (final character in trimmed.characters) {
final upper = character.toUpperCase();
if (upper != character.toLowerCase()) return upper;
if (RegExp(r'[\p{L}]', unicode: true).hasMatch(character)) return upper;
if (_letterPattern.hasMatch(character)) return upper;
return '#';
}
return '#';
@@ -79,9 +88,7 @@ extension _QueueTabSections on _QueueTabState {
return _sectionInitial(item.albumName);
}
if (_dateSorts.contains(_sortMode)) {
return DateFormat.yMMMM(
Localizations.localeOf(context).toLanguageTag(),
).format(item.addedAt);
return _monthYearFormat(context).format(item.addedAt);
}
return null;
}
@@ -98,9 +105,7 @@ extension _QueueTabSections on _QueueTabState {
}
if (_artistSorts.contains(_sortMode)) return _sectionInitial(artist);
if (_dateSorts.contains(_sortMode)) {
return DateFormat.yMMMM(
Localizations.localeOf(context).toLanguageTag(),
).format(added);
return _monthYearFormat(context).format(added);
}
return null;
}
@@ -3,6 +3,8 @@ part of 'track_metadata_screen.dart';
// Display helpers: quality/bitrate labels, service track-id formatting,
// snackbars, and path formatting.
final _spotifyTrackIdPattern = RegExp(r'^[A-Za-z0-9]{22}$');
extension _TrackMetadataDisplay on _TrackMetadataScreenState {
bool _isBitrateFormatValue(String? value) {
return const {
@@ -61,7 +63,7 @@ extension _TrackMetadataDisplay on _TrackMetadataScreenState {
String _displayServiceTrackId(String value) {
final raw = value.trim();
if (raw.isEmpty) return raw;
final spotifyTrackIdPattern = RegExp(r'^[A-Za-z0-9]{22}$');
final spotifyTrackIdPattern = _spotifyTrackIdPattern;
if (raw.startsWith('deezer:')) return raw.substring('deezer:'.length);
if (raw.startsWith('tidal:')) return raw.substring('tidal:'.length);
@@ -91,7 +93,7 @@ extension _TrackMetadataDisplay on _TrackMetadataScreenState {
String _serviceForTrackId(String value, {required String fallbackService}) {
final raw = value.trim();
if (raw.isEmpty) return fallbackService;
final spotifyTrackIdPattern = RegExp(r'^[A-Za-z0-9]{22}$');
final spotifyTrackIdPattern = _spotifyTrackIdPattern;
if (raw.startsWith('deezer:')) return MusicServices.deezer;
if (raw.startsWith('tidal:')) return MusicServices.tidal;
+6 -3
View File
@@ -15,6 +15,9 @@ class LyricPronunciationLayout {
static const _phraseGap = 4.0;
static const _pronunciationGap = 2.0;
static const _rowGap = 6.0;
static final _whitespaceRun = RegExp(r'\s+');
static final _endsWithWhitespace = RegExp(r'\s$');
static final _startsWithWhitespace = RegExp(r'^\s');
double get primaryHeight =>
_rows.fold(0.0, (height, row) => height + row.primaryHeight) +
@@ -42,7 +45,7 @@ class LyricPronunciationLayout {
maxWidth <= 0) {
return null;
}
String normalize(String text) => text.replaceAll(RegExp(r'\s+'), '');
String normalize(String text) => text.replaceAll(_whitespaceRun, '');
if (normalize(original.map((word) => word.text).join()) !=
normalize(line.text) ||
normalize(pronunciation.map((word) => word.text).join()) !=
@@ -57,8 +60,8 @@ class LyricPronunciationLayout {
for (var i = 1; i < pronunciation.length; i++) {
// Preserve syllables inside a word. Providers may split a romanized
// word into several timed spans without adding whitespace.
if (!RegExp(r'\s$').hasMatch(pronunciation[i - 1].text) &&
!RegExp(r'^\s').hasMatch(pronunciation[i].text)) {
if (!_endsWithWhitespace.hasMatch(pronunciation[i - 1].text) &&
!_startsWithWhitespace.hasMatch(pronunciation[i].text)) {
continue;
}
final start = pronunciation[i].time.inMilliseconds;
+3 -1
View File
@@ -671,9 +671,11 @@ String _peak(double value) {
return '${value.toStringAsFixed(6)} · ${_db(dbfs).replaceFirst(' dB', ' dBFS')}';
}
final _numberPattern = RegExp(r'[-+]?\d+(?:\.\d+)?');
/// Reads tag values such as "-6.52 dB" or "0.988831".
double? _number(Object? raw) {
if (raw is num) return raw.toDouble();
final match = RegExp(r'[-+]?\d+(?:\.\d+)?').firstMatch(raw?.toString() ?? '');
final match = _numberPattern.firstMatch(raw?.toString() ?? '');
return match == null ? null : double.tryParse(match[0]!);
}