mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
feat: desktop mini player support
This commit is contained in:
parent
62ad86e88d
commit
471812d789
@ -3,6 +3,7 @@ import 'package:flutter/widgets.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:spotify/spotify.dart' hide Search;
|
import 'package:spotify/spotify.dart' hide Search;
|
||||||
import 'package:spotube/pages/home/home.dart';
|
import 'package:spotube/pages/home/home.dart';
|
||||||
|
import 'package:spotube/pages/lyrics/mini_lyrics.dart';
|
||||||
import 'package:spotube/pages/search/search.dart';
|
import 'package:spotube/pages/search/search.dart';
|
||||||
import 'package:spotube/pages/settings/blacklist.dart';
|
import 'package:spotube/pages/settings/blacklist.dart';
|
||||||
import 'package:spotube/pages/settings/about.dart';
|
import 'package:spotube/pages/settings/about.dart';
|
||||||
@ -31,42 +32,51 @@ final router = GoRouter(
|
|||||||
routes: [
|
routes: [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: "/",
|
path: "/",
|
||||||
pageBuilder: (context, state) => SpotubePage(child: const HomePage()),
|
pageBuilder: (context, state) => const SpotubePage(child: HomePage()),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: "/search",
|
path: "/search",
|
||||||
name: "Search",
|
name: "Search",
|
||||||
pageBuilder: (context, state) =>
|
pageBuilder: (context, state) =>
|
||||||
SpotubePage(child: const SearchPage()),
|
const SpotubePage(child: SearchPage()),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: "/library",
|
path: "/library",
|
||||||
name: "Library",
|
name: "Library",
|
||||||
pageBuilder: (context, state) =>
|
pageBuilder: (context, state) =>
|
||||||
SpotubePage(child: const LibraryPage()),
|
const SpotubePage(child: LibraryPage()),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: "/lyrics",
|
path: "/lyrics",
|
||||||
name: "Lyrics",
|
name: "Lyrics",
|
||||||
pageBuilder: (context, state) =>
|
pageBuilder: (context, state) =>
|
||||||
SpotubePage(child: const LyricsPage()),
|
const SpotubePage(child: LyricsPage()),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
path: "mini",
|
||||||
|
parentNavigatorKey: rootNavigatorKey,
|
||||||
|
pageBuilder: (context, state) => const SpotubePage(
|
||||||
|
child: MiniLyricsPage(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: "/settings",
|
path: "/settings",
|
||||||
pageBuilder: (context, state) => SpotubePage(
|
pageBuilder: (context, state) => const SpotubePage(
|
||||||
child: const SettingsPage(),
|
child: SettingsPage(),
|
||||||
),
|
),
|
||||||
routes: [
|
routes: [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: "blacklist",
|
path: "blacklist",
|
||||||
pageBuilder: (context, state) => SpotubePage(
|
pageBuilder: (context, state) => const SpotubePage(
|
||||||
child: const BlackListPage(),
|
child: BlackListPage(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: "about",
|
path: "about",
|
||||||
pageBuilder: (context, state) => SpotubePage(
|
pageBuilder: (context, state) => const SpotubePage(
|
||||||
child: const AboutSpotube(),
|
child: AboutSpotube(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -106,16 +116,16 @@ final router = GoRouter(
|
|||||||
GoRoute(
|
GoRoute(
|
||||||
path: "/login-tutorial",
|
path: "/login-tutorial",
|
||||||
parentNavigatorKey: rootNavigatorKey,
|
parentNavigatorKey: rootNavigatorKey,
|
||||||
pageBuilder: (context, state) => SpotubePage(
|
pageBuilder: (context, state) => const SpotubePage(
|
||||||
child: const LoginTutorial(),
|
child: LoginTutorial(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: "/player",
|
path: "/player",
|
||||||
parentNavigatorKey: rootNavigatorKey,
|
parentNavigatorKey: rootNavigatorKey,
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
return SpotubePage(
|
return const SpotubePage(
|
||||||
child: const PlayerView(),
|
child: PlayerView(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -68,4 +68,8 @@ abstract class SpotubeIcons {
|
|||||||
static const zoomIn = FeatherIcons.zoomIn;
|
static const zoomIn = FeatherIcons.zoomIn;
|
||||||
static const zoomOut = FeatherIcons.zoomOut;
|
static const zoomOut = FeatherIcons.zoomOut;
|
||||||
static const tray = FeatherIcons.chevronDown;
|
static const tray = FeatherIcons.chevronDown;
|
||||||
|
static const miniPlayer = Icons.picture_in_picture_rounded;
|
||||||
|
static const maximize = FeatherIcons.maximize2;
|
||||||
|
static const pinOn = Icons.push_pin_rounded;
|
||||||
|
static const pinOff = Icons.push_pin_outlined;
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ class PlayerActions extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final playlist = ref.watch(PlaylistQueueNotifier.provider);
|
final playlist = ref.watch(PlaylistQueueNotifier.provider);
|
||||||
final playlistNotifier = ref.watch(PlaylistQueueNotifier.provider.notifier);
|
|
||||||
final isLocalTrack = playlist?.activeTrack is LocalTrack;
|
final isLocalTrack = playlist?.activeTrack is LocalTrack;
|
||||||
final downloader = ref.watch(downloaderProvider);
|
final downloader = ref.watch(downloaderProvider);
|
||||||
final isInQueue = downloader.inQueue
|
final isInQueue = downloader.inQueue
|
||||||
|
@ -13,9 +13,11 @@ import 'package:spotube/utils/primitive_utils.dart';
|
|||||||
|
|
||||||
class PlayerControls extends HookConsumerWidget {
|
class PlayerControls extends HookConsumerWidget {
|
||||||
final PaletteGenerator? palette;
|
final PaletteGenerator? palette;
|
||||||
|
final bool compact;
|
||||||
|
|
||||||
PlayerControls({
|
PlayerControls({
|
||||||
this.palette,
|
this.palette,
|
||||||
|
this.compact = false,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -78,8 +80,8 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
backgroundColor: accentColor?.color ?? theme.colorScheme.primary,
|
backgroundColor: accentColor?.color ?? theme.colorScheme.primary,
|
||||||
foregroundColor:
|
foregroundColor:
|
||||||
accentColor?.titleTextColor ?? theme.colorScheme.onPrimary,
|
accentColor?.titleTextColor ?? theme.colorScheme.onPrimary,
|
||||||
padding: const EdgeInsets.all(12),
|
padding: EdgeInsets.all(compact ? 10 : 12),
|
||||||
iconSize: 24,
|
iconSize: compact ? 18 : 24,
|
||||||
);
|
);
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
@ -97,6 +99,7 @@ class PlayerControls extends HookConsumerWidget {
|
|||||||
constraints: const BoxConstraints(maxWidth: 600),
|
constraints: const BoxConstraints(maxWidth: 600),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
if (!compact)
|
||||||
HookBuilder(
|
HookBuilder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
final progressObj = useProgress(ref);
|
final progressObj = useProgress(ref);
|
||||||
|
@ -2,9 +2,11 @@ import 'dart:ui';
|
|||||||
|
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
|
||||||
import 'package:spotube/collections/assets.gen.dart';
|
import 'package:spotube/collections/assets.gen.dart';
|
||||||
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/player/player_actions.dart';
|
import 'package:spotube/components/player/player_actions.dart';
|
||||||
import 'package:spotube/components/player/player_overlay.dart';
|
import 'package:spotube/components/player/player_overlay.dart';
|
||||||
import 'package:spotube/components/player/player_track_details.dart';
|
import 'package:spotube/components/player/player_track_details.dart';
|
||||||
@ -123,7 +125,17 @@ class BottomPlayer extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
PlayerActions()
|
PlayerActions(
|
||||||
|
extraActions: [
|
||||||
|
IconButton(
|
||||||
|
tooltip: 'Mini Player',
|
||||||
|
icon: const Icon(SpotubeIcons.miniPlayer),
|
||||||
|
onPressed: () {
|
||||||
|
GoRouter.of(context).push('/lyrics/mini');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
88
lib/components/shared/bordered_text.dart
Normal file
88
lib/components/shared/bordered_text.dart
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
library bordered_text;
|
||||||
|
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
/// Adds stroke to text widget
|
||||||
|
/// We can apply a very thin and subtle stroke to a [Text]
|
||||||
|
/// ```dart
|
||||||
|
/// BorderedText(
|
||||||
|
/// strokeWidth: 1.0,
|
||||||
|
/// text: Text(
|
||||||
|
/// 'Bordered Text',
|
||||||
|
/// style: TextStyle(
|
||||||
|
/// decoration: TextDecoration.none,
|
||||||
|
/// decorationStyle: TextDecorationStyle.wavy,
|
||||||
|
/// decorationColor: Colors.red,
|
||||||
|
/// ),
|
||||||
|
/// ),
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
class BorderedText extends StatelessWidget {
|
||||||
|
const BorderedText({
|
||||||
|
super.key,
|
||||||
|
required this.child,
|
||||||
|
this.strokeCap = StrokeCap.round,
|
||||||
|
this.strokeJoin = StrokeJoin.round,
|
||||||
|
this.strokeWidth = 6.0,
|
||||||
|
this.strokeColor = const Color.fromRGBO(53, 0, 71, 1),
|
||||||
|
});
|
||||||
|
|
||||||
|
/// the stroke cap style
|
||||||
|
final StrokeCap strokeCap;
|
||||||
|
|
||||||
|
/// the stroke joint style
|
||||||
|
final StrokeJoin strokeJoin;
|
||||||
|
|
||||||
|
/// the stroke width
|
||||||
|
final double strokeWidth;
|
||||||
|
|
||||||
|
/// the stroke color
|
||||||
|
final Color strokeColor;
|
||||||
|
|
||||||
|
/// the [Text] widget to apply stroke on
|
||||||
|
final Text child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
TextStyle style;
|
||||||
|
if (child.style != null) {
|
||||||
|
style = child.style!.copyWith(
|
||||||
|
foreground: Paint()
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeCap = strokeCap
|
||||||
|
..strokeJoin = strokeJoin
|
||||||
|
..strokeWidth = strokeWidth
|
||||||
|
..color = strokeColor,
|
||||||
|
color: null,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
style = TextStyle(
|
||||||
|
foreground: Paint()
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeCap = strokeCap
|
||||||
|
..strokeJoin = strokeJoin
|
||||||
|
..strokeWidth = strokeWidth
|
||||||
|
..color = strokeColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
textDirection: child.textDirection,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
child.data!,
|
||||||
|
style: style,
|
||||||
|
maxLines: child.maxLines,
|
||||||
|
overflow: child.overflow,
|
||||||
|
semanticsLabel: child.semanticsLabel,
|
||||||
|
softWrap: child.softWrap,
|
||||||
|
strutStyle: child.strutStyle,
|
||||||
|
textAlign: child.textAlign,
|
||||||
|
textDirection: child.textDirection,
|
||||||
|
textScaleFactor: child.textScaleFactor,
|
||||||
|
),
|
||||||
|
child,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -26,8 +26,9 @@ import 'package:spotube/services/pocketbase.dart';
|
|||||||
import 'package:spotube/services/youtube.dart';
|
import 'package:spotube/services/youtube.dart';
|
||||||
import 'package:spotube/themes/theme.dart';
|
import 'package:spotube/themes/theme.dart';
|
||||||
import 'package:system_theme/system_theme.dart';
|
import 'package:system_theme/system_theme.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
import 'hooks/use_init_sys_tray.dart';
|
import 'package:spotube/hooks/use_init_sys_tray.dart';
|
||||||
|
|
||||||
Future<void> main(List<String> rawArgs) async {
|
Future<void> main(List<String> rawArgs) async {
|
||||||
final parser = ArgParser();
|
final parser = ArgParser();
|
||||||
@ -78,7 +79,10 @@ Future<void> main(List<String> rawArgs) async {
|
|||||||
|
|
||||||
await SystemTheme.accentColor.load();
|
await SystemTheme.accentColor.load();
|
||||||
MetadataGod.initialize();
|
MetadataGod.initialize();
|
||||||
await QueryClient.initialize(cachePrefix: "oss.krtirtho.spotube");
|
await QueryClient.initialize(
|
||||||
|
cachePrefix: "oss.krtirtho.spotube",
|
||||||
|
cacheDir: (await getApplicationSupportDirectory()).path,
|
||||||
|
);
|
||||||
Hive.registerAdapter(CacheTrackAdapter());
|
Hive.registerAdapter(CacheTrackAdapter());
|
||||||
Hive.registerAdapter(CacheTrackEngagementAdapter());
|
Hive.registerAdapter(CacheTrackEngagementAdapter());
|
||||||
Hive.registerAdapter(CacheTrackSkipSegmentAdapter());
|
Hive.registerAdapter(CacheTrackSkipSegmentAdapter());
|
||||||
|
177
lib/pages/lyrics/mini_lyrics.dart
Normal file
177
lib/pages/lyrics/mini_lyrics.dart
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:palette_generator/palette_generator.dart';
|
||||||
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
|
import 'package:spotube/components/player/player_controls.dart';
|
||||||
|
import 'package:spotube/components/player/player_queue.dart';
|
||||||
|
import 'package:spotube/components/root/sidebar.dart';
|
||||||
|
import 'package:spotube/components/shared/fallbacks/anonymous_fallback.dart';
|
||||||
|
import 'package:spotube/components/shared/page_window_title_bar.dart';
|
||||||
|
import 'package:spotube/hooks/use_force_update.dart';
|
||||||
|
import 'package:spotube/pages/lyrics/plain_lyrics.dart';
|
||||||
|
import 'package:spotube/pages/lyrics/synced_lyrics.dart';
|
||||||
|
import 'package:spotube/provider/authentication_provider.dart';
|
||||||
|
import 'package:spotube/provider/playlist_queue_provider.dart';
|
||||||
|
|
||||||
|
class MiniLyricsPage extends HookConsumerWidget {
|
||||||
|
const MiniLyricsPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, ref) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final update = useForceUpdate();
|
||||||
|
final prevSize = useRef<Size?>(null);
|
||||||
|
|
||||||
|
final playlistQueue = ref.watch(PlaylistQueueNotifier.provider);
|
||||||
|
|
||||||
|
useEffect(() {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
prevSize.value = await DesktopTools.window.getSize();
|
||||||
|
await DesktopTools.window.setMinimumSize(const Size(300, 300));
|
||||||
|
await DesktopTools.window.setAlwaysOnTop(true);
|
||||||
|
await DesktopTools.window.setSize(const Size(400, 500));
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
final auth = ref.watch(AuthenticationNotifier.provider);
|
||||||
|
|
||||||
|
if (auth == null) {
|
||||||
|
return const Scaffold(
|
||||||
|
appBar: PageWindowTitleBar(),
|
||||||
|
body: AnonymousFallback(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefaultTabController(
|
||||||
|
length: 2,
|
||||||
|
child: Scaffold(
|
||||||
|
backgroundColor: theme.colorScheme.surface.withOpacity(0.4),
|
||||||
|
appBar: PreferredSize(
|
||||||
|
preferredSize: const Size.fromHeight(60),
|
||||||
|
child: DragToMoveArea(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
SizedBox(
|
||||||
|
height: 30,
|
||||||
|
width: 30,
|
||||||
|
child: Sidebar.brandLogo(),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
const SizedBox(
|
||||||
|
height: 30,
|
||||||
|
child: TabBar(
|
||||||
|
tabs: [Tab(text: 'Synced'), Tab(text: 'Plain')],
|
||||||
|
isScrollable: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
FutureBuilder(
|
||||||
|
future: DesktopTools.window.isAlwaysOnTop(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
return IconButton(
|
||||||
|
tooltip: 'Always on top',
|
||||||
|
icon: Icon(
|
||||||
|
snapshot.data == true
|
||||||
|
? SpotubeIcons.pinOn
|
||||||
|
: SpotubeIcons.pinOff,
|
||||||
|
),
|
||||||
|
style: ButtonStyle(
|
||||||
|
foregroundColor: snapshot.data == true
|
||||||
|
? MaterialStateProperty.all(
|
||||||
|
theme.colorScheme.primary)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
onPressed: snapshot.data == null
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
await DesktopTools.window.setAlwaysOnTop(
|
||||||
|
snapshot.data == true ? false : true,
|
||||||
|
);
|
||||||
|
update();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
IconButton(
|
||||||
|
tooltip: 'Exit Mini Player',
|
||||||
|
icon: const Icon(SpotubeIcons.maximize),
|
||||||
|
onPressed: () async {
|
||||||
|
try {
|
||||||
|
await DesktopTools.window
|
||||||
|
.setMinimumSize(const Size(300, 700));
|
||||||
|
await DesktopTools.window.setAlwaysOnTop(false);
|
||||||
|
await DesktopTools.window.setSize(prevSize.value!);
|
||||||
|
await DesktopTools.window.setAlignment(Alignment.center);
|
||||||
|
} finally {
|
||||||
|
if (context.mounted) GoRouter.of(context).go('/');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
if (playlistQueue != null)
|
||||||
|
Text(
|
||||||
|
playlistQueue.activeTrack.name!,
|
||||||
|
style: theme.textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TabBarView(
|
||||||
|
children: [
|
||||||
|
SyncedLyrics(
|
||||||
|
palette: PaletteColor(theme.colorScheme.background, 0),
|
||||||
|
isModal: true,
|
||||||
|
defaultTextZoom: 65,
|
||||||
|
),
|
||||||
|
PlainLyrics(
|
||||||
|
palette: PaletteColor(theme.colorScheme.background, 0),
|
||||||
|
isModal: true,
|
||||||
|
defaultTextZoom: 65,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(SpotubeIcons.queue),
|
||||||
|
tooltip: 'Queue',
|
||||||
|
onPressed: playlistQueue != null
|
||||||
|
? () {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
isDismissible: true,
|
||||||
|
enableDrag: true,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: Colors.black12,
|
||||||
|
barrierColor: Colors.black12,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxHeight:
|
||||||
|
MediaQuery.of(context).size.height * .7,
|
||||||
|
),
|
||||||
|
builder: (context) {
|
||||||
|
return const PlayerQueue(floating: true);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
Flexible(child: PlayerControls(compact: true))
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -15,9 +15,11 @@ import 'package:spotube/utils/type_conversion_utils.dart';
|
|||||||
class PlainLyrics extends HookConsumerWidget {
|
class PlainLyrics extends HookConsumerWidget {
|
||||||
final PaletteColor palette;
|
final PaletteColor palette;
|
||||||
final bool? isModal;
|
final bool? isModal;
|
||||||
|
final int defaultTextZoom;
|
||||||
const PlainLyrics({
|
const PlainLyrics({
|
||||||
required this.palette,
|
required this.palette,
|
||||||
this.isModal,
|
this.isModal,
|
||||||
|
this.defaultTextZoom = 100,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -31,7 +33,7 @@ class PlainLyrics extends HookConsumerWidget {
|
|||||||
final breakpoint = useBreakpoints();
|
final breakpoint = useBreakpoints();
|
||||||
final textTheme = Theme.of(context).textTheme;
|
final textTheme = Theme.of(context).textTheme;
|
||||||
|
|
||||||
final textZoomLevel = useState<int>(100);
|
final textZoomLevel = useState<int>(defaultTextZoom);
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
|
@ -20,10 +20,12 @@ final _delay = StateProvider<int>((ref) => 0);
|
|||||||
class SyncedLyrics extends HookConsumerWidget {
|
class SyncedLyrics extends HookConsumerWidget {
|
||||||
final PaletteColor palette;
|
final PaletteColor palette;
|
||||||
final bool? isModal;
|
final bool? isModal;
|
||||||
|
final int defaultTextZoom;
|
||||||
|
|
||||||
const SyncedLyrics({
|
const SyncedLyrics({
|
||||||
required this.palette,
|
required this.palette,
|
||||||
this.isModal,
|
this.isModal,
|
||||||
|
this.defaultTextZoom = 100,
|
||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -51,7 +53,7 @@ class SyncedLyrics extends HookConsumerWidget {
|
|||||||
[lyricValue],
|
[lyricValue],
|
||||||
);
|
);
|
||||||
final currentTime = useSyncedLyrics(ref, lyricsMap, delay);
|
final currentTime = useSyncedLyrics(ref, lyricsMap, delay);
|
||||||
final textZoomLevel = useState<int>(100);
|
final textZoomLevel = useState<int>(defaultTextZoom);
|
||||||
|
|
||||||
final textTheme = Theme.of(context).textTheme;
|
final textTheme = Theme.of(context).textTheme;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user