@@ -30,101 +30,110 @@ import 'package:musify/services/settings_manager.dart';
3030import 'package:musify/widgets/mini_player.dart' ;
3131
3232class BottomNavigationPage extends StatefulWidget {
33- const BottomNavigationPage ({
34- required this .child,
35- this .isOfflineMode = false ,
36- super .key,
37- });
33+ const BottomNavigationPage ({required this .child, super .key});
3834
3935 final StatefulNavigationShell child;
40- final bool isOfflineMode;
4136
4237 @override
4338 State <BottomNavigationPage > createState () => _BottomNavigationPageState ();
4439}
4540
4641class _BottomNavigationPageState extends State <BottomNavigationPage > {
42+ bool ? _previousOfflineMode;
43+
4744 @override
4845 Widget build (BuildContext context) {
49- return LayoutBuilder (
50- builder: (context, constraints) {
51- final isLargeScreen = MediaQuery .of (context).size.width >= 600 ;
52- final items = _getNavigationItems;
46+ return ValueListenableBuilder <bool >(
47+ valueListenable: offlineMode,
48+ builder: (context, isOfflineMode, _) {
49+ // Handle offline mode changes
50+ if (_previousOfflineMode != null &&
51+ _previousOfflineMode != isOfflineMode) {
52+ SchedulerBinding .instance.addPostFrameCallback ((_) {
53+ _handleOfflineModeChange (isOfflineMode);
54+ });
55+ }
56+ _previousOfflineMode = isOfflineMode;
57+
58+ return LayoutBuilder (
59+ builder: (context, constraints) {
60+ final isLargeScreen = MediaQuery .of (context).size.width >= 600 ;
61+ final items = _getNavigationItems (isOfflineMode);
5362
54- return Scaffold (
55- body: SafeArea (
56- child: Row (
57- children: [
58- if (isLargeScreen)
59- NavigationRail (
60- labelType: NavigationRailLabelType .selected,
61- destinations:
62- items
63- .map (
64- (item) => NavigationRailDestination (
65- icon: Icon (item.icon),
66- selectedIcon: Icon (item.icon),
67- label: Text (item.label),
68- ),
69- )
70- .toList (),
71- selectedIndex: _getCurrentIndex,
72- onDestinationSelected: _onTabTapped,
73- ),
74- Expanded (
75- child: Column (
76- children: [
77- Expanded (child: widget.child),
78- StreamBuilder <MediaItem ?>(
79- stream: audioHandler.mediaItem.distinct ((prev, curr) {
80- if (prev == null || curr == null ) return false ;
81- return prev.id == curr.id &&
82- prev.title == curr.title &&
83- prev.artist == curr.artist &&
84- prev.artUri == curr.artUri;
85- }),
86- builder: (context, snapshot) {
87- final metadata = snapshot.data;
88- if (metadata == null ) {
89- return const SizedBox .shrink ();
90- }
91- return MiniPlayer (metadata: metadata);
92- },
63+ return Scaffold (
64+ body: SafeArea (
65+ child: Row (
66+ children: [
67+ if (isLargeScreen)
68+ NavigationRail (
69+ labelType: NavigationRailLabelType .selected,
70+ destinations:
71+ items
72+ .map (
73+ (item) => NavigationRailDestination (
74+ icon: Icon (item.icon),
75+ selectedIcon: Icon (item.selectedIcon),
76+ label: Text (item.label),
77+ ),
78+ )
79+ .toList (),
80+ selectedIndex: _getCurrentIndex (items, isOfflineMode),
81+ onDestinationSelected:
82+ (index) => _onTabTapped (index, items),
9383 ),
94- ],
95- ),
84+ Expanded (
85+ child: Column (
86+ children: [
87+ Expanded (child: widget.child),
88+ StreamBuilder <MediaItem ?>(
89+ stream: audioHandler.mediaItem.distinct (
90+ _mediaItemEquals,
91+ ),
92+ builder: (context, snapshot) {
93+ final metadata = snapshot.data;
94+ if (metadata == null ) {
95+ return const SizedBox .shrink ();
96+ }
97+ return MiniPlayer (metadata: metadata);
98+ },
99+ ),
100+ ],
101+ ),
102+ ),
103+ ],
96104 ),
97- ],
98- ),
99- ),
100- bottomNavigationBar:
101- ! isLargeScreen
102- ? NavigationBar (
103- selectedIndex: _getCurrentIndex,
104- labelBehavior:
105- languageSetting == const Locale ('en' , '' )
106- ? NavigationDestinationLabelBehavior
107- .onlyShowSelected
108- : NavigationDestinationLabelBehavior .alwaysHide,
109- onDestinationSelected: _onTabTapped,
110- destinations:
111- items
112- .map (
113- (item) => NavigationDestination (
114- icon: Icon (item.icon),
115- selectedIcon: Icon (item.icon),
116- label: item.label,
117- ),
118- )
119- .toList (),
120- )
121- : null ,
105+ ),
106+ bottomNavigationBar:
107+ ! isLargeScreen
108+ ? NavigationBar (
109+ selectedIndex: _getCurrentIndex (items, isOfflineMode),
110+ labelBehavior:
111+ languageSetting == const Locale ('en' , '' )
112+ ? NavigationDestinationLabelBehavior
113+ .onlyShowSelected
114+ : NavigationDestinationLabelBehavior .alwaysHide,
115+ onDestinationSelected:
116+ (index) => _onTabTapped (index, items),
117+ destinations:
118+ items
119+ .map (
120+ (item) => NavigationDestination (
121+ icon: Icon (item.icon),
122+ selectedIcon: Icon (item.selectedIcon),
123+ label: item.label,
124+ ),
125+ )
126+ .toList (),
127+ )
128+ : null ,
129+ );
130+ },
122131 );
123132 },
124133 );
125134 }
126135
127- List <_NavigationItem > get _getNavigationItems {
136+ List <_NavigationItem > _getNavigationItems ( bool isOfflineMode) {
128137 final items = < _NavigationItem > [
129138 _NavigationItem (
130139 icon: FluentIcons .home_24_regular,
@@ -136,7 +145,7 @@ class _BottomNavigationPageState extends State<BottomNavigationPage> {
136145 ];
137146
138147 // Only add search tab in online mode
139- if (! widget. isOfflineMode) {
148+ if (! isOfflineMode) {
140149 items.add (
141150 _NavigationItem (
142151 icon: FluentIcons .search_24_regular,
@@ -149,8 +158,8 @@ class _BottomNavigationPageState extends State<BottomNavigationPage> {
149158 }
150159
151160 // Adjust indices based on whether search is included
152- final libraryIndex = widget. isOfflineMode ? 1 : 2 ;
153- final settingsIndex = widget. isOfflineMode ? 2 : 3 ;
161+ final libraryIndex = isOfflineMode ? 1 : 2 ;
162+ final settingsIndex = isOfflineMode ? 2 : 3 ;
154163
155164 items.addAll ([
156165 _NavigationItem (
@@ -172,41 +181,27 @@ class _BottomNavigationPageState extends State<BottomNavigationPage> {
172181 return items;
173182 }
174183
175- @override
176- void didUpdateWidget (BottomNavigationPage oldWidget) {
177- super .didUpdateWidget (oldWidget);
184+ void _handleOfflineModeChange (bool isOfflineMode) {
185+ if (! mounted) return ;
178186
179- // Handle offline mode transition
180- if (oldWidget.isOfflineMode != widget.isOfflineMode) {
181- _handleOfflineModeChange ();
182- }
183- }
184-
185- void _handleOfflineModeChange () {
186187 final currentRoute = GoRouterState .of (context).matchedLocation;
187188
188189 // If we're switching to offline mode and currently on search tab
189- if (widget. isOfflineMode && currentRoute.startsWith ('/search' )) {
190+ if (isOfflineMode && currentRoute.startsWith ('/search' )) {
190191 // Navigate to home
191- SchedulerBinding .instance.addPostFrameCallback ((_) {
192- if (mounted) {
193- widget.child.goBranch (0 );
194- }
195- });
192+ widget.child.goBranch (0 );
196193 }
197194 }
198195
199- void _onTabTapped (int index) {
200- final items = _getNavigationItems;
196+ void _onTabTapped (int index, List <_NavigationItem > items) {
201197 if (index < items.length) {
202198 final item = items[index];
203199 // Use the shell navigation index instead of the route
204200 widget.child.goBranch (item.shellIndex);
205201 }
206202 }
207203
208- int get _getCurrentIndex {
209- final items = _getNavigationItems;
204+ int _getCurrentIndex (List <_NavigationItem > items, bool isOfflineMode) {
210205 final currentIndex = widget.child.currentIndex;
211206
212207 // Add bounds checking
@@ -222,16 +217,26 @@ class _BottomNavigationPageState extends State<BottomNavigationPage> {
222217 }
223218
224219 // Handle edge cases more robustly
225- if (widget. isOfflineMode && currentIndex == 1 ) {
220+ if (isOfflineMode && currentIndex == 1 ) {
226221 return 0 ; // Search -> Home
227222 }
228223
229- if (widget. isOfflineMode && currentIndex > 1 ) {
224+ if (isOfflineMode && currentIndex > 1 ) {
230225 return (currentIndex - 1 ).clamp (0 , items.length - 1 );
231226 }
232227
233228 return currentIndex.clamp (0 , items.length - 1 );
234229 }
230+
231+ static bool _mediaItemEquals (MediaItem ? prev, MediaItem ? curr) {
232+ if (prev == curr) return true ;
233+ if (prev == null || curr == null ) return false ;
234+
235+ return prev.id == curr.id &&
236+ prev.title == curr.title &&
237+ prev.artist == curr.artist &&
238+ prev.artUri == curr.artUri;
239+ }
235240}
236241
237242class _NavigationItem {
0 commit comments