Song-Tap oeffnet den Vollbild-Player mit grossem Cover
This commit is contained in:
@@ -5,6 +5,29 @@ Format angelehnt an [Keep a Changelog](https://keepachangelog.com/de/1.0.0/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### 🎧 Song antippen öffnet den Vollbild-Player (2026-08-27)
|
||||||
|
|
||||||
|
- 🐛 **Ein Song antippen öffnete früher nur die Wiedergabe, ohne zum
|
||||||
|
Vollbild-Player mit großem Cover zu wechseln** — man landete weiter auf
|
||||||
|
der Bibliotheks-/Playlist-/Server-Liste und musste erst den Mini-Player
|
||||||
|
antippen oder hochwischen, um das große Cover zu sehen. Behoben an den
|
||||||
|
drei Stellen, an denen ein Song EXPLIZIT zum Abspielen angetippt wird:
|
||||||
|
Bibliothek (`song_list.dart`), Wiedergabeliste
|
||||||
|
(`playlist_detail_screen.dart`) und Server-Titel
|
||||||
|
(`server_titel_screen.dart`, dort auch für die Knöpfe "Abspielen" und
|
||||||
|
"Zufällig", weil sie über dieselbe zentrale `_spiele`-Methode laufen).
|
||||||
|
Nutzt dasselbe, im Projekt etablierte Navigationsmuster wie der
|
||||||
|
Mini-Player (`Navigator.push(MaterialPageRoute(builder: (_) =>
|
||||||
|
NowPlayingScreen()))`), nach erfolgreicher Wiedergabe und vor einer
|
||||||
|
eventuellen Offline-Snackbar, damit die Meldung auf dem neuen Screen
|
||||||
|
sichtbar bleibt.
|
||||||
|
- ↩️ **Bewusst unverändert:** `queue_screen.dart` (Song innerhalb der
|
||||||
|
bereits offenen Warteschlange antippen) — dort ist man schon im
|
||||||
|
Player-Kontext, ein erneutes Aufspringen des Vollbild-Players bei jedem
|
||||||
|
Sprung wäre aufdringlich.
|
||||||
|
- ✅ **Getestet:** 3 neue Widget-Tests (RED→GREEN, je einer pro Stelle),
|
||||||
|
volle Suite (590 Tests) grün, `flutter analyze` ohne Befund.
|
||||||
|
|
||||||
### 📺🔑 YouTube-Tab: Server-User brauchen keine eigene Baka-Anmeldung mehr (2026-08-26)
|
### 📺🔑 YouTube-Tab: Server-User brauchen keine eigene Baka-Anmeldung mehr (2026-08-26)
|
||||||
|
|
||||||
- 🔑 **Server-User (Dustin, Baka, Tinker) sehen den Baka-Anmelde-Schritt nicht
|
- 🔑 **Server-User (Dustin, Baka, Tinker) sehen den Baka-Anmelde-Schritt nicht
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
|
|||||||
import '../library/database.dart';
|
import '../library/database.dart';
|
||||||
import '../library/song_media.dart';
|
import '../library/song_media.dart';
|
||||||
import '../player/audio_handler.dart';
|
import '../player/audio_handler.dart';
|
||||||
|
import '../player/now_playing_screen.dart';
|
||||||
import '../services/navidrome_service.dart';
|
import '../services/navidrome_service.dart';
|
||||||
import '../shared/cover.dart';
|
import '../shared/cover.dart';
|
||||||
import '../shared/lauf_balken.dart';
|
import '../shared/lauf_balken.dart';
|
||||||
@@ -77,6 +78,10 @@ class _ServerTitelScreenState extends State<ServerTitelScreen> {
|
|||||||
];
|
];
|
||||||
try {
|
try {
|
||||||
final uebersprungen = await handler.loadPlaylist(items, startIndex: ab);
|
final uebersprungen = await handler.loadPlaylist(items, startIndex: ab);
|
||||||
|
if (!mounted) return;
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(builder: (_) => const NowPlayingScreen()),
|
||||||
|
);
|
||||||
if (uebersprungen > 0) {
|
if (uebersprungen > 0) {
|
||||||
messenger.showSnackBar(
|
messenger.showSnackBar(
|
||||||
SnackBar(content: Text(offlineHinweis(uebersprungen))),
|
SnackBar(content: Text(offlineHinweis(uebersprungen))),
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../player/audio_handler.dart';
|
import '../player/audio_handler.dart';
|
||||||
|
import '../player/now_playing_screen.dart';
|
||||||
import '../playlists/create_playlist_dialog.dart';
|
import '../playlists/create_playlist_dialog.dart';
|
||||||
import '../settings/app_settings.dart';
|
import '../settings/app_settings.dart';
|
||||||
import '../shared/cover.dart';
|
import '../shared/cover.dart';
|
||||||
@@ -378,6 +379,10 @@ class SongZeile extends StatelessWidget {
|
|||||||
coverOf: (s) => categories.coverFor(s,
|
coverOf: (s) => categories.coverFor(s,
|
||||||
groupByCategory: settings.groupCoversByCategory),
|
groupByCategory: settings.groupCoversByCategory),
|
||||||
);
|
);
|
||||||
|
if (!context.mounted) return;
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(builder: (_) => const NowPlayingScreen()),
|
||||||
|
);
|
||||||
if (uebersprungen > 0) {
|
if (uebersprungen > 0) {
|
||||||
messenger.showSnackBar(
|
messenger.showSnackBar(
|
||||||
SnackBar(content: Text(offlineHinweis(uebersprungen))),
|
SnackBar(content: Text(offlineHinweis(uebersprungen))),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import '../library/database.dart';
|
|||||||
import '../library/playlist_service.dart';
|
import '../library/playlist_service.dart';
|
||||||
import '../library/song_media.dart';
|
import '../library/song_media.dart';
|
||||||
import '../player/audio_handler.dart';
|
import '../player/audio_handler.dart';
|
||||||
|
import '../player/now_playing_screen.dart';
|
||||||
import '../settings/app_settings.dart';
|
import '../settings/app_settings.dart';
|
||||||
import '../shared/cover.dart';
|
import '../shared/cover.dart';
|
||||||
import '../shared/lauf_balken.dart';
|
import '../shared/lauf_balken.dart';
|
||||||
@@ -133,6 +134,10 @@ class _Zeile extends StatelessWidget {
|
|||||||
coverOf: (s) => categories.coverFor(s,
|
coverOf: (s) => categories.coverFor(s,
|
||||||
groupByCategory: settings.groupCoversByCategory),
|
groupByCategory: settings.groupCoversByCategory),
|
||||||
);
|
);
|
||||||
|
if (!context.mounted) return;
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(builder: (_) => const NowPlayingScreen()),
|
||||||
|
);
|
||||||
if (uebersprungen > 0) {
|
if (uebersprungen > 0) {
|
||||||
messenger.showSnackBar(
|
messenger.showSnackBar(
|
||||||
SnackBar(content: Text(offlineHinweis(uebersprungen))),
|
SnackBar(content: Text(offlineHinweis(uebersprungen))),
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import 'package:audio_service/audio_service.dart';
|
||||||
|
import 'package:drift/drift.dart' show driftRuntimeOptions;
|
||||||
|
import 'package:drift/native.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
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/services/navidrome_service.dart';
|
||||||
|
|
||||||
|
/// Ersetzt in [MeloAudioHandler.loadPlaylist] den echten just_audio-Zugriff:
|
||||||
|
/// der spricht über Plattform-Kanäle, die im Widget-Test nicht existieren
|
||||||
|
/// und mit `MissingPluginException` scheitern würden. Alles andere
|
||||||
|
/// (Warteschlange, Mediendaten) bleibt echtes Handler-Verhalten.
|
||||||
|
class _FakeAudioHandler extends MeloAudioHandler {
|
||||||
|
_FakeAudioHandler(MeloDb db) : super(db: db);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<int> loadPlaylist(List<MediaItem> items, {int startIndex = 0}) async {
|
||||||
|
queue.add(items);
|
||||||
|
if (items.isNotEmpty) mediaItem.add(items[startIndex]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late MeloDb db;
|
||||||
|
late _FakeAudioHandler handler;
|
||||||
|
|
||||||
|
void stelleKanaeleRuhig() {
|
||||||
|
final messenger =
|
||||||
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger;
|
||||||
|
messenger.setMockMethodCallHandler(
|
||||||
|
const MethodChannel('plugins.flutter.io/path_provider'),
|
||||||
|
(call) async => '/tmp/melo-test',
|
||||||
|
);
|
||||||
|
messenger.setMockMethodCallHandler(
|
||||||
|
const MethodChannel('plugins.it_nomads.com/flutter_secure_storage'),
|
||||||
|
(call) async => null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
driftRuntimeOptions.dontWarnAboutMultipleDatabases = true;
|
||||||
|
SharedPreferences.setMockInitialValues({});
|
||||||
|
stelleKanaeleRuhig();
|
||||||
|
db = MeloDb(NativeDatabase.memory());
|
||||||
|
handler = _FakeAudioHandler(db);
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
handler.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
Widget wrap(Widget child) => MultiProvider(
|
||||||
|
providers: [
|
||||||
|
Provider<MeloDb>.value(value: db),
|
||||||
|
Provider<MeloAudioHandler>.value(value: handler),
|
||||||
|
],
|
||||||
|
child: MaterialApp(home: child),
|
||||||
|
);
|
||||||
|
|
||||||
|
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),
|
||||||
|
],
|
||||||
|
)));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.byType(NowPlayingScreen), findsNothing);
|
||||||
|
|
||||||
|
await tester.tap(find.text('Feuer'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.byType(NowPlayingScreen), findsOneWidget);
|
||||||
|
|
||||||
|
// Ohne diesen Abschluss bleibt ein drift-Stream-Abo beim Abbau des
|
||||||
|
// Baums als offener Fake-Timer stehen.
|
||||||
|
await tester.pumpWidget(const SizedBox());
|
||||||
|
await tester.pump(const Duration(milliseconds: 1));
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
import 'package:audio_service/audio_service.dart';
|
||||||
|
import 'package:drift/drift.dart' show Value, driftRuntimeOptions;
|
||||||
|
import 'package:drift/native.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:melo/library/category_service.dart';
|
||||||
|
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/settings/app_settings.dart';
|
||||||
|
|
||||||
|
/// Ersetzt in [MeloAudioHandler.loadPlaylist] den echten just_audio-Zugriff:
|
||||||
|
/// der spricht über Plattform-Kanäle, die im Widget-Test nicht existieren
|
||||||
|
/// und mit `MissingPluginException` scheitern würden. Alles andere
|
||||||
|
/// (Warteschlange, Mediendaten) bleibt echtes Handler-Verhalten.
|
||||||
|
class _FakeAudioHandler extends MeloAudioHandler {
|
||||||
|
_FakeAudioHandler(MeloDb db) : super(db: db);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<int> loadPlaylist(List<MediaItem> items, {int startIndex = 0}) async {
|
||||||
|
queue.add(items);
|
||||||
|
if (items.isNotEmpty) mediaItem.add(items[startIndex]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late MeloDb db;
|
||||||
|
late CategoryService kategorien;
|
||||||
|
late PlaylistService playlists;
|
||||||
|
late AppSettings einstellungen;
|
||||||
|
late _FakeAudioHandler handler;
|
||||||
|
|
||||||
|
void stelleKanaeleRuhig() {
|
||||||
|
final messenger =
|
||||||
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger;
|
||||||
|
messenger.setMockMethodCallHandler(
|
||||||
|
const MethodChannel('plugins.flutter.io/path_provider'),
|
||||||
|
(call) async => '/tmp/melo-test',
|
||||||
|
);
|
||||||
|
messenger.setMockMethodCallHandler(
|
||||||
|
const MethodChannel('plugins.it_nomads.com/flutter_secure_storage'),
|
||||||
|
(call) async => null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
driftRuntimeOptions.dontWarnAboutMultipleDatabases = true;
|
||||||
|
SharedPreferences.setMockInitialValues({});
|
||||||
|
stelleKanaeleRuhig();
|
||||||
|
db = MeloDb(NativeDatabase.memory());
|
||||||
|
kategorien = CategoryService(db);
|
||||||
|
playlists = PlaylistService(db);
|
||||||
|
einstellungen = AppSettings();
|
||||||
|
await einstellungen.init();
|
||||||
|
handler = _FakeAudioHandler(db);
|
||||||
|
await db.into(db.songs).insert(SongsCompanion.insert(
|
||||||
|
id: 'song-1',
|
||||||
|
path: '/music/roses.mp3',
|
||||||
|
title: 'Roses',
|
||||||
|
artist: const Value('Neoni'),
|
||||||
|
dateAddedMs: 0,
|
||||||
|
updatedAtMs: 0,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
handler.dispose();
|
||||||
|
kategorien.dispose();
|
||||||
|
playlists.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
Song derSong() => Song(
|
||||||
|
id: 'song-1',
|
||||||
|
path: '/music/roses.mp3',
|
||||||
|
title: 'Roses',
|
||||||
|
artist: 'Neoni',
|
||||||
|
dateAddedMs: 0,
|
||||||
|
updatedAtMs: 0,
|
||||||
|
deleted: false,
|
||||||
|
playCount: 0,
|
||||||
|
categoriesEdited: false,
|
||||||
|
metadataEdited: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
testWidgets('Song antippen öffnet den Vollbild-Player mit großem Cover',
|
||||||
|
(tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MultiProvider(
|
||||||
|
providers: [
|
||||||
|
Provider<MeloDb>.value(value: db),
|
||||||
|
Provider<MeloAudioHandler>.value(value: handler),
|
||||||
|
ChangeNotifierProvider<CategoryService>.value(value: kategorien),
|
||||||
|
ChangeNotifierProvider<PlaylistService>.value(value: playlists),
|
||||||
|
ChangeNotifierProvider<AppSettings>.value(value: einstellungen),
|
||||||
|
],
|
||||||
|
child: MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: SongZeile(
|
||||||
|
song: derSong(),
|
||||||
|
warteschlange: [derSong()],
|
||||||
|
index: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.byType(NowPlayingScreen), findsNothing);
|
||||||
|
|
||||||
|
await tester.tap(find.text('Roses'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.byType(NowPlayingScreen), findsOneWidget);
|
||||||
|
|
||||||
|
// Ohne diesen Abschluss bleibt ein drift-Stream-Abo (Favoriten-Herz)
|
||||||
|
// beim Abbau des Baums als offener Fake-Timer stehen — dieselbe
|
||||||
|
// Aufräum-Zeile wie in song_zeile_lauf_test.dart.
|
||||||
|
await tester.pumpWidget(const SizedBox());
|
||||||
|
await tester.pump(const Duration(milliseconds: 1));
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
import 'package:audio_service/audio_service.dart';
|
||||||
|
import 'package:drift/drift.dart' show Value, driftRuntimeOptions;
|
||||||
|
import 'package:drift/native.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
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/playlists/playlist_detail_screen.dart';
|
||||||
|
import 'package:melo/settings/app_settings.dart';
|
||||||
|
|
||||||
|
/// Ersetzt in [MeloAudioHandler.loadPlaylist] den echten just_audio-Zugriff:
|
||||||
|
/// der spricht über Plattform-Kanäle, die im Widget-Test nicht existieren
|
||||||
|
/// und mit `MissingPluginException` scheitern würden. Alles andere
|
||||||
|
/// (Warteschlange, Mediendaten) bleibt echtes Handler-Verhalten.
|
||||||
|
class _FakeAudioHandler extends MeloAudioHandler {
|
||||||
|
_FakeAudioHandler(MeloDb db) : super(db: db);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<int> loadPlaylist(List<MediaItem> items, {int startIndex = 0}) async {
|
||||||
|
queue.add(items);
|
||||||
|
if (items.isNotEmpty) mediaItem.add(items[startIndex]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late MeloDb db;
|
||||||
|
late CategoryService kategorien;
|
||||||
|
late PlaylistService playlists;
|
||||||
|
late AppSettings einstellungen;
|
||||||
|
late _FakeAudioHandler handler;
|
||||||
|
late String playlistId;
|
||||||
|
|
||||||
|
void stelleKanaeleRuhig() {
|
||||||
|
final messenger =
|
||||||
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger;
|
||||||
|
messenger.setMockMethodCallHandler(
|
||||||
|
const MethodChannel('plugins.flutter.io/path_provider'),
|
||||||
|
(call) async => '/tmp/melo-test',
|
||||||
|
);
|
||||||
|
messenger.setMockMethodCallHandler(
|
||||||
|
const MethodChannel('plugins.it_nomads.com/flutter_secure_storage'),
|
||||||
|
(call) async => null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
driftRuntimeOptions.dontWarnAboutMultipleDatabases = true;
|
||||||
|
SharedPreferences.setMockInitialValues({});
|
||||||
|
stelleKanaeleRuhig();
|
||||||
|
db = MeloDb(NativeDatabase.memory());
|
||||||
|
kategorien = CategoryService(db);
|
||||||
|
playlists = PlaylistService(db);
|
||||||
|
einstellungen = AppSettings();
|
||||||
|
await einstellungen.init();
|
||||||
|
handler = _FakeAudioHandler(db);
|
||||||
|
await db.into(db.songs).insert(SongsCompanion.insert(
|
||||||
|
id: 'song-1',
|
||||||
|
path: '/music/roses.mp3',
|
||||||
|
title: 'Roses',
|
||||||
|
artist: const Value('Neoni'),
|
||||||
|
dateAddedMs: 0,
|
||||||
|
updatedAtMs: 0,
|
||||||
|
));
|
||||||
|
playlistId = await db.createPlaylist('Road Trip');
|
||||||
|
await db.addSongToPlaylist(playlistId, 'song-1', 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
handler.dispose();
|
||||||
|
kategorien.dispose();
|
||||||
|
playlists.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'Song in einer Wiedergabeliste antippen öffnet den Vollbild-Player',
|
||||||
|
(tester) async {
|
||||||
|
final playlist = Playlist(
|
||||||
|
id: playlistId,
|
||||||
|
name: 'Road Trip',
|
||||||
|
createdAtMs: 0,
|
||||||
|
updatedAtMs: 0,
|
||||||
|
deleted: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MultiProvider(
|
||||||
|
providers: [
|
||||||
|
Provider<MeloDb>.value(value: db),
|
||||||
|
Provider<MeloAudioHandler>.value(value: handler),
|
||||||
|
ChangeNotifierProvider<CategoryService>.value(value: kategorien),
|
||||||
|
ChangeNotifierProvider<PlaylistService>.value(value: playlists),
|
||||||
|
ChangeNotifierProvider<AppSettings>.value(value: einstellungen),
|
||||||
|
],
|
||||||
|
child: MaterialApp(
|
||||||
|
home: PlaylistDetailScreen(playlist: playlist),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.byType(NowPlayingScreen), findsNothing);
|
||||||
|
|
||||||
|
await tester.tap(find.text('Roses'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.byType(NowPlayingScreen), findsOneWidget);
|
||||||
|
|
||||||
|
// Ohne diesen Abschluss bleibt ein drift-Stream-Abo beim Abbau des
|
||||||
|
// Baums als offener Fake-Timer stehen.
|
||||||
|
await tester.pumpWidget(const SizedBox());
|
||||||
|
await tester.pump(const Duration(milliseconds: 1));
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user