v2 Roadmap: Responsive navigation added

for three different breakpoints
This commit is contained in:
Kingkor Roy Tirtho 2022-02-24 19:43:18 +06:00
parent a86b6bc40b
commit 5b389564c1
4 changed files with 180 additions and 78 deletions

View File

@ -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,
),
], ],
), ),
); );

View 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),
),
);
},
),
);
}
}

View 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,
);
}
}

View File

@ -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)),