Skip to content

Commit f4595d5

Browse files
committed
feat: Implement playback controller buttons for the Zune view
1 parent a28a059 commit f4595d5

File tree

4 files changed

+136
-55
lines changed

4 files changed

+136
-55
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
import 'package:material_symbols_icons/symbols.dart';
12
import 'package:provider/provider.dart';
23
import 'package:fluent_ui/fluent_ui.dart';
4+
import 'package:rune/providers/playback_controller.dart';
5+
import 'package:rune/providers/volume.dart';
6+
import 'package:rune/widgets/playback_controller/constants/controller_items.dart';
37

48
import '../../../utils/ax_shadow.dart';
59
import '../../../utils/format_time.dart';
@@ -27,14 +31,17 @@ class SmallScreenPlayingTrack extends StatelessWidget {
2731

2832
final width = MediaQuery.of(context).size.width;
2933

34+
Provider.of<VolumeProvider>(context);
35+
3036
return Selector<PlaybackStatusProvider,
31-
(String?, String?, String?, String?, double?)>(
37+
(String?, String?, String?, String?, double?, String?)>(
3238
selector: (context, playbackStatusProvider) => (
3339
playbackStatusProvider.playbackStatus?.coverArtPath,
3440
playbackStatusProvider.playbackStatus?.artist,
3541
playbackStatusProvider.playbackStatus?.album,
3642
playbackStatusProvider.playbackStatus?.title,
3743
playbackStatusProvider.playbackStatus?.duration,
44+
playbackStatusProvider.playbackStatus?.state,
3845
),
3946
builder: (context, p, child) {
4047
if (p.$1 == null) return Container();
@@ -50,68 +57,142 @@ class SmallScreenPlayingTrack extends StatelessWidget {
5057
playbackControllerHeight + 12,
5158
),
5259
constraints: const BoxConstraints(maxWidth: 240),
53-
child: Column(
54-
crossAxisAlignment: CrossAxisAlignment.center,
55-
mainAxisAlignment: MainAxisAlignment.center,
56-
children: [
57-
SmallerOrEqualTo(
58-
breakpoint: DeviceType.zune,
59-
builder: (context, isSmaller) {
60-
if (isSmaller) return Container();
61-
return Container(
62-
padding: const EdgeInsets.symmetric(horizontal: 10),
63-
child: Container(
64-
decoration: BoxDecoration(
65-
border: Border.all(color: Colors.white, width: 4),
66-
boxShadow: axShadow(9),
67-
),
68-
child: AspectRatio(
69-
aspectRatio: 1,
70-
child: CoverArt(
71-
hint: (
72-
p.$3 ?? "",
73-
p.$2 ?? "",
74-
'Total Time ${formatTime(p.$5 ?? 0)}'
60+
child: SmallerOrEqualTo(
61+
breakpoint: DeviceType.zune,
62+
builder: (context, isZune) {
63+
return Column(
64+
crossAxisAlignment: CrossAxisAlignment.center,
65+
mainAxisAlignment: MainAxisAlignment.center,
66+
children: [
67+
if (!isZune)
68+
Container(
69+
padding: const EdgeInsets.symmetric(horizontal: 10),
70+
child: Container(
71+
decoration: BoxDecoration(
72+
border: Border.all(color: Colors.white, width: 4),
73+
boxShadow: axShadow(9),
74+
),
75+
child: AspectRatio(
76+
aspectRatio: 1,
77+
child: CoverArt(
78+
hint: (
79+
p.$3 ?? "",
80+
p.$2 ?? "",
81+
'Total Time ${formatTime(p.$5 ?? 0)}'
82+
),
83+
key: p.$1 != null ? Key(p.$1.toString()) : null,
84+
path: p.$1,
85+
size: (width - 20).clamp(0, 240),
7586
),
76-
key: p.$1 != null ? Key(p.$1.toString()) : null,
77-
path: p.$1,
78-
size: (width - 20).clamp(0, 240),
7987
),
8088
),
8189
),
82-
);
83-
},
84-
),
85-
SmallerOrEqualTo(
86-
breakpoint: DeviceType.zune,
87-
builder: (context, isSmaller) {
88-
if (isSmaller) return Container();
89-
90-
return Transform.translate(
90+
if (!isZune)
91+
Transform.translate(
9192
offset: const Offset(0, -16),
9293
child: SizedBox(
9394
height: 80,
9495
child: CoverArtPageProgressBar(shadows: shadows),
9596
),
96-
);
97-
}),
98-
const SizedBox(height: 8),
99-
Text(
100-
p.$4 ?? "Unknown Track",
101-
style: typography.subtitle?.apply(shadows: shadows),
102-
textAlign: TextAlign.center,
103-
),
104-
const SizedBox(height: 12),
105-
Text(
106-
'$artist · $album',
107-
style:
108-
typography.body?.apply(shadows: shadows, heightFactor: 2),
109-
textAlign: TextAlign.center,
110-
),
111-
],
97+
),
98+
const SizedBox(height: 8),
99+
Text(
100+
p.$4 ?? "Unknown Track",
101+
style: typography.subtitle?.apply(shadows: shadows),
102+
textAlign: TextAlign.center,
103+
),
104+
const SizedBox(height: 12),
105+
Text(
106+
'$artist · $album',
107+
style: typography.body
108+
?.apply(shadows: shadows, heightFactor: 2),
109+
textAlign: TextAlign.center,
110+
),
111+
if (isZune) const SizedBox(height: 12),
112+
if (isZune)
113+
Selector<PlaybackControllerProvider,
114+
(List<ControllerEntry>, List<ControllerEntry>)>(
115+
selector: (context, controllerProvider) {
116+
final entries = controllerProvider.entries;
117+
final hiddenIndex =
118+
entries.indexWhere((entry) => entry.id == 'hidden');
119+
final List<ControllerEntry> visibleEntries =
120+
hiddenIndex != -1
121+
? entries.sublist(0, hiddenIndex)
122+
: entries;
123+
final List<ControllerEntry> hiddenEntries =
124+
hiddenIndex != -1
125+
? entries.sublist(hiddenIndex + 1)
126+
: [];
127+
128+
return (visibleEntries, hiddenEntries);
129+
},
130+
builder: (context, entries, child) {
131+
return CommandBar(
132+
isCompact: true,
133+
overflowMenuItemBuilder: (context, entry) {
134+
if (entry is PrimaryCommandBarItem) {
135+
return entry.entry.flyoutEntryBuilder(context);
136+
}
137+
138+
throw "Unacceptable entry type";
139+
},
140+
overflowItemBuilder: (onPressed) {
141+
return OverflowCommandBarItem(
142+
key: const ValueKey("Overflow Item"),
143+
onPressed: onPressed,
144+
);
145+
},
146+
primaryItems: entries.$1
147+
.map(
148+
(x) => PrimaryCommandBarItem(
149+
key: ValueKey(x.id),
150+
entry: x,
151+
),
152+
)
153+
.toList(),
154+
secondaryItems: entries.$2
155+
.map(
156+
(x) => PrimaryCommandBarItem(
157+
key: ValueKey(x.id),
158+
entry: x,
159+
),
160+
)
161+
.toList(),
162+
);
163+
},
164+
)
165+
],
166+
);
167+
},
112168
),
113169
);
114170
},
115171
);
116172
}
117173
}
174+
175+
class PrimaryCommandBarItem extends CommandBarItem {
176+
PrimaryCommandBarItem({required super.key, required this.entry});
177+
178+
final ControllerEntry entry;
179+
180+
@override
181+
Widget build(BuildContext context, CommandBarItemDisplayMode displayMode) {
182+
return entry.controllerButtonBuilder(context);
183+
}
184+
}
185+
186+
class OverflowCommandBarItem extends CommandBarItem {
187+
OverflowCommandBarItem({required super.key, required this.onPressed});
188+
189+
final VoidCallback onPressed;
190+
191+
@override
192+
Widget build(BuildContext context, CommandBarItemDisplayMode displayMode) {
193+
return IconButton(
194+
icon: const Icon(Symbols.more_vert),
195+
onPressed: onPressed,
196+
);
197+
}
198+
}

lib/widgets/playback_controller/controller_buttons.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import 'package:provider/provider.dart';
22
import 'package:go_router/go_router.dart';
33
import 'package:fluent_ui/fluent_ui.dart';
44
import 'package:material_symbols_icons/symbols.dart';
5-
import 'package:rune/providers/status.dart';
65

76
import '../../widgets/playback_controller/constants/controller_items.dart';
7+
import '../../providers/status.dart';
88
import '../../providers/playback_controller.dart';
99
import '../../providers/responsive_providers.dart';
1010

pubspec.lock

+2-2
Original file line numberDiff line numberDiff line change
@@ -338,8 +338,8 @@ packages:
338338
dependency: "direct main"
339339
description:
340340
path: "."
341-
ref: "6f3e1695a43ceb5b7d9c14e7563a0d1eaf303814"
342-
resolved-ref: "6f3e1695a43ceb5b7d9c14e7563a0d1eaf303814"
341+
ref: "2186f815de5d6b461775938eb2484d9a3f4162ee"
342+
resolved-ref: "2186f815de5d6b461775938eb2484d9a3f4162ee"
343343
url: "https://github.com/Losses/fluent_ui.git"
344344
source: git
345345
version: "4.9.1"

pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ dependencies:
3939
fluent_ui:
4040
git:
4141
url: https://github.com/Losses/fluent_ui.git
42-
ref: 6f3e1695a43ceb5b7d9c14e7563a0d1eaf303814
42+
ref: 2186f815de5d6b461775938eb2484d9a3f4162ee
4343
flutter_acrylic: ^1.1.4
4444
system_theme: ^3.0.0
4545
go_router: ^14.2.1

0 commit comments

Comments
 (0)