Skip to content

Commit dd59b7f

Browse files
authored
fix: organize player code and add a different video player for linux to fix video crash (#1371)
* chore: organize player code and test linux video player without top controls * chore: update media_kit * chore: use material video player for linux * chore: use custom controls for linux * chore: add more overlays for linux video player * chore: padding
1 parent b210365 commit dd59b7f

File tree

6 files changed

+305
-184
lines changed

6 files changed

+305
-184
lines changed

lib/player/view/full_height_player.dart

Lines changed: 26 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,15 @@
11
import 'package:flutter/material.dart';
22
import 'package:watch_it/watch_it.dart';
33

4-
import '../../app/app_model.dart';
54
import '../../app/connectivity_model.dart';
6-
import '../../common/data/audio_type.dart';
7-
import '../../common/view/header_bar.dart';
8-
import '../../common/view/ui_constants.dart';
95
import '../../extensions/build_context_x.dart';
106
import '../../extensions/taget_platform_x.dart';
117
import '../../player/player_model.dart';
12-
import '../../radio/view/radio_history_list.dart';
13-
import 'full_height_player_image.dart';
14-
import 'full_height_player_top_controls.dart';
8+
import 'full_height_player_audio_body.dart';
9+
import 'full_height_player_header_bar.dart';
1510
import 'full_height_video_player.dart';
1611
import 'player_color.dart';
17-
import 'player_main_controls.dart';
18-
import 'player_title_and_artist.dart';
19-
import 'player_track.dart';
2012
import 'player_view.dart';
21-
import 'queue/queue_body.dart';
22-
import 'queue/queue_button.dart';
2313

2414
class FullHeightPlayer extends StatelessWidget with WatchItMixin {
2515
const FullHeightPlayer({super.key, required this.playerPosition});
@@ -35,139 +25,40 @@ class FullHeightPlayer extends StatelessWidget with WatchItMixin {
3525
final isVideo = watchPropertyValue((PlayerModel m) => m.isVideo == true);
3626
final active = audio?.path != null || isOnline;
3727
final iconColor = isVideo ? Colors.white : theme.colorScheme.onSurface;
38-
final showQueue = watchPropertyValue((AppModel m) => m.showQueueOverlay);
39-
final playerWithSidePanel =
40-
playerPosition == PlayerPosition.fullWindow &&
41-
context.mediaQuerySize.width > 1000;
4228

4329
final Widget body;
4430
if (isVideo) {
45-
body = Stack(
46-
alignment: Alignment.topRight,
47-
children: [
48-
const SimpleFullHeightVideoPlayer(),
49-
FullHeightPlayerTopControls(
50-
iconColor: iconColor,
51-
playerPosition: playerPosition,
52-
),
53-
],
54-
);
55-
} else {
56-
final queueOrHistory = audio?.audioType == AudioType.radio
57-
? const SizedBox(
58-
width: 400,
59-
height: 500,
60-
child: RadioHistoryList(simpleList: true),
61-
)
62-
: QueueBody(selectedColor: theme.colorScheme.onSurface);
63-
final column = Column(
64-
mainAxisAlignment: MainAxisAlignment.center,
65-
mainAxisSize: MainAxisSize.min,
66-
children: [
67-
if (showQueue && !playerWithSidePanel)
68-
Padding(
69-
padding: const EdgeInsets.only(
70-
bottom: 2 * kLargestSpace,
71-
top: kLargestSpace,
72-
),
73-
child: queueOrHistory,
74-
)
75-
else ...[
76-
if (!isMobile || context.isPortrait)
77-
const Hero(
78-
tag: 'FullHeightPlayerImageInPortrait',
79-
child: FullHeightPlayerImage(showAudioVisualizer: true),
80-
),
81-
const SizedBox(height: kLargestSpace),
82-
Padding(
83-
padding: const EdgeInsets.symmetric(horizontal: 30),
84-
child: PlayerTitleAndArtist(playerPosition: playerPosition),
85-
),
86-
const SizedBox(height: kLargestSpace),
87-
SizedBox(
88-
height: kLargestSpace,
89-
width: playerWithSidePanel ? 400 : 350,
90-
child: const PlayerTrack(),
91-
),
92-
const SizedBox(height: kLargestSpace),
93-
],
94-
SizedBox(
95-
width: playerWithSidePanel ? 400 : 320,
96-
child: PlayerMainControls(active: active),
97-
),
98-
],
99-
);
100-
101-
body = Stack(
102-
alignment: Alignment.center,
103-
children: [
104-
Center(
105-
child: playerWithSidePanel
106-
? Row(
107-
mainAxisSize: MainAxisSize.min,
108-
mainAxisAlignment: MainAxisAlignment.center,
109-
children: [
110-
SizedBox(width: 490, child: column),
111-
queueOrHistory,
112-
],
113-
)
114-
: isMobile && !context.isPortrait
115-
? Row(
116-
spacing: kLargestSpace,
117-
mainAxisAlignment: MainAxisAlignment.center,
118-
mainAxisSize: MainAxisSize.min,
119-
children: [
120-
const Hero(
121-
tag: 'FullHeightPlayerImageInLandscape',
122-
child: FullHeightPlayerImage(height: 200, width: 200),
123-
),
124-
SizedBox(width: 400, child: column),
125-
],
126-
)
127-
: column,
128-
),
129-
Positioned(
130-
top: 0,
131-
right: 0,
132-
child: FullHeightPlayerTopControls(
31+
body = isLinux
32+
// Note: for some reason the video widget crashes if we use the built in controls, so we replicate this with a stack
33+
? LinuxFullHeightPlayer(
13334
iconColor: iconColor,
35+
active: active,
13436
playerPosition: playerPosition,
135-
showQueueButton: !playerWithSidePanel,
136-
),
137-
),
138-
if (isMobile)
139-
const Positioned(
140-
bottom: 2 * kLargestSpace,
141-
child: QueueButton.text(),
142-
),
143-
],
37+
)
38+
: FullHeightVideoPlayer(
39+
playerPosition: playerPosition,
40+
audio: audio,
41+
controlsActive: active,
42+
);
43+
} else {
44+
body = FullHeightPlayerAudioBody(
45+
active: active,
46+
iconColor: iconColor,
47+
playerPosition: playerPosition,
48+
audio: audio,
14449
);
14550
}
14651

147-
final headerBar = HeaderBar(
148-
adaptive: false,
149-
includeBackButton: false,
150-
includeSidebarButton: false,
151-
title: const Text('', maxLines: 1, overflow: TextOverflow.ellipsis),
152-
foregroundColor: isVideo == true ? Colors.white : null,
153-
backgroundColor: isVideo == true ? Colors.black : Colors.transparent,
154-
);
155-
156-
final fullHeightPlayer = Column(
157-
children: [
158-
if (!isMobile) headerBar,
159-
Expanded(child: body),
160-
],
161-
);
162-
163-
if (isVideo) {
164-
return fullHeightPlayer;
165-
}
166-
16752
return Stack(
16853
children: [
169-
PlayerColor(alpha: 0.4, size: size, position: playerPosition),
170-
fullHeightPlayer,
54+
if (!isVideo)
55+
PlayerColor(alpha: 0.4, size: size, position: playerPosition),
56+
Column(
57+
children: [
58+
if (!isMobile) FullHeightPlayerHeaderBar(isVideo: isVideo),
59+
Expanded(child: body),
60+
],
61+
),
17162
],
17263
);
17364
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:watch_it/watch_it.dart';
3+
import '../../app/app_model.dart';
4+
import '../../common/data/audio.dart';
5+
import '../../common/data/audio_type.dart';
6+
import '../../common/view/ui_constants.dart';
7+
import '../../extensions/build_context_x.dart';
8+
import '../../extensions/taget_platform_x.dart';
9+
import '../../radio/view/radio_history_list.dart';
10+
import 'full_height_player_image.dart';
11+
import 'full_height_player_top_controls.dart';
12+
import 'player_main_controls.dart';
13+
import 'player_title_and_artist.dart';
14+
import 'player_track.dart';
15+
import 'player_view.dart';
16+
import 'queue/queue_body.dart';
17+
import 'queue/queue_button.dart';
18+
19+
class FullHeightPlayerAudioBody extends StatelessWidget with WatchItMixin {
20+
const FullHeightPlayerAudioBody({
21+
super.key,
22+
this.audio,
23+
required this.playerPosition,
24+
required this.active,
25+
required this.iconColor,
26+
});
27+
28+
final Audio? audio;
29+
final bool active;
30+
final PlayerPosition playerPosition;
31+
final Color iconColor;
32+
33+
@override
34+
Widget build(BuildContext context) {
35+
final showQueue = watchPropertyValue((AppModel m) => m.showQueueOverlay);
36+
final playerWithSidePanel =
37+
playerPosition == PlayerPosition.fullWindow &&
38+
context.mediaQuerySize.width > 1000;
39+
final theme = context.theme;
40+
final queueOrHistory = audio?.audioType == AudioType.radio
41+
? const SizedBox(
42+
width: 400,
43+
height: 500,
44+
child: RadioHistoryList(simpleList: true),
45+
)
46+
: QueueBody(selectedColor: theme.colorScheme.onSurface);
47+
final column = Column(
48+
mainAxisAlignment: MainAxisAlignment.center,
49+
mainAxisSize: MainAxisSize.min,
50+
children: [
51+
if (showQueue && !playerWithSidePanel)
52+
Padding(
53+
padding: const EdgeInsets.only(
54+
bottom: 2 * kLargestSpace,
55+
top: kLargestSpace,
56+
),
57+
child: queueOrHistory,
58+
)
59+
else ...[
60+
if (!isMobile || context.isPortrait)
61+
const Hero(
62+
tag: 'FullHeightPlayerImageInPortrait',
63+
child: FullHeightPlayerImage(showAudioVisualizer: true),
64+
),
65+
const SizedBox(height: kLargestSpace),
66+
Padding(
67+
padding: const EdgeInsets.symmetric(horizontal: 30),
68+
child: PlayerTitleAndArtist(playerPosition: playerPosition),
69+
),
70+
const SizedBox(height: kLargestSpace),
71+
SizedBox(
72+
height: kLargestSpace,
73+
width: playerWithSidePanel ? 400 : 350,
74+
child: const PlayerTrack(),
75+
),
76+
const SizedBox(height: kLargestSpace),
77+
],
78+
SizedBox(
79+
width: playerWithSidePanel ? 400 : 320,
80+
child: PlayerMainControls(active: active),
81+
),
82+
],
83+
);
84+
85+
return Stack(
86+
alignment: Alignment.center,
87+
children: [
88+
Center(
89+
child: playerWithSidePanel
90+
? Row(
91+
mainAxisSize: MainAxisSize.min,
92+
mainAxisAlignment: MainAxisAlignment.center,
93+
children: [
94+
SizedBox(width: 490, child: column),
95+
queueOrHistory,
96+
],
97+
)
98+
: isMobile && !context.isPortrait
99+
? Row(
100+
spacing: kLargestSpace,
101+
mainAxisAlignment: MainAxisAlignment.center,
102+
mainAxisSize: MainAxisSize.min,
103+
children: [
104+
const Hero(
105+
tag: 'FullHeightPlayerImageInLandscape',
106+
child: FullHeightPlayerImage(height: 200, width: 200),
107+
),
108+
SizedBox(width: 400, child: column),
109+
],
110+
)
111+
: column,
112+
),
113+
Positioned(
114+
top: 0,
115+
right: 0,
116+
child: FullHeightPlayerTopControls(
117+
iconColor: iconColor,
118+
playerPosition: playerPosition,
119+
showQueueButton: !playerWithSidePanel,
120+
),
121+
),
122+
if (isMobile)
123+
const Positioned(
124+
bottom: 2 * kLargestSpace,
125+
child: QueueButton.text(),
126+
),
127+
],
128+
);
129+
}
130+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import 'package:flutter/material.dart';
2+
import '../../common/view/header_bar.dart';
3+
4+
class FullHeightPlayerHeaderBar extends StatelessWidget {
5+
const FullHeightPlayerHeaderBar({super.key, required this.isVideo});
6+
7+
final bool isVideo;
8+
9+
@override
10+
Widget build(BuildContext context) {
11+
return HeaderBar(
12+
adaptive: false,
13+
includeBackButton: false,
14+
includeSidebarButton: false,
15+
title: const Text('', maxLines: 1, overflow: TextOverflow.ellipsis),
16+
foregroundColor: isVideo == true ? Colors.white : null,
17+
backgroundColor: isVideo == true ? Colors.black : Colors.transparent,
18+
);
19+
}
20+
}

0 commit comments

Comments
 (0)