From c5f934cca8eac84a0d452b5b15e4b2d8952147f7 Mon Sep 17 00:00:00 2001 From: ThibaultBee <37510686+ThibaultBee@users.noreply.github.com> Date: Tue, 1 Oct 2024 11:05:51 +0200 Subject: [PATCH] feat(lib): events are now mixin --- example/lib/main.dart | 62 ++++++++------ lib/apivideo_live_stream.dart | 1 + lib/src/camera_preview.dart | 42 +++++---- lib/src/controller.dart | 103 ++++++++++++----------- lib/src/listeners.dart | 25 ++---- lib/src/platform/mobile_platform.dart | 11 ++- lib/src/platform/platform_interface.dart | 4 +- test/controller_test.dart | 1 - 8 files changed, 123 insertions(+), 126 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index c4ac00b..e45a8ad 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -45,7 +45,7 @@ class LiveViewPage extends StatefulWidget { } class _LiveViewPageState extends State - with WidgetsBindingObserver { + with WidgetsBindingObserver, ApiVideoLiveStreamEventsListener { final ButtonStyle buttonStyle = ElevatedButton.styleFrom(textStyle: const TextStyle(fontSize: 20)); Params params = Params(); @@ -67,6 +67,7 @@ class _LiveViewPageState extends State @override void dispose() { WidgetsBinding.instance.removeObserver(this); + _controller.removeEventsListener(this); _controller.dispose(); super.dispose(); } @@ -85,36 +86,43 @@ class _LiveViewPageState extends State } } + void onConnectionSuccess() { + print('Connection succeeded'); + } + + void onConnectionFailed(String reason) { + print('Connection failed: $reason'); + _showDialog(context, 'Connection failed', '$reason'); + if (mounted) { + setIsStreaming(false); + } + } + + void onDisconnection() { + showInSnackBar('Disconnected'); + if (mounted) { + setIsStreaming(false); + } + } + + void onError(Exception error) { + // Get error such as missing permission,... + if (error is PlatformException) { + _showDialog( + context, "Error", error.message ?? "An unknown error occurred"); + } else { + _showDialog(context, "Error", "$error"); + } + if (mounted) { + setIsStreaming(false); + } + } + ApiVideoLiveStreamController createLiveStreamController() { final controller = ApiVideoLiveStreamController( initialAudioConfig: params.audioConfig, initialVideoConfig: params.videoConfig); - - controller.addCallbacksListener(onConnectionSuccess: () { - print('Connection succeeded'); - }, onConnectionFailed: (error) { - print('Connection failed: $error'); - _showDialog(context, 'Connection failed', '$error'); - if (mounted) { - setIsStreaming(false); - } - }, onDisconnection: () { - showInSnackBar('Disconnected'); - if (mounted) { - setIsStreaming(false); - } - }, onError: (error) { - // Get error such as missing permission,... - if (error is PlatformException) { - _showDialog( - context, "Error", error.message ?? "An unknown error occurred"); - } else { - _showDialog(context, "Error", "$error"); - } - if (mounted) { - setIsStreaming(false); - } - }); + controller.addEventsListener(this); return controller; } diff --git a/lib/apivideo_live_stream.dart b/lib/apivideo_live_stream.dart index b73e5fb..733b6a9 100644 --- a/lib/apivideo_live_stream.dart +++ b/lib/apivideo_live_stream.dart @@ -1,5 +1,6 @@ export 'src/camera_preview.dart'; export 'src/controller.dart'; +export 'src/listeners.dart' show ApiVideoLiveStreamEventsListener; export 'src/platform/generated/live_stream_api.g.dart' show CameraPosition, Channel; export 'src/platform/mobile_platform.dart'; diff --git a/lib/src/camera_preview.dart b/lib/src/camera_preview.dart index eee3850..052c3d2 100644 --- a/lib/src/camera_preview.dart +++ b/lib/src/camera_preview.dart @@ -28,25 +28,8 @@ class ApiVideoCameraPreview extends StatefulWidget { State createState() => _ApiVideoCameraPreviewState(); } -class _ApiVideoCameraPreviewState extends State { - _ApiVideoCameraPreviewState() { - _widgetListener = ApiVideoLiveStreamWidgetListener(onTextureReady: () { - final int newTextureId = widget.controller.textureId; - if (newTextureId != _textureId) { - setState(() { - _textureId = newTextureId; - }); - } - }); - - _eventsListener = - ApiVideoLiveStreamEventsListener(onVideoSizeChanged: (size) { - _updateAspectRatio(size); - }); - } - - late ApiVideoLiveStreamWidgetListener _widgetListener; - late ApiVideoLiveStreamEventsListener _eventsListener; +class _ApiVideoCameraPreviewState extends State + with ApiVideoLiveStreamEventsListener, ApiVideoLiveStreamWidgetListener { late int _textureId; double _aspectRatio = 1.77; @@ -56,8 +39,8 @@ class _ApiVideoCameraPreviewState extends State { void initState() { super.initState(); _textureId = widget.controller.textureId; - widget.controller.addWidgetListener(_widgetListener); - widget.controller.addEventsListener(_eventsListener); + widget.controller.addWidgetListener(this); + widget.controller.addEventsListener(this); if (widget.controller.isInitialized) { widget.controller.videoSize.then((size) { if (size != null) { @@ -70,11 +53,24 @@ class _ApiVideoCameraPreviewState extends State { @override void dispose() { widget.controller.stopPreview(); - widget.controller.removeWidgetListener(_widgetListener); - widget.controller.removeEventsListener(_eventsListener); + widget.controller.removeWidgetListener(this); + widget.controller.removeEventsListener(this); super.dispose(); } + void onTextureReady() { + final int newTextureId = widget.controller.textureId; + if (newTextureId != _textureId) { + setState(() { + _textureId = newTextureId; + }); + } + } + + void onVideoSizeChanged(Size size) { + _updateAspectRatio(size); + } + @override Widget build(BuildContext context) { return _textureId == ApiVideoLiveStreamController.kUninitializedTextureId diff --git a/lib/src/controller.dart b/lib/src/controller.dart index 763e060..6aa1db0 100644 --- a/lib/src/controller.dart +++ b/lib/src/controller.dart @@ -30,36 +30,9 @@ class ApiVideoLiveStreamController { bool get isInitialized => _isInitialized; /// Events listeners - List _eventsListeners = []; + _EventListenersManager _eventsListenersManager = _EventListenersManager(); List _widgetListeners = []; - late ApiVideoLiveStreamEventsListener _platformListener = - ApiVideoLiveStreamEventsListener(onConnectionSuccess: () { - _eventsListeners.forEach((listener) { - if (listener.onConnectionSuccess != null) { - listener.onConnectionSuccess!(); - } - }); - }, onConnectionFailed: (error) { - _eventsListeners.forEach((listener) { - if (listener.onConnectionFailed != null) { - listener.onConnectionFailed!(error); - } - }); - }, onDisconnection: () { - _eventsListeners.forEach((listener) { - if (listener.onDisconnection != null) { - listener.onDisconnection!(); - } - }); - }, onError: (error) { - _eventsListeners.forEach((listener) { - if (listener.onError != null) { - listener.onError!(error); - } - }); - }); - /// Creates a new [ApiVideoLiveStreamController] instance. ApiVideoLiveStreamController( {required AudioConfig initialAudioConfig, @@ -71,13 +44,11 @@ class ApiVideoLiveStreamController { /// Creates a new live stream instance with initial audio and video configurations. Future initialize() async { - _platform.setListener(_platformListener); + _platform.setListener(_eventsListenersManager); _textureId = await _platform.initialize() ?? kUninitializedTextureId; for (var listener in [..._widgetListeners]) { - if (listener.onTextureReady != null) { - listener.onTextureReady!(); - } + listener.onTextureReady(); } await setCameraPosition(_initialCameraPosition); @@ -92,7 +63,7 @@ class ApiVideoLiveStreamController { /// Disposes the live stream instance. Future dispose() async { _platform.setListener(null); - _eventsListeners.clear(); + _eventsListenersManager.dispose(); _widgetListeners.clear(); await _platform.dispose(); return; @@ -197,30 +168,14 @@ class ApiVideoLiveStreamController { return Texture(textureId: textureId); } - /// Adds a new events listener from the direct callbacks. - /// Returns the listener instance. You can remove it later with [removeEventsListener]. - ApiVideoLiveStreamEventsListener addCallbacksListener( - {VoidCallback? onConnectionSuccess, - Function(String)? onConnectionFailed, - VoidCallback? onDisconnection, - Function(Exception)? onError}) { - final listener = ApiVideoLiveStreamEventsListener( - onConnectionSuccess: onConnectionSuccess, - onConnectionFailed: onConnectionFailed, - onDisconnection: onDisconnection, - onError: onError); - _eventsListeners.add(listener); - return listener; - } - /// Adds a new widget listener from the events listener. void addEventsListener(ApiVideoLiveStreamEventsListener listener) { - _eventsListeners.add(listener); + _eventsListenersManager.add(listener); } /// Removes an events listener. void removeEventsListener(ApiVideoLiveStreamEventsListener listener) { - _eventsListeners.remove(listener); + _eventsListenersManager.remove(listener); } /// This is exposed for internal use only. Do not use it. @@ -235,3 +190,49 @@ class ApiVideoLiveStreamController { _widgetListeners.remove(listener); } } + +class _EventListenersManager with ApiVideoLiveStreamEventsListener { + final List listeners = []; + + void onConnectionSuccess() { + for (var listener in listeners) { + listener.onConnectionSuccess(); + } + } + + void onConnectionFailed(String reason) { + for (var listener in listeners) { + listener.onConnectionFailed(reason); + } + } + + void onDisconnection() { + for (var listener in listeners) { + listener.onDisconnection(); + } + } + + void onError(Exception error) { + for (var listener in listeners) { + listener.onError(error); + } + } + + void onVideoSizeChanged(Size size) { + for (var listener in listeners) { + listener.onVideoSizeChanged(size); + } + } + + void add(ApiVideoLiveStreamEventsListener listener) { + listeners.add(listener); + } + + void remove(ApiVideoLiveStreamEventsListener listener) { + listeners.remove(listener); + } + + void dispose() { + listeners.clear(); + } +} diff --git a/lib/src/listeners.dart b/lib/src/listeners.dart index 75f2ef9..47a0a82 100644 --- a/lib/src/listeners.dart +++ b/lib/src/listeners.dart @@ -1,31 +1,22 @@ import 'dart:ui'; -class ApiVideoLiveStreamEventsListener { +mixin ApiVideoLiveStreamEventsListener { /// Gets notified when the connection is successful - final VoidCallback? onConnectionSuccess; + void onConnectionSuccess() {} /// Gets notified when the connection failed - final Function(String)? onConnectionFailed; + void onConnectionFailed(String reason) {} /// Gets notified when the device has been disconnected - final VoidCallback? onDisconnection; + void onDisconnection() {} /// Gets notified when the video size has changed. Mostly designed to update Widget aspect ratio. - final Function(Size)? onVideoSizeChanged; + void onVideoSizeChanged(Size size) {} /// Gets notified when an error occurs - final Function(Exception)? onError; - - ApiVideoLiveStreamEventsListener( - {this.onConnectionSuccess, - this.onConnectionFailed, - this.onDisconnection, - this.onVideoSizeChanged, - this.onError}); + void onError(Exception error) {} } -class ApiVideoLiveStreamWidgetListener { - final VoidCallback? onTextureReady; - - ApiVideoLiveStreamWidgetListener({this.onTextureReady}); +mixin ApiVideoLiveStreamWidgetListener { + void onTextureReady() {} } diff --git a/lib/src/platform/mobile_platform.dart b/lib/src/platform/mobile_platform.dart index 81c2026..323ceb9 100644 --- a/lib/src/platform/mobile_platform.dart +++ b/lib/src/platform/mobile_platform.dart @@ -112,26 +112,25 @@ class ApiVideoMobileLiveStreamPlatform extends ApiVideoLiveStreamPlatform @override void onConnectionFailed(String message) { - _eventsListener?.onConnectionFailed?.call(message); + _eventsListener?.onConnectionFailed(message); } @override void onIsConnectedChanged(bool isConnected) { if (isConnected) { - _eventsListener?.onConnectionSuccess?.call(); + _eventsListener?.onConnectionSuccess(); } else { - _eventsListener?.onDisconnection?.call(); + _eventsListener?.onDisconnection(); } } @override void onVideoSizeChanged(NativeResolution resolution) { - _eventsListener?.onVideoSizeChanged?.call(resolution.toSize()); + _eventsListener?.onVideoSizeChanged(resolution.toSize()); } @override void onError(String code, String message) { - _eventsListener?.onError - ?.call(PlatformException(code: code, message: message)); + _eventsListener?.onError(PlatformException(code: code, message: message)); } } diff --git a/lib/src/platform/platform_interface.dart b/lib/src/platform/platform_interface.dart index 5f04220..9522751 100644 --- a/lib/src/platform/platform_interface.dart +++ b/lib/src/platform/platform_interface.dart @@ -1,5 +1,7 @@ -import 'package:apivideo_live_stream/apivideo_live_stream.dart'; import 'package:apivideo_live_stream/src/listeners.dart'; +import 'package:apivideo_live_stream/src/platform/generated/live_stream_api.g.dart'; +import 'package:apivideo_live_stream/src/platform/mobile_platform.dart'; +import 'package:apivideo_live_stream/src/types/types.dart'; import 'package:flutter/widgets.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; diff --git a/test/controller_test.dart b/test/controller_test.dart index ba77e71..44809dd 100644 --- a/test/controller_test.dart +++ b/test/controller_test.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:apivideo_live_stream/apivideo_live_stream.dart'; -import 'package:apivideo_live_stream/src/listeners.dart'; import 'package:apivideo_live_stream/src/platform/platform_interface.dart'; import 'package:flutter_test/flutter_test.dart';