mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
v2 Roadmap: Responsive navigation added
for three different breakpoints
This commit is contained in:
parent
a86b6bc40b
commit
5b389564c1
@ -1,26 +1,24 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:bitsdojo_window/bitsdojo_window.dart';
|
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/material.dart' hide Page;
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:oauth2/oauth2.dart' show AuthorizationException;
|
import 'package:oauth2/oauth2.dart' show AuthorizationException;
|
||||||
import 'package:spotify/spotify.dart' hide Image, Player, Search;
|
import 'package:spotify/spotify.dart' hide Image, Player, Search;
|
||||||
|
|
||||||
import 'package:spotube/components/Category/CategoryCard.dart';
|
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/Login.dart';
|
||||||
import 'package:spotube/components/Lyrics.dart';
|
import 'package:spotube/components/Lyrics.dart';
|
||||||
import 'package:spotube/components/Search/Search.dart';
|
import 'package:spotube/components/Search/Search.dart';
|
||||||
import 'package:spotube/components/Shared/PageWindowTitleBar.dart';
|
import 'package:spotube/components/Shared/PageWindowTitleBar.dart';
|
||||||
import 'package:spotube/components/Player/Player.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/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/helpers/oauth-login.dart';
|
||||||
import 'package:spotube/models/LocalStorageKeys.dart';
|
import 'package:spotube/models/LocalStorageKeys.dart';
|
||||||
import 'package:spotube/models/sideBarTiles.dart';
|
|
||||||
import 'package:spotube/provider/Auth.dart';
|
import 'package:spotube/provider/Auth.dart';
|
||||||
import 'package:spotube/provider/SpotifyDI.dart';
|
import 'package:spotube/provider/SpotifyDI.dart';
|
||||||
|
|
||||||
@ -135,10 +133,14 @@ class _HomeState extends ConsumerState<Home> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onSelectedIndexChanged(int index) => setState(() {
|
||||||
|
_selectedIndex = index;
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Auth auth = ref.watch(authProvider);
|
Auth auth = ref.watch(authProvider);
|
||||||
SpotifyApi spotify = ref.watch(spotifyProvider);
|
final width = MediaQuery.of(context).size.width;
|
||||||
if (!auth.isLoggedIn) {
|
if (!auth.isLoggedIn) {
|
||||||
return const Login();
|
return const Login();
|
||||||
}
|
}
|
||||||
@ -153,7 +155,12 @@ class _HomeState extends ConsumerState<Home> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 256),
|
constraints: BoxConstraints(
|
||||||
|
maxWidth: width > 400 && width <= 700
|
||||||
|
? 72
|
||||||
|
: width > 700
|
||||||
|
? 256
|
||||||
|
: 0),
|
||||||
color:
|
color:
|
||||||
Theme.of(context).navigationRailTheme.backgroundColor,
|
Theme.of(context).navigationRailTheme.backgroundColor,
|
||||||
child: MoveWindow(),
|
child: MoveWindow(),
|
||||||
@ -168,76 +175,9 @@ class _HomeState extends ConsumerState<Home> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
NavigationRail(
|
Sidebar(
|
||||||
destinations: sidebarTileList
|
|
||||||
.map((e) => NavigationRailDestination(
|
|
||||||
icon: Icon(e.icon),
|
|
||||||
label: Text(
|
|
||||||
e.title,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
.toList(),
|
|
||||||
selectedIndex: _selectedIndex,
|
selectedIndex: _selectedIndex,
|
||||||
onDestinationSelected: (value) => setState(() {
|
onSelectedIndexChanged: _onSelectedIndexChanged,
|
||||||
_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<User>(
|
|
||||||
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(),
|
|
||||||
));
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
// contents of the spotify
|
// contents of the spotify
|
||||||
if (_selectedIndex == 0)
|
if (_selectedIndex == 0)
|
||||||
@ -261,7 +201,11 @@ class _HomeState extends ConsumerState<Home> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
// player itself
|
// player itself
|
||||||
const Player()
|
const Player(),
|
||||||
|
SpotubeNavigationBar(
|
||||||
|
selectedIndex: _selectedIndex,
|
||||||
|
onSelectedIndexChanged: _onSelectedIndexChanged,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
120
lib/components/Home/Sidebar.dart
Normal file
120
lib/components/Home/Sidebar.dart
Normal file
@ -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<User>(
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
30
lib/components/Home/SpotubeNavigationBar.dart
Normal file
30
lib/components/Home/SpotubeNavigationBar.dart
Normal file
@ -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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@ void main() async {
|
|||||||
await hotKeyManager.unregisterAll();
|
await hotKeyManager.unregisterAll();
|
||||||
runApp(ProviderScope(child: MyApp()));
|
runApp(ProviderScope(child: MyApp()));
|
||||||
doWhenWindowReady(() {
|
doWhenWindowReady(() {
|
||||||
appWindow.minSize = const Size(900, 700);
|
appWindow.minSize = const Size(280, 700);
|
||||||
appWindow.size = const Size(900, 700);
|
appWindow.size = const Size(900, 700);
|
||||||
appWindow.alignment = Alignment.center;
|
appWindow.alignment = Alignment.center;
|
||||||
appWindow.maximize();
|
appWindow.maximize();
|
||||||
@ -88,6 +88,10 @@ class MyApp extends HookConsumerWidget {
|
|||||||
color: Colors.grey[850],
|
color: Colors.grey[850],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
navigationBarTheme: NavigationBarThemeData(
|
||||||
|
backgroundColor: Colors.blueGrey[50],
|
||||||
|
height: 55,
|
||||||
|
),
|
||||||
cardTheme: CardTheme(
|
cardTheme: CardTheme(
|
||||||
shape:
|
shape:
|
||||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
@ -122,6 +126,10 @@ class MyApp extends HookConsumerWidget {
|
|||||||
backgroundColor: Colors.blueGrey[800],
|
backgroundColor: Colors.blueGrey[800],
|
||||||
unselectedIconTheme: const IconThemeData(opacity: 1),
|
unselectedIconTheme: const IconThemeData(opacity: 1),
|
||||||
),
|
),
|
||||||
|
navigationBarTheme: NavigationBarThemeData(
|
||||||
|
backgroundColor: Colors.blueGrey[800],
|
||||||
|
height: 55,
|
||||||
|
),
|
||||||
cardTheme: CardTheme(
|
cardTheme: CardTheme(
|
||||||
shape:
|
shape:
|
||||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||||
|
Loading…
Reference in New Issue
Block a user