diff --git a/lib/components/Home.dart b/lib/components/Home.dart index 2d6cfa97..1d0339d5 100644 --- a/lib/components/Home.dart +++ b/lib/components/Home.dart @@ -1,26 +1,24 @@ import 'dart:io'; import 'package:bitsdojo_window/bitsdojo_window.dart'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart' hide Page; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:oauth2/oauth2.dart' show AuthorizationException; import 'package:spotify/spotify.dart' hide Image, Player, Search; + import 'package:spotube/components/Category/CategoryCard.dart'; +import 'package:spotube/components/Home/Sidebar.dart'; +import 'package:spotube/components/Home/SpotubeNavigationBar.dart'; import 'package:spotube/components/Login.dart'; import 'package:spotube/components/Lyrics.dart'; import 'package:spotube/components/Search/Search.dart'; import 'package:spotube/components/Shared/PageWindowTitleBar.dart'; import 'package:spotube/components/Player/Player.dart'; -import 'package:spotube/components/Settings.dart'; import 'package:spotube/components/Library/UserLibrary.dart'; -import 'package:spotube/components/Shared/SpotubePageRoute.dart'; -import 'package:spotube/helpers/image-to-url-string.dart'; import 'package:spotube/helpers/oauth-login.dart'; import 'package:spotube/models/LocalStorageKeys.dart'; -import 'package:spotube/models/sideBarTiles.dart'; import 'package:spotube/provider/Auth.dart'; import 'package:spotube/provider/SpotifyDI.dart'; @@ -135,10 +133,14 @@ class _HomeState extends ConsumerState { super.dispose(); } + _onSelectedIndexChanged(int index) => setState(() { + _selectedIndex = index; + }); + @override Widget build(BuildContext context) { Auth auth = ref.watch(authProvider); - SpotifyApi spotify = ref.watch(spotifyProvider); + final width = MediaQuery.of(context).size.width; if (!auth.isLoggedIn) { return const Login(); } @@ -153,7 +155,12 @@ class _HomeState extends ConsumerState { child: Row( children: [ Container( - constraints: const BoxConstraints(maxWidth: 256), + constraints: BoxConstraints( + maxWidth: width > 400 && width <= 700 + ? 72 + : width > 700 + ? 256 + : 0), color: Theme.of(context).navigationRailTheme.backgroundColor, child: MoveWindow(), @@ -168,76 +175,9 @@ class _HomeState extends ConsumerState { Expanded( child: Row( children: [ - NavigationRail( - destinations: sidebarTileList - .map((e) => NavigationRailDestination( - icon: Icon(e.icon), - label: Text( - e.title, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - )) - .toList(), + Sidebar( selectedIndex: _selectedIndex, - onDestinationSelected: (value) => setState(() { - _selectedIndex = value; - }), - extended: true, - leading: Padding( - padding: const EdgeInsets.only(left: 15), - child: Row(children: [ - Image.asset( - "assets/spotube-logo.png", - height: 50, - width: 50, - ), - const SizedBox( - width: 10, - ), - Text("Spotube", - style: Theme.of(context).textTheme.headline4), - ]), - ), - trailing: FutureBuilder( - future: spotify.me.get(), - builder: (context, snapshot) { - var avatarImg = imageToUrlString(snapshot.data?.images, - index: (snapshot.data?.images?.length ?? 1) - 1); - return Padding( - padding: const EdgeInsets.all(16), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - CircleAvatar( - backgroundImage: - CachedNetworkImageProvider(avatarImg), - ), - const SizedBox(width: 10), - Text( - snapshot.data?.displayName ?? "User's name", - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - ], - ), - IconButton( - icon: const Icon(Icons.settings_outlined), - onPressed: () { - Navigator.of(context).push(SpotubePageRoute( - child: const Settings(), - )); - }), - ], - ), - ); - }, - ), + onSelectedIndexChanged: _onSelectedIndexChanged, ), // contents of the spotify if (_selectedIndex == 0) @@ -261,7 +201,11 @@ class _HomeState extends ConsumerState { ), ), // player itself - const Player() + const Player(), + SpotubeNavigationBar( + selectedIndex: _selectedIndex, + onSelectedIndexChanged: _onSelectedIndexChanged, + ), ], ), ); diff --git a/lib/components/Home/Sidebar.dart b/lib/components/Home/Sidebar.dart new file mode 100644 index 00000000..f7b5be46 --- /dev/null +++ b/lib/components/Home/Sidebar.dart @@ -0,0 +1,120 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:flutter/material.dart'; +import 'package:spotify/spotify.dart' hide Image; +import 'package:spotube/components/Settings.dart'; +import 'package:spotube/components/Shared/SpotubePageRoute.dart'; +import 'package:spotube/helpers/image-to-url-string.dart'; +import 'package:spotube/provider/SpotifyDI.dart'; + +import '../../models/sideBarTiles.dart'; + +class Sidebar extends HookConsumerWidget { + final int selectedIndex; + final void Function(int) onSelectedIndexChanged; + + const Sidebar({ + required this.selectedIndex, + required this.onSelectedIndexChanged, + Key? key, + }) : super(key: key); + + Widget _buildSmallLogo() { + return Image.asset( + "assets/spotube-logo.png", + height: 50, + width: 50, + ); + } + + void _goToSettings(BuildContext context) { + Navigator.of(context).push(SpotubePageRoute( + child: const Settings(), + )); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final width = MediaQuery.of(context).size.width; + if (width <= 400) return Container(); + final extended = useState(false); + final SpotifyApi spotify = ref.watch(spotifyProvider); + useEffect(() { + if (width <= 700 && extended.value) { + extended.value = false; + } else if (width > 700 && !extended.value) { + extended.value = true; + } + }); + + return NavigationRail( + destinations: sidebarTileList + .map((e) => NavigationRailDestination( + icon: Icon(e.icon), + label: Text( + e.title, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + )) + .toList(), + selectedIndex: selectedIndex, + onDestinationSelected: onSelectedIndexChanged, + extended: extended.value, + leading: extended.value + ? Padding( + padding: const EdgeInsets.only(left: 15), + child: Row(children: [ + _buildSmallLogo(), + const SizedBox( + width: 10, + ), + Text("Spotube", style: Theme.of(context).textTheme.headline4), + ]), + ) + : _buildSmallLogo(), + trailing: FutureBuilder( + future: spotify.me.get(), + builder: (context, snapshot) { + var avatarImg = imageToUrlString(snapshot.data?.images, + index: (snapshot.data?.images?.length ?? 1) - 1); + return extended.value + ? Padding( + padding: const EdgeInsets.all(16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + CircleAvatar( + backgroundImage: + CachedNetworkImageProvider(avatarImg), + ), + const SizedBox(width: 10), + Text( + snapshot.data?.displayName ?? "User's name", + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ], + ), + IconButton( + icon: const Icon(Icons.settings_outlined), + onPressed: () => _goToSettings(context)), + ], + )) + : InkWell( + onTap: () => _goToSettings(context), + child: CircleAvatar( + backgroundImage: CachedNetworkImageProvider(avatarImg), + ), + ); + }, + ), + ); + } +} diff --git a/lib/components/Home/SpotubeNavigationBar.dart b/lib/components/Home/SpotubeNavigationBar.dart new file mode 100644 index 00000000..f57383ad --- /dev/null +++ b/lib/components/Home/SpotubeNavigationBar.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:spotube/models/sideBarTiles.dart'; + +class SpotubeNavigationBar extends HookWidget { + final int selectedIndex; + final void Function(int) onSelectedIndexChanged; + + const SpotubeNavigationBar({ + required this.selectedIndex, + required this.onSelectedIndexChanged, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final width = MediaQuery.of(context).size.width; + + if (width > 400) return Container(); + return NavigationBar( + destinations: sidebarTileList + .map( + (e) => NavigationDestination(icon: Icon(e.icon), label: e.title), + ) + .toList(), + selectedIndex: selectedIndex, + onDestinationSelected: onSelectedIndexChanged, + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index c3f73d3f..cd81052a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -13,7 +13,7 @@ void main() async { await hotKeyManager.unregisterAll(); runApp(ProviderScope(child: MyApp())); doWhenWindowReady(() { - appWindow.minSize = const Size(900, 700); + appWindow.minSize = const Size(280, 700); appWindow.size = const Size(900, 700); appWindow.alignment = Alignment.center; appWindow.maximize(); @@ -88,6 +88,10 @@ class MyApp extends HookConsumerWidget { color: Colors.grey[850], ), ), + navigationBarTheme: NavigationBarThemeData( + backgroundColor: Colors.blueGrey[50], + height: 55, + ), cardTheme: CardTheme( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), @@ -122,6 +126,10 @@ class MyApp extends HookConsumerWidget { backgroundColor: Colors.blueGrey[800], unselectedIconTheme: const IconThemeData(opacity: 1), ), + navigationBarTheme: NavigationBarThemeData( + backgroundColor: Colors.blueGrey[800], + height: 55, + ), cardTheme: CardTheme( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),