Line data Source code
1 : import 'package:flutter/material.dart';
2 : import 'package:flutter_chrome_cast/_session_manager/cast_session_manager.dart';
3 : import 'package:get/get_rx/get_rx.dart';
4 :
5 : /// A widget that provides volume control for Google Cast devices.
6 : ///
7 : /// This widget displays a volume icon that, when tapped, shows a popup
8 : /// with a slider to control the volume of the currently connected Cast device.
9 : class GoogleCastVolume extends StatefulWidget {
10 : /// Color of the volume icon.
11 : final Color? iconColor;
12 :
13 : /// Size of the volume icon.
14 : final double? iconSize;
15 :
16 : /// Background color of the volume control popup.
17 : final Color? popupBackgroundColor;
18 :
19 : /// Color of the active portion of the volume slider.
20 : final Color? sliderActiveColor;
21 :
22 : /// Color of the inactive portion of the volume slider.
23 : final Color? sliderInactiveColor;
24 :
25 : /// Color of the volume slider thumb/handle.
26 : final Color? sliderThumbColor;
27 :
28 : /// Creates a Google Cast volume control widget.
29 : ///
30 : /// All styling parameters are optional and will use default Material Design
31 : /// colors if not specified.
32 0 : const GoogleCastVolume({
33 : super.key,
34 : this.iconColor,
35 : this.iconSize,
36 : this.popupBackgroundColor,
37 : this.sliderActiveColor,
38 : this.sliderInactiveColor,
39 : this.sliderThumbColor,
40 : });
41 :
42 0 : @override
43 0 : State<GoogleCastVolume> createState() => _GoogleCastVolumeState();
44 : }
45 :
46 : class _GoogleCastVolumeState extends State<GoogleCastVolume> {
47 0 : @override
48 : void initState() {
49 0 : GoogleCastSessionManager.instance.currentSessionStream.listen((event) {
50 0 : volumeLevel.value = event?.currentDeviceVolume ?? 0;
51 0 : volumeMuted.value = event?.currentDeviceMuted ?? false;
52 : });
53 0 : super.initState();
54 : }
55 :
56 : final volumeLevel = 0.5.obs;
57 : final volumeMuted = false.obs;
58 : bool isSliderTapDown = false;
59 :
60 0 : @override
61 : Widget build(BuildContext context) {
62 0 : return PopupMenuButton(
63 : padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4),
64 0 : shape: RoundedRectangleBorder(
65 0 : borderRadius: BorderRadius.circular(10),
66 : ),
67 0 : color: widget.popupBackgroundColor,
68 0 : icon: StreamBuilder(
69 0 : stream: volumeMuted.stream,
70 0 : builder: (context, snapshot) {
71 0 : return Icon(
72 0 : volumeMuted.value || volumeLevel.value == 0
73 : ? Icons.volume_off
74 : : Icons.volume_up,
75 0 : color: widget.iconColor ?? Colors.white,
76 0 : size: widget.iconSize ?? 44,
77 : );
78 : }),
79 0 : itemBuilder: (context) {
80 0 : return [
81 0 : PopupMenuItem(
82 : enabled: false,
83 0 : child: StreamBuilder(
84 0 : stream: volumeLevel.stream,
85 0 : builder: (context, snapshot) {
86 0 : return Slider(
87 0 : value: volumeLevel.value,
88 0 : onChanged: _onVolumeChanged,
89 0 : onChangeStart: _onVolumeChangedStart,
90 0 : onChangeEnd: _onVolumeChangedEnd,
91 0 : activeColor: widget.sliderActiveColor,
92 0 : inactiveColor: widget.sliderInactiveColor,
93 0 : thumbColor: widget.sliderThumbColor,
94 : );
95 : },
96 : ),
97 : ),
98 : ];
99 : },
100 : );
101 : }
102 :
103 0 : void _onVolumeChangedEnd(double value) {
104 0 : isSliderTapDown = false;
105 0 : GoogleCastSessionManager.instance.setDeviceVolume(value);
106 : }
107 :
108 0 : void _onVolumeChangedStart(double value) {
109 0 : isSliderTapDown = true;
110 : }
111 :
112 0 : void _onVolumeChanged(double value) {
113 0 : volumeLevel.value = value;
114 : }
115 : }
|