feat: new sidebar widget and translucent bottom player

This commit is contained in:
Kingkor Roy Tirtho 2023-03-10 20:51:44 +06:00
parent a90261ed19
commit 4ba1e70636
5 changed files with 230 additions and 95 deletions

View File

@ -1,3 +1,5 @@
import 'dart:ui';
import 'package:flutter/gestures.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -8,6 +10,7 @@ import 'package:spotube/components/player/player_overlay.dart';
import 'package:spotube/components/player/player_track_details.dart';
import 'package:spotube/components/player/player_controls.dart';
import 'package:spotube/hooks/use_breakpoints.dart';
import 'package:spotube/hooks/use_brightness_value.dart';
import 'package:spotube/models/logger.dart';
import 'package:flutter/material.dart';
import 'package:spotube/provider/playlist_queue_provider.dart';
@ -37,6 +40,13 @@ class BottomPlayer extends HookConsumerWidget {
[playlist?.activeTrack.album?.images],
);
final bg = Theme.of(context).colorScheme.surfaceVariant;
final bgColor = useBrightnessValue(
Color.lerp(bg, Colors.white, 0.7),
Color.lerp(bg, Colors.black, 0.45)!,
);
// returning an empty non spacious Container as the overlay will take
// place in the global overlay stack aka [_entries]
if (layoutMode == LayoutMode.compact ||
@ -45,10 +55,11 @@ class BottomPlayer extends HookConsumerWidget {
return PlayerOverlay(albumArt: albumArt);
}
return DecoratedBox(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.background,
),
return ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15),
child: DecoratedBox(
decoration: BoxDecoration(color: bgColor?.withOpacity(0.8)),
child: Material(
type: MaterialType.transparency,
textStyle: Theme.of(context).textTheme.bodyMedium!,
@ -72,7 +83,8 @@ class BottomPlayer extends HookConsumerWidget {
height: 20,
constraints: const BoxConstraints(maxWidth: 200),
child: HookBuilder(builder: (context) {
final volumeState = ref.watch(VolumeProvider.provider);
final volumeState =
ref.watch(VolumeProvider.provider);
final volumeNotifier =
ref.watch(VolumeProvider.provider.notifier);
final volume = useState(volumeState);
@ -89,10 +101,12 @@ class BottomPlayer extends HookConsumerWidget {
if (event is PointerScrollEvent) {
if (event.scrollDelta.dy > 0) {
final value = volume.value - .2;
volumeNotifier.setVolume(value < 0 ? 0 : value);
volumeNotifier
.setVolume(value < 0 ? 0 : value);
} else {
final value = volume.value + .2;
volumeNotifier.setVolume(value > 1 ? 1 : value);
volumeNotifier
.setVolume(value > 1 ? 1 : value);
}
}
},
@ -115,6 +129,8 @@ class BottomPlayer extends HookConsumerWidget {
],
),
),
),
),
);
}
}

View File

@ -2,12 +2,15 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:flutter/material.dart';
import 'package:sidebarx/sidebarx.dart';
import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/collections/side_bar_tiles.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/hooks/use_breakpoints.dart';
import 'package:spotube/hooks/use_brightness_value.dart';
import 'package:spotube/hooks/use_sidebarx_controller.dart';
import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/provider/downloader_provider.dart';
@ -55,6 +58,34 @@ class Sidebar extends HookConsumerWidget {
final layoutMode =
ref.watch(userPreferencesProvider.select((s) => s.layoutMode));
final controller = useSidebarXController(
selectedIndex: selectedIndex,
extended: breakpoints > Breakpoints.md,
);
final bg = Theme.of(context).colorScheme.surfaceVariant;
final bgColor = useBrightnessValue(
Color.lerp(bg, Colors.white, 0.7),
Color.lerp(bg, Colors.black, 0.45)!,
);
useEffect(() {
controller.addListener(() {
onSelectedIndexChanged(controller.selectedIndex);
});
return null;
}, [controller]);
useEffect(() {
if (breakpoints > Breakpoints.md && !controller.extended) {
controller.setExtended(true);
} else if (breakpoints <= Breakpoints.md && controller.extended) {
controller.setExtended(false);
}
return null;
}, [breakpoints, controller]);
if (layoutMode == LayoutMode.compact ||
(breakpoints.isSm && layoutMode == LayoutMode.adaptive)) {
return Scaffold(body: child);
@ -62,41 +93,61 @@ class Sidebar extends HookConsumerWidget {
return Row(
children: [
NavigationRail(
selectedIndex: selectedIndex,
onDestinationSelected: onSelectedIndexChanged,
destinations: sidebarTileList.map(
SidebarX(
controller: controller,
items: sidebarTileList.map(
(e) {
return NavigationRailDestination(
icon: Badge(
backgroundColor: Theme.of(context).primaryColor,
isLabelVisible: e.title == "Library" && downloadCount > 0,
label: Text(
downloadCount.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 10,
),
),
child: Icon(e.icon),
),
label: Text(
e.title,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
return SidebarXItem(
// iconWidget: Badge(
// backgroundColor: Theme.of(context).primaryColor,
// isLabelVisible: e.title == "Library" && downloadCount > 0,
// label: Text(
// downloadCount.toString(),
// style: const TextStyle(
// color: Colors.white,
// fontSize: 10,
// ),
// ),
// child: Icon(e.icon),
// ),
icon: e.icon,
label: e.title,
);
},
).toList(),
extended: breakpoints > Breakpoints.md,
leading: const SidebarHeader(),
trailing: const Expanded(
child: Align(
alignment: Alignment.bottomCenter,
headerBuilder: (_, __) => const SidebarHeader(),
footerBuilder: (_, __) => const Padding(
padding: EdgeInsets.only(bottom: 5),
child: SidebarFooter(),
),
showToggleButton: false,
theme: SidebarXTheme(
width: 60,
margin: const EdgeInsets.all(10).copyWith(bottom: 122),
),
extendedTheme: SidebarXTheme(
width: 250,
margin: const EdgeInsets.all(10).copyWith(bottom: 122, left: 0),
padding: const EdgeInsets.symmetric(horizontal: 6),
decoration: BoxDecoration(
color: bgColor?.withOpacity(0.8),
borderRadius: const BorderRadius.only(
topRight: Radius.circular(10),
bottomRight: Radius.circular(10),
)),
selectedItemDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
),
selectedIconTheme: IconThemeData(
color: Theme.of(context).colorScheme.primary,
),
selectedTextStyle: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.w600,
),
itemTextPadding: const EdgeInsets.only(left: 10),
selectedItemTextPadding: const EdgeInsets.only(left: 10),
),
),
Expanded(child: child)

View File

@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:sidebarx/sidebarx.dart';
/// Creates [SidebarXController] that will be disposed automatically.
///
/// See also:
/// - [SidebarXController]
SidebarXController useSidebarXController({
required int selectedIndex,
bool? extended,
List<Object?>? keys,
}) {
return use(
_SidebarXControllerHook(
selectedIndex: selectedIndex,
extended: extended,
keys: keys,
),
);
}
class _SidebarXControllerHook extends Hook<SidebarXController> {
const _SidebarXControllerHook({
required this.selectedIndex,
this.extended,
List<Object?>? keys,
}) : super(keys: keys);
final int selectedIndex;
final bool? extended;
@override
HookState<SidebarXController, Hook<SidebarXController>> createState() =>
_SidebarXControllerHookState();
}
class _SidebarXControllerHookState
extends HookState<SidebarXController, _SidebarXControllerHook> {
late final SidebarXController controller;
@override
void initHook() {
super.initHook();
controller = SidebarXController(
selectedIndex: hook.selectedIndex,
extended: hook.extended,
);
}
@override
SidebarXController build(BuildContext context) => controller;
@override
void dispose() => controller.dispose();
@override
String get debugLabel => 'useSidebarXController';
}

View File

@ -1393,6 +1393,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.3"
sidebarx:
dependency: "direct main"
description:
name: sidebarx
sha256: "26a8392ceddb659c8f2c688beba6c04bcbf520b4d5decb143c5fd7253653081f"
url: "https://pub.dev"
source: hosted
version: "0.15.0"
simple_circular_progress_bar:
dependency: "direct main"
description:

View File

@ -64,6 +64,7 @@ dependencies:
queue: ^3.1.0+1
scroll_to_index: ^3.0.1
shared_preferences: ^2.0.11
sidebarx: ^0.15.0
simple_circular_progress_bar: ^1.0.2
skeleton_text: ^3.0.0
spotify: