Skip to content

Commit 202ce36

Browse files
authored
fix: compile errors on Dart 3.13 from stricter flow analysis (#1146)
## What Dart 3.13 (shipping with Flutter 3.47, currently in beta) no longer keeps null promotion of captured variables across `await`. The video `negotiate()` closure in `LocalParticipant` dereferences the captured nullable `publishOptions` after an `await`, which now fails to compile: ``` lib/src/participant/local.dart:384: Error: Property 'videoCodec' cannot be accessed on 'VideoPublishOptions?' because it is potentially null. Info: Variable 'publishOptions' could not be promoted due to an 'await' or 'yield'. ``` This breaks every app that depends on `livekit_client` once users update to Flutter 3.47, including the agent starter example. ## Fix Pass the publish options into the `negotiate()` closure as a non-nullable parameter instead of capturing the nullable variable. At both call sites `publishOptions` is already promoted non-null in the enclosing function body, so this needs no null assertions at all. The audio closure gets the same treatment for consistency, which removes the pre-existing `publishOptions!` there too. ## Verification - `flutter analyze` on 3.47 beta goes from 5 errors to 0, and stays clean on stable 3.44 - All unit tests pass - The agent starter example builds on 3.47 beta with this patch applied to the resolved package
1 parent d3c53f2 commit 202ce36

2 files changed

Lines changed: 12 additions & 11 deletions

File tree

.changes/fix-dart313-flow-analysis

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
patch type="fixed" "Fix compile errors on Dart 3.13 where nullable publish options are no longer promoted across await"

lib/src/participant/local.dart

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -196,15 +196,15 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
196196
if (publishOptions.preConnect) lk_models.AudioTrackFeature.TF_PRECONNECT_BUFFER,
197197
]);
198198

199-
Future<lk_models.TrackInfo> negotiate() async {
200-
track.transceiver = await room.engine.createTransceiverRTCRtpSender(track, publishOptions!, encodings);
199+
Future<lk_models.TrackInfo> negotiate(AudioPublishOptions options) async {
200+
track.transceiver = await room.engine.createTransceiverRTCRtpSender(track, options, encodings);
201201
await room.engine.negotiate();
202202
return lk_models.TrackInfo();
203203
}
204204

205205
late lk_models.TrackInfo trackInfo;
206206
if (room.engine.enabledPublishCodecs?.isNotEmpty ?? false) {
207-
final rets = await Future.wait<lk_models.TrackInfo>([room.engine.addTrack(req), negotiate()]);
207+
final rets = await Future.wait<lk_models.TrackInfo>([room.engine.addTrack(req), negotiate(publishOptions)]);
208208
trackInfo = rets[0];
209209
} else {
210210
trackInfo = await room.engine.addTrack(req);
@@ -378,31 +378,31 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
378378

379379
logger.fine('Video layers: ${layers.map((e) => e)}');
380380

381-
Future<lk_models.TrackInfo> negotiate() async {
382-
track.transceiver = await room.engine.createTransceiverRTCRtpSender(track, publishOptions!, encodings);
381+
Future<lk_models.TrackInfo> negotiate(VideoPublishOptions options) async {
382+
track.transceiver = await room.engine.createTransceiverRTCRtpSender(track, options, encodings);
383383

384-
track.codec = publishOptions.videoCodec;
384+
track.codec = options.videoCodec;
385385
if (lkBrowser() != BrowserType.firefox) {
386386
await room.engine.setPreferredCodec(
387387
track.transceiver!,
388388
'video',
389-
publishOptions.videoCodec,
389+
options.videoCodec,
390390
);
391391
}
392392

393393
if ([TrackSource.camera, TrackSource.screenShareVideo].contains(track.source)) {
394-
final degradationPreference = publishOptions.degradationPreference ?? DegradationPreference.maintainResolution;
394+
final degradationPreference = options.degradationPreference ?? DegradationPreference.maintainResolution;
395395
await track.setDegradationPreference(degradationPreference);
396396
}
397397

398398
if (kIsWeb && lkBrowser() == BrowserType.firefox && track.kind == TrackType.AUDIO) {
399399
//TOOD:
400-
} else if (isVideoCodec(publishOptions.videoCodec) && encodings?.first.maxBitrate != null) {
400+
} else if (isVideoCodec(options.videoCodec) && encodings?.first.maxBitrate != null) {
401401
// Apply start bitrate for all video codecs to prevent initial blurriness
402402
room.engine.publisher?.setTrackBitrateInfo(TrackBitrateInfo(
403403
cid: track.getCid(),
404404
transceiver: track.transceiver,
405-
codec: publishOptions.videoCodec,
405+
codec: options.videoCodec,
406406
maxbr: encodings![0].maxBitrate! ~/ 1000));
407407
}
408408

@@ -438,7 +438,7 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
438438
}
439439
late lk_models.TrackInfo trackInfo;
440440
if (room.engine.enabledPublishCodecs?.isNotEmpty ?? false) {
441-
final rets = await Future.wait<lk_models.TrackInfo>([room.engine.addTrack(req), negotiate()]);
441+
final rets = await Future.wait<lk_models.TrackInfo>([room.engine.addTrack(req), negotiate(publishOptions)]);
442442
trackInfo = rets[0];
443443
} else {
444444
trackInfo = await room.engine.addTrack(req);

0 commit comments

Comments
 (0)