From aea80d9755caf2f92dc534886ad39a2dfaf84579 Mon Sep 17 00:00:00 2001 From: "Hermes (Server)" Date: Sat, 29 Aug 2026 14:34:30 +0200 Subject: [PATCH] =?UTF-8?q?3=20Tap-Stellen=20=C3=B6ffnen=20den=20Vollbild-?= =?UTF-8?q?Player=20jetzt=20=C3=BCber=20PlayerExpansionController=20statt?= =?UTF-8?q?=20Navigator.push?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zusätzlich test/player/now_playing_heart_test.dart angepasst (Plan-Lücke: nutzt denselben SongZeile-Tap-Pfad, war nicht in Task 3s Datei-Liste erwähnt) — Provider ergänzt, Route für die Layout-Prüfung selbst separat geöffnet, bis Task 5 NowPlayingScreen zu Overlay-Inhalt umbaut. --- lib/downloads/server_titel_screen.dart | 6 ++-- lib/library/song_list.dart | 6 ++-- lib/playlists/playlist_detail_screen.dart | 6 ++-- .../server_titel_navigation_test.dart | 30 ++++++++++++------- test/library/song_list_navigation_test.dart | 10 +++++-- test/player/now_playing_heart_test.dart | 21 +++++++++++++ .../playlist_detail_navigation_test.dart | 10 +++++-- 7 files changed, 60 insertions(+), 29 deletions(-) diff --git a/lib/downloads/server_titel_screen.dart b/lib/downloads/server_titel_screen.dart index bb860e7..ac43ea8 100644 --- a/lib/downloads/server_titel_screen.dart +++ b/lib/downloads/server_titel_screen.dart @@ -5,7 +5,7 @@ import 'package:provider/provider.dart'; import '../library/database.dart'; import '../library/song_media.dart'; import '../player/audio_handler.dart'; -import '../player/now_playing_screen.dart'; +import '../player/player_expansion_controller.dart'; import '../services/navidrome_service.dart'; import '../shared/cover.dart'; import '../shared/lauf_balken.dart'; @@ -79,9 +79,7 @@ class _ServerTitelScreenState extends State { try { final uebersprungen = await handler.loadPlaylist(items, startIndex: ab); if (!mounted) return; - Navigator.of(context).push( - MaterialPageRoute(builder: (_) => const NowPlayingScreen()), - ); + context.read().open(context); if (uebersprungen > 0) { messenger.showSnackBar( SnackBar(content: Text(offlineHinweis(uebersprungen))), diff --git a/lib/library/song_list.dart b/lib/library/song_list.dart index 1e714af..6368c56 100644 --- a/lib/library/song_list.dart +++ b/lib/library/song_list.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../player/audio_handler.dart'; -import '../player/now_playing_screen.dart'; +import '../player/player_expansion_controller.dart'; import '../playlists/create_playlist_dialog.dart'; import '../settings/app_settings.dart'; import '../shared/cover.dart'; @@ -384,9 +384,7 @@ class SongZeile extends StatelessWidget { groupByCategory: settings.groupCoversByCategory), ); if (!context.mounted) return; - Navigator.of(context).push( - MaterialPageRoute(builder: (_) => const NowPlayingScreen()), - ); + context.read().open(context); if (uebersprungen > 0) { messenger.showSnackBar( SnackBar(content: Text(offlineHinweis(uebersprungen))), diff --git a/lib/playlists/playlist_detail_screen.dart b/lib/playlists/playlist_detail_screen.dart index 6e32ee1..da72e95 100644 --- a/lib/playlists/playlist_detail_screen.dart +++ b/lib/playlists/playlist_detail_screen.dart @@ -7,7 +7,7 @@ import '../library/database.dart'; import '../library/playlist_service.dart'; import '../library/song_media.dart'; import '../player/audio_handler.dart'; -import '../player/now_playing_screen.dart'; +import '../player/player_expansion_controller.dart'; import '../settings/app_settings.dart'; import '../shared/cover.dart'; import '../shared/lauf_balken.dart'; @@ -135,9 +135,7 @@ class _Zeile extends StatelessWidget { groupByCategory: settings.groupCoversByCategory), ); if (!context.mounted) return; - Navigator.of(context).push( - MaterialPageRoute(builder: (_) => const NowPlayingScreen()), - ); + context.read().open(context); if (uebersprungen > 0) { messenger.showSnackBar( SnackBar(content: Text(offlineHinweis(uebersprungen))), diff --git a/test/downloads/server_titel_navigation_test.dart b/test/downloads/server_titel_navigation_test.dart index 915e402..ac82165 100644 --- a/test/downloads/server_titel_navigation_test.dart +++ b/test/downloads/server_titel_navigation_test.dart @@ -9,7 +9,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:melo/downloads/server_titel_screen.dart'; import 'package:melo/library/database.dart'; import 'package:melo/player/audio_handler.dart'; -import 'package:melo/player/now_playing_screen.dart'; +import 'package:melo/player/player_expansion_controller.dart'; import 'package:melo/services/navidrome_service.dart'; /// Ersetzt in [MeloAudioHandler.loadPlaylist] den echten just_audio-Zugriff: @@ -57,10 +57,13 @@ void main() { handler.dispose(); }); - Widget wrap(Widget child) => MultiProvider( + Widget wrap(Widget child, PlayerExpansionController expansion) => + MultiProvider( providers: [ Provider.value(value: db), Provider.value(value: handler), + ChangeNotifierProvider.value( + value: expansion), ], child: MaterialApp(home: child), ); @@ -68,21 +71,26 @@ void main() { testWidgets('Titel antippen öffnet den Vollbild-Player', (tester) async { final navidrome = NavidromeService() ..setCredentials('https://musik.baka-net.de', 'Baka', 'geheim'); - await tester.pumpWidget(wrap(ServerTitelScreen( - titel: 'Ablaze', - navidrome: navidrome, - holeTitel: () async => const [ - SubsonicSong(id: 's1', titel: 'Feuer', kuenstler: 'X', dauerSekunden: 120), - ], - ))); + final expansion = PlayerExpansionController(vsync: tester); + addTearDown(expansion.dispose); + await tester.pumpWidget(wrap( + ServerTitelScreen( + titel: 'Ablaze', + navidrome: navidrome, + holeTitel: () async => const [ + SubsonicSong(id: 's1', titel: 'Feuer', kuenstler: 'X', dauerSekunden: 120), + ], + ), + expansion, + )); await tester.pumpAndSettle(); - expect(find.byType(NowPlayingScreen), findsNothing); + expect(expansion.progress, 0.0); await tester.tap(find.text('Feuer')); await tester.pumpAndSettle(); - expect(find.byType(NowPlayingScreen), findsOneWidget); + expect(expansion.progress, 1.0); // Ohne diesen Abschluss bleibt ein drift-Stream-Abo beim Abbau des // Baums als offener Fake-Timer stehen. diff --git a/test/library/song_list_navigation_test.dart b/test/library/song_list_navigation_test.dart index 920281a..2be4c8f 100644 --- a/test/library/song_list_navigation_test.dart +++ b/test/library/song_list_navigation_test.dart @@ -11,7 +11,7 @@ import 'package:melo/library/database.dart'; import 'package:melo/library/playlist_service.dart'; import 'package:melo/library/song_list.dart'; import 'package:melo/player/audio_handler.dart'; -import 'package:melo/player/now_playing_screen.dart'; +import 'package:melo/player/player_expansion_controller.dart'; import 'package:melo/settings/app_settings.dart'; /// Ersetzt in [MeloAudioHandler.loadPlaylist] den echten just_audio-Zugriff: @@ -91,6 +91,8 @@ void main() { testWidgets('Song antippen öffnet den Vollbild-Player mit großem Cover', (tester) async { + final expansion = PlayerExpansionController(vsync: tester); + addTearDown(expansion.dispose); await tester.pumpWidget( MultiProvider( providers: [ @@ -99,6 +101,8 @@ void main() { ChangeNotifierProvider.value(value: kategorien), ChangeNotifierProvider.value(value: playlists), ChangeNotifierProvider.value(value: einstellungen), + ChangeNotifierProvider.value( + value: expansion), ], child: MaterialApp( home: Scaffold( @@ -112,12 +116,12 @@ void main() { ), ); - expect(find.byType(NowPlayingScreen), findsNothing); + expect(expansion.progress, 0.0); await tester.tap(find.text('Roses')); await tester.pumpAndSettle(); - expect(find.byType(NowPlayingScreen), findsOneWidget); + expect(expansion.progress, 1.0); // Ohne diesen Abschluss bleibt ein drift-Stream-Abo (Favoriten-Herz) // beim Abbau des Baums als offener Fake-Timer stehen — dieselbe diff --git a/test/player/now_playing_heart_test.dart b/test/player/now_playing_heart_test.dart index 3e3b7a8..b616d2e 100644 --- a/test/player/now_playing_heart_test.dart +++ b/test/player/now_playing_heart_test.dart @@ -12,6 +12,7 @@ import 'package:melo/library/playlist_service.dart'; import 'package:melo/library/song_list.dart'; import 'package:melo/player/audio_handler.dart'; import 'package:melo/player/now_playing_screen.dart'; +import 'package:melo/player/player_expansion_controller.dart'; import 'package:melo/settings/app_settings.dart'; import 'package:melo/shared/favorite_button.dart'; @@ -92,7 +93,19 @@ void main() { // song_list_navigation_test.dart (Tap auf eine SongZeile) — dieser Weg ist // dort nachweislich unproblematisch, im Unterschied zu einem direkten // Aufbau von NowPlayingScreen mit vorab befülltem MediaItem-Stream. + // + // Seit dem Umbau auf PlayerExpansionController (Plan + // 2026-08-29-blur-oeffnen-transition, Task 3) zeigt der Tap allein noch + // keine sichtbare Oberfläche — das Overlay-Rendering kommt erst mit + // Task 4/5 ("Bekannte Zwischenlücke", siehe Plan). Für die Prüfung von + // NowPlayingScreens eigenem Layout (Herz-Position) wird die Route hier + // deshalb zusätzlich, unabhängig vom Produktions-Pfad, direkt geöffnet. + // Sobald Task 5 NowPlayingScreen zu Overlay-Inhalt umbaut, muss dieser + // Test ohnehin überarbeitet werden (Task 5 durchsucht dafür selbst + // `test -iname "*now_playing*"`, was diese Datei einschließt). Future oeffnePlayer(WidgetTester tester) async { + final expansion = PlayerExpansionController(vsync: tester); + addTearDown(expansion.dispose); await tester.pumpWidget( MultiProvider( providers: [ @@ -101,6 +114,8 @@ void main() { ChangeNotifierProvider.value(value: kategorien), ChangeNotifierProvider.value(value: playlists), ChangeNotifierProvider.value(value: einstellungen), + ChangeNotifierProvider.value( + value: expansion), ], child: MaterialApp( home: Scaffold( @@ -116,6 +131,12 @@ void main() { await tester.tap(find.text('Roses')); await tester.pumpAndSettle(); + expect(expansion.progress, 1.0); + + Navigator.of(tester.element(find.byType(SongZeile))).push( + MaterialPageRoute(builder: (_) => const NowPlayingScreen()), + ); + await tester.pumpAndSettle(); } Future raeumeAuf(WidgetTester tester) async { diff --git a/test/playlists/playlist_detail_navigation_test.dart b/test/playlists/playlist_detail_navigation_test.dart index 9b7b890..bcb9588 100644 --- a/test/playlists/playlist_detail_navigation_test.dart +++ b/test/playlists/playlist_detail_navigation_test.dart @@ -10,7 +10,7 @@ import 'package:melo/library/category_service.dart'; import 'package:melo/library/database.dart'; import 'package:melo/library/playlist_service.dart'; import 'package:melo/player/audio_handler.dart'; -import 'package:melo/player/now_playing_screen.dart'; +import 'package:melo/player/player_expansion_controller.dart'; import 'package:melo/playlists/playlist_detail_screen.dart'; import 'package:melo/settings/app_settings.dart'; @@ -90,6 +90,8 @@ void main() { deleted: false, ); + final expansion = PlayerExpansionController(vsync: tester); + addTearDown(expansion.dispose); await tester.pumpWidget( MultiProvider( providers: [ @@ -98,6 +100,8 @@ void main() { ChangeNotifierProvider.value(value: kategorien), ChangeNotifierProvider.value(value: playlists), ChangeNotifierProvider.value(value: einstellungen), + ChangeNotifierProvider.value( + value: expansion), ], child: MaterialApp( home: PlaylistDetailScreen(playlist: playlist), @@ -106,12 +110,12 @@ void main() { ); await tester.pumpAndSettle(); - expect(find.byType(NowPlayingScreen), findsNothing); + expect(expansion.progress, 0.0); await tester.tap(find.text('Roses')); await tester.pumpAndSettle(); - expect(find.byType(NowPlayingScreen), findsOneWidget); + expect(expansion.progress, 1.0); // Ohne diesen Abschluss bleibt ein drift-Stream-Abo beim Abbau des // Baums als offener Fake-Timer stehen.