Tinker-Feedback: 5 Bugfixes (Shuffle/Repeat, Full-Cover, Auto-Play, Scan-Button, Back-Geste) #6
@@ -5,6 +5,21 @@ Format angelehnt an [Keep a Changelog](https://keepachangelog.com/de/1.0.0/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### ⬅️ Zurück-Geste auf Nicht-Start-Tabs minimierte die App (2026-08-27)
|
||||||
|
|
||||||
|
- 🐛 **Auf "Favoriten" (oder einem anderen Tab außer "Meine Musik") tippen und
|
||||||
|
dann die System-Zurück-Geste nutzen minimierte die App**, statt zur
|
||||||
|
vorherigen Ansicht zu navigieren. Grund: Tab-Wechsel in `HomeShell`
|
||||||
|
laufen nur über `setState` (`IndexedStack` + `BottomNavigationBar`), nie
|
||||||
|
über `Navigator.push` — es lag also nie eine Route auf dem
|
||||||
|
Navigator-Stack, die eine Zurück-Geste hätte poppen können.
|
||||||
|
- 🔧 **Fix:** `build()` in `main.dart` in ein `PopScope` gewickelt
|
||||||
|
(`canPop: _index == 0`), das bei einem Nicht-Start-Tab zuerst zu Tab 0
|
||||||
|
("Meine Musik") zurückspringt, statt die App zu verlassen.
|
||||||
|
- ✅ **Getestet:** neuer Widget-Test in `home_shell_test.dart` (RED→GREEN,
|
||||||
|
simuliert die System-Zurück-Geste über `WidgetsApp.didPopRoute()`), volle
|
||||||
|
Suite (593 Tests) grün, `flutter analyze` ohne Befund.
|
||||||
|
|
||||||
### 🔄 Doppelten Scan-Button auf Android entfernt (2026-08-27)
|
### 🔄 Doppelten Scan-Button auf Android entfernt (2026-08-27)
|
||||||
|
|
||||||
- 🐛 **In den Einstellungen gab es auf Android zwei Buttons für dieselbe
|
- 🐛 **In den Einstellungen gab es auf Android zwei Buttons für dieselbe
|
||||||
|
|||||||
+10
-1
@@ -222,7 +222,15 @@ class _HomeShellState extends State<HomeShell> with WidgetsBindingObserver {
|
|||||||
const FavoritesScreen(),
|
const FavoritesScreen(),
|
||||||
const YoutubeSearchScreen(),
|
const YoutubeSearchScreen(),
|
||||||
];
|
];
|
||||||
return Scaffold(
|
return PopScope(
|
||||||
|
// Tab-Wechsel laufen nur über setState, nie über Navigator.push — die
|
||||||
|
// Zurück-Geste auf einem Nicht-Start-Tab hätte sonst nichts zum
|
||||||
|
// Poppen und würde die App minimieren statt zu "Meine Musik" zu gehen.
|
||||||
|
canPop: _index == 0,
|
||||||
|
onPopInvokedWithResult: (didPop, _) {
|
||||||
|
if (!didPop) setState(() => _index = 0);
|
||||||
|
},
|
||||||
|
child: Scaffold(
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: IndexedStack(index: _index, children: tabs)),
|
Expanded(child: IndexedStack(index: _index, children: tabs)),
|
||||||
@@ -257,6 +265,7 @@ class _HomeShellState extends State<HomeShell> with WidgetsBindingObserver {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,4 +72,71 @@ void main() {
|
|||||||
handler.dispose();
|
handler.dispose();
|
||||||
await db.close();
|
await db.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Zurück-Geste auf einem Nicht-Start-Tab wechselt zu '
|
||||||
|
'"Meine Musik" statt die App zu minimieren', (tester) async {
|
||||||
|
final db = MeloDb(NativeDatabase.memory());
|
||||||
|
final lib = LibraryService(db);
|
||||||
|
final playlists = PlaylistService(db);
|
||||||
|
final handler = MeloAudioHandler(db: db);
|
||||||
|
final auth = BakaAuth(speicher: _MemorySpeicher());
|
||||||
|
|
||||||
|
await tester.pumpWidget(MultiProvider(
|
||||||
|
providers: [
|
||||||
|
Provider<MeloDb>.value(value: db),
|
||||||
|
ChangeNotifierProvider<LibraryService>.value(value: lib),
|
||||||
|
ChangeNotifierProvider<PlaylistService>.value(value: playlists),
|
||||||
|
Provider<MeloAudioHandler>.value(value: handler),
|
||||||
|
ChangeNotifierProvider<CategoryService>(create: (_) => CategoryService(db)),
|
||||||
|
ChangeNotifierProvider<AppSettings>(create: (_) => AppSettings()),
|
||||||
|
ChangeNotifierProvider<OfflineMode>(create: (_) => OfflineMode()),
|
||||||
|
ChangeNotifierProvider<BakaAuth>(create: (_) => auth),
|
||||||
|
ChangeNotifierProvider<YtDownloadService>(
|
||||||
|
create: (_) => YtDownloadService(auth: auth)),
|
||||||
|
ChangeNotifierProvider<YtSearchService>(
|
||||||
|
create: (_) => YtSearchService(auth: auth)),
|
||||||
|
ChangeNotifierProvider<DownloadService>(
|
||||||
|
create: (_) => DownloadService(db: db, navidrome: NavidromeService())),
|
||||||
|
ChangeNotifierProvider<SyncService>(
|
||||||
|
create: (_) => SyncService(
|
||||||
|
db: db, cloud: MeloCloudService(auth: auth))),
|
||||||
|
],
|
||||||
|
child: const MaterialApp(home: HomeShell()),
|
||||||
|
));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
// Auf "Favoriten" wechseln (Index 3) — kein Navigator.push, nur der Tab.
|
||||||
|
await tester.tap(find.descendant(
|
||||||
|
of: find.byType(BottomNavigationBar), matching: find.text('Favoriten')));
|
||||||
|
await tester.pump();
|
||||||
|
expect(
|
||||||
|
tester
|
||||||
|
.widget<BottomNavigationBar>(find.byType(BottomNavigationBar))
|
||||||
|
.currentIndex,
|
||||||
|
3);
|
||||||
|
|
||||||
|
// System-Zurück-Geste simulieren — derselbe Weg, über den auch das
|
||||||
|
// echte Android-Zurück-Gesten-Signal bei WidgetsApp ankommt.
|
||||||
|
final widgetsAppState = tester.state(find.byType(WidgetsApp));
|
||||||
|
// ignore: avoid_dynamic_calls
|
||||||
|
await (widgetsAppState as dynamic).didPopRoute();
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
// Ohne Abfangen bliebe der Tab unverändert (Flutter minimiert dann die
|
||||||
|
// App statt zu navigieren) — die Geste muss stattdessen zu Tab 0 führen.
|
||||||
|
expect(
|
||||||
|
tester
|
||||||
|
.widget<BottomNavigationBar>(find.byType(BottomNavigationBar))
|
||||||
|
.currentIndex,
|
||||||
|
0);
|
||||||
|
|
||||||
|
// Baum abbauen, bevor der Test endet: der Positions-Timer des Audio-
|
||||||
|
// Handlers läuft sonst über das Testende hinaus weiter, und der
|
||||||
|
// Test-Rahmen meldet einen offenen Timer (wie in hauptmenue_test.dart
|
||||||
|
// dokumentiert). Aus demselben Grund bewusst kein db.close() nach
|
||||||
|
// einem tester.tap.
|
||||||
|
await tester.pumpWidget(const SizedBox());
|
||||||
|
handler.dispose();
|
||||||
|
await tester.pump(const Duration(milliseconds: 1));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user