flet-audio 0.1.0.dev1__py3-none-any.whl → 0.2.0.dev35__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,59 +2,48 @@ import 'dart:async';
2
2
  import 'dart:convert';
3
3
 
4
4
  import 'package:audioplayers/audioplayers.dart';
5
- import 'package:collection/collection.dart';
6
5
  import 'package:flet/flet.dart';
7
6
  import 'package:flutter/foundation.dart';
8
- import 'package:flutter/widgets.dart';
9
7
 
10
- class AudioControl extends StatefulWidget {
11
- final Control? parent;
12
- final Control control;
13
- final Widget? nextChild;
14
- final FletControlBackend backend;
8
+ import 'utils/audio.dart';
15
9
 
16
- const AudioControl(
17
- {super.key,
18
- required this.parent,
19
- required this.control,
20
- required this.nextChild,
21
- required this.backend});
10
+ class AudioService extends FletService {
11
+ AudioService({required super.control});
22
12
 
23
- @override
24
- State<AudioControl> createState() => _AudioControlState();
25
- }
26
-
27
- class _AudioControlState extends State<AudioControl> with FletStoreMixin {
28
- AudioPlayer? player;
29
- void Function(Duration)? _onDurationChanged;
30
- void Function(PlayerState)? _onStateChanged;
31
- void Function(int)? _onPositionChanged;
13
+ AudioPlayer player = AudioPlayer();
32
14
  Duration? _duration;
33
15
  int _position = -1;
34
- void Function()? _onSeekComplete;
35
16
  StreamSubscription? _onDurationChangedSubscription;
36
17
  StreamSubscription? _onStateChangedSubscription;
37
18
  StreamSubscription? _onPositionChangedSubscription;
38
19
  StreamSubscription? _onSeekCompleteSubscription;
39
20
 
21
+ String? _src;
22
+ String? _srcBase64;
23
+ ReleaseMode? _releaseMode;
24
+ double? _volume;
25
+ double? _balance;
26
+ double? _playbackRate;
27
+
40
28
  @override
41
- void initState() {
42
- debugPrint("Audio.initState($hashCode)");
43
- player = widget.control.state["player"];
44
- if (player == null) {
45
- player = AudioPlayer();
46
- player = widget.control.state["player"] = player;
47
- }
29
+ void init() {
30
+ super.init();
31
+ debugPrint("Audio(${control.id}).init: ${control.properties}");
32
+ control.addInvokeMethodListener(_invokeMethod);
33
+
48
34
  _onDurationChangedSubscription =
49
- player?.onDurationChanged.listen((duration) {
50
- _onDurationChanged?.call(duration);
35
+ player.onDurationChanged.listen((duration) {
36
+ control.triggerEvent(
37
+ "duration_change", {"duration": duration.inMilliseconds});
51
38
  _duration = duration;
52
39
  });
53
- _onStateChangedSubscription = player?.onPlayerStateChanged.listen((state) {
54
- _onStateChanged?.call(state);
40
+
41
+ _onStateChangedSubscription = player.onPlayerStateChanged.listen((PlayerState state) {
42
+ control.triggerEvent("state_change", {"state": state.name});
55
43
  });
44
+
56
45
  _onPositionChangedSubscription =
57
- player?.onPositionChanged.listen((position) {
46
+ player.onPositionChanged.listen((position) {
58
47
  int posMs = (position.inMilliseconds / 1000).round() * 1000;
59
48
  if (posMs != _position) {
60
49
  _position = posMs;
@@ -63,184 +52,126 @@ class _AudioControlState extends State<AudioControl> with FletStoreMixin {
63
52
  } else {
64
53
  return;
65
54
  }
66
- _onPositionChanged?.call(_position);
67
- });
68
- _onSeekCompleteSubscription = player?.onSeekComplete.listen((event) {
69
- _onSeekComplete?.call();
55
+ control.triggerEvent("position_change", {"position": posMs});
70
56
  });
71
57
 
72
- widget.control.onRemove.clear();
73
- widget.control.onRemove.add(_onRemove);
74
- super.initState();
75
- }
76
-
77
- void _onRemove() {
78
- debugPrint("Audio.remove($hashCode)");
79
- widget.control.state["player"]?.dispose();
80
- widget.backend.unsubscribeMethods(widget.control.id);
81
- }
58
+ _onSeekCompleteSubscription = player.onSeekComplete.listen((event) {
59
+ control.triggerEvent("seek_complete");
60
+ });
82
61
 
83
- @override
84
- void deactivate() {
85
- debugPrint("Audio.deactivate($hashCode)");
86
- _onDurationChangedSubscription?.cancel();
87
- _onStateChangedSubscription?.cancel();
88
- _onPositionChangedSubscription?.cancel();
89
- _onSeekCompleteSubscription?.cancel();
90
- super.deactivate();
62
+ update();
91
63
  }
92
64
 
93
65
  @override
94
- Widget build(BuildContext context) {
95
- debugPrint(
96
- "Audio build: ${widget.control.id} (${widget.control.hashCode})");
66
+ void update() {
67
+ debugPrint("Audio(${control.id}).update: ${control.properties}");
97
68
 
98
- var src = widget.control.attrString("src", "")!;
99
- var srcBase64 = widget.control.attrString("srcBase64", "")!;
69
+ var src = control.getString("src", "")!;
70
+ var srcBase64 = control.getString("src_base64", "")!;
100
71
  if (src == "" && srcBase64 == "") {
101
- return const ErrorControl(
72
+ throw Exception(
102
73
  "Audio must have either \"src\" or \"src_base64\" specified.");
103
74
  }
104
- bool autoplay = widget.control.attrBool("autoplay", false)!;
105
- double? volume = widget.control.attrDouble("volume", null);
106
- double? balance = widget.control.attrDouble("balance", null);
107
- double? playbackRate = widget.control.attrDouble("playbackRate", null);
108
- var releaseMode = ReleaseMode.values.firstWhereOrNull((e) =>
109
- e.name.toLowerCase() ==
110
- widget.control.attrString("releaseMode", "")!.toLowerCase());
111
- bool onPositionChanged =
112
- widget.control.attrBool("onPositionChanged", false)!;
113
-
114
- final String prevSrc = widget.control.state["src"] ?? "";
115
- final String prevSrcBase64 = widget.control.state["srcBase64"] ?? "";
116
- final ReleaseMode? prevReleaseMode = widget.control.state["releaseMode"];
117
- final double? prevVolume = widget.control.state["volume"];
118
- final double? prevBalance = widget.control.state["balance"];
119
- final double? prevPlaybackRate = widget.control.state["playbackRate"];
120
-
121
- return withPageArgs((context, pageArgs) {
122
- _onDurationChanged = (duration) {
123
- widget.backend.triggerControlEvent(widget.control.id,
124
- "duration_changed", duration.inMilliseconds.toString());
125
- };
126
-
127
- _onStateChanged = (state) {
128
- debugPrint("Audio($hashCode) - state_changed: ${state.name}");
129
- widget.backend.triggerControlEvent(
130
- widget.control.id, "state_changed", state.name.toString());
131
- };
132
-
133
- if (onPositionChanged) {
134
- _onPositionChanged = (position) {
135
- widget.backend.triggerControlEvent(
136
- widget.control.id, "position_changed", position.toString());
137
- };
75
+ var autoplay = control.getBool("autoplay", false)!;
76
+ var volume = control.getDouble("volume", 1.0)!;
77
+ var balance = control.getDouble("balance", 0.0)!;
78
+ var playbackRate = control.getDouble("playback_rate", 1)!;
79
+ var releaseMode = parseReleaseMode(control.getString("release_mode"));
80
+
81
+ () async {
82
+ bool srcChanged = false;
83
+ if (src != "" && src != _src) {
84
+ _src = src;
85
+ srcChanged = true;
86
+
87
+ // URL or file?
88
+ var assetSrc = control.backend.getAssetSource(src);
89
+ if (assetSrc.isFile) {
90
+ await player.setSourceDeviceFile(assetSrc.path);
91
+ } else {
92
+ await player.setSourceUrl(assetSrc.path);
93
+ }
94
+ } else if (srcBase64 != "" && srcBase64 != _srcBase64) {
95
+ _srcBase64 = srcBase64;
96
+ srcChanged = true;
97
+ await player.setSourceBytes(base64Decode(srcBase64));
138
98
  }
139
99
 
140
- _onSeekComplete = () {
141
- widget.backend.triggerControlEvent(widget.control.id, "seek_complete");
142
- };
143
-
144
- () async {
145
- debugPrint("Audio ($hashCode) src=$src, prevSrc=$prevSrc");
146
- debugPrint(
147
- "Audio ($hashCode) srcBase64=$srcBase64, prevSrcBase64=$prevSrcBase64");
148
-
149
- bool srcChanged = false;
150
- if (src != "" && src != prevSrc) {
151
- widget.control.state["src"] = src;
152
- srcChanged = true;
153
-
154
- // URL or file?
155
- var assetSrc =
156
- getAssetSrc(src, pageArgs.pageUri!, pageArgs.assetsDir);
157
- if (assetSrc.isFile) {
158
- await player?.setSourceDeviceFile(assetSrc.path);
159
- } else {
160
- await player?.setSourceUrl(assetSrc.path);
161
- }
162
- } else if (srcBase64 != "" && srcBase64 != prevSrcBase64) {
163
- widget.control.state["srcBase64"] = srcBase64;
164
- srcChanged = true;
165
- await player?.setSourceBytes(base64Decode(srcBase64));
166
- }
100
+ if (srcChanged) {
101
+ control.triggerEvent("loaded");
102
+ }
167
103
 
168
- if (srcChanged) {
169
- debugPrint("Audio.srcChanged!");
170
- widget.backend.triggerControlEvent(widget.control.id, "loaded");
171
- }
104
+ if (releaseMode != null && releaseMode != _releaseMode) {
105
+ _releaseMode = releaseMode;
106
+ await player.setReleaseMode(releaseMode);
107
+ }
172
108
 
173
- if (releaseMode != null && releaseMode != prevReleaseMode) {
174
- debugPrint("Audio.setReleaseMode($releaseMode)");
175
- widget.control.state["releaseMode"] = releaseMode;
176
- await player?.setReleaseMode(releaseMode);
177
- }
109
+ if (volume != _volume && volume >= 0 && volume <= 1) {
110
+ _volume = volume;
111
+ await player.setVolume(volume);
112
+ }
178
113
 
179
- if (volume != null &&
180
- volume != prevVolume &&
181
- volume >= 0 &&
182
- volume <= 1) {
183
- widget.control.state["volume"] = volume;
184
- debugPrint("Audio.setVolume($volume)");
185
- await player?.setVolume(volume);
186
- }
114
+ if (playbackRate != _playbackRate) {
115
+ _playbackRate = playbackRate;
116
+ await player.setPlaybackRate(playbackRate);
117
+ }
187
118
 
188
- if (playbackRate != null &&
189
- playbackRate != prevPlaybackRate &&
190
- playbackRate >= 0 &&
191
- playbackRate <= 2) {
192
- widget.control.state["playbackRate"] = playbackRate;
193
- debugPrint("Audio.setPlaybackRate($playbackRate)");
194
- await player?.setPlaybackRate(playbackRate);
195
- }
119
+ if (!kIsWeb &&
120
+ balance != _balance &&
121
+ balance >= -1 &&
122
+ balance <= 1) {
123
+ _balance = balance;
124
+ await player.setBalance(balance);
125
+ }
196
126
 
197
- if (!kIsWeb &&
198
- balance != null &&
199
- balance != prevBalance &&
200
- balance >= -1 &&
201
- balance <= 1) {
202
- widget.control.state["balance"] = balance;
203
- debugPrint("Audio.setBalance($balance)");
204
- await player?.setBalance(balance);
205
- }
127
+ if (srcChanged && autoplay) {
128
+ await player.resume();
129
+ }
130
+ }();
131
+ }
206
132
 
207
- if (srcChanged && autoplay) {
208
- debugPrint("Audio.resume($srcChanged, $autoplay)");
209
- await player?.resume();
133
+ Future<dynamic> _invokeMethod(String name, dynamic args) async {
134
+ debugPrint("Audio.$name($args)");
135
+ switch (name) {
136
+ case "play":
137
+ final position = parseDuration(args["position"]);
138
+ if (position != null) {
139
+ await player.seek(position);
140
+ }
141
+ await player.resume();
142
+ break;
143
+ case "resume":
144
+ await player.resume();
145
+ break;
146
+ case "pause":
147
+ await player.pause();
148
+ break;
149
+ case "release":
150
+ await player.release();
151
+ break;
152
+ case "seek":
153
+ final position = parseDuration(args["position"]);
154
+ if (position != null) {
155
+ await player.seek(position);
210
156
  }
157
+ break;
158
+ case "get_duration":
159
+ return await player.getDuration();
160
+ case "get_current_position":
161
+ return await player.getCurrentPosition();
162
+ default:
163
+ throw Exception("Unknown Audio method: $name");
164
+ }
165
+ }
211
166
 
212
- widget.backend.subscribeMethods(widget.control.id,
213
- (methodName, args) async {
214
- switch (methodName) {
215
- case "play":
216
- await player?.seek(const Duration(milliseconds: 0));
217
- await player?.resume();
218
- break;
219
- case "resume":
220
- await player?.resume();
221
- break;
222
- case "pause":
223
- await player?.pause();
224
- break;
225
- case "release":
226
- await player?.release();
227
- break;
228
- case "seek":
229
- await player?.seek(Duration(
230
- milliseconds: int.tryParse(args["position"] ?? "") ?? 0));
231
- break;
232
- case "get_duration":
233
- return (await player?.getDuration())?.inMilliseconds.toString();
234
- case "get_current_position":
235
- return (await player?.getCurrentPosition())
236
- ?.inMilliseconds
237
- .toString();
238
- }
239
- return null;
240
- });
241
- }();
242
-
243
- return const SizedBox.shrink();
244
- });
167
+ @override
168
+ void dispose() {
169
+ debugPrint("Audio(${control.id}).dispose()");
170
+ control.removeInvokeMethodListener(_invokeMethod);
171
+ _onDurationChangedSubscription?.cancel();
172
+ _onStateChangedSubscription?.cancel();
173
+ _onPositionChangedSubscription?.cancel();
174
+ _onSeekCompleteSubscription?.cancel();
175
+ super.dispose();
245
176
  }
246
177
  }
@@ -0,0 +1,15 @@
1
+ import 'package:flet/flet.dart';
2
+
3
+ import 'audio.dart';
4
+
5
+ class Extension extends FletExtension {
6
+ @override
7
+ FletService? createService(Control control) {
8
+ switch (control.type) {
9
+ case "Audio":
10
+ return AudioService(control: control);
11
+ default:
12
+ return null;
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,9 @@
1
+ import 'package:audioplayers/audioplayers.dart';
2
+ import 'package:collection/collection.dart';
3
+
4
+ ReleaseMode? parseReleaseMode(String? value, [ReleaseMode? defaultValue]) {
5
+ if (value == null) return defaultValue;
6
+ return ReleaseMode.values.firstWhereOrNull(
7
+ (e) => e.name.toLowerCase() == value.toLowerCase()) ??
8
+ defaultValue;
9
+ }