import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; enum CastingStatus { unavailable, idle, connecting, connected, error } enum CastingPlaybackStatus { unknown, stopped, buffering, playing } class CastingBusinessObject extends ChangeNotifier { static const MethodChannel _methodChannel = MethodChannel( 'kryz_go_flutter/casting/methods', ); static const EventChannel _eventChannel = EventChannel( 'kryz_go_flutter/casting/events', ); StreamSubscription? _eventSubscription; CastingStatus _status = CastingStatus.unavailable; CastingPlaybackStatus _playbackStatus = CastingPlaybackStatus.unknown; String? _connectedDeviceName; String? _lastError; CastingStatus get status => _status; CastingPlaybackStatus get playbackStatus => _playbackStatus; String? get connectedDeviceName => _connectedDeviceName; String? get lastError => _lastError; bool get isSupportedOnThisPlatform { if (kIsWeb) { return false; } return defaultTargetPlatform == TargetPlatform.iOS || defaultTargetPlatform == TargetPlatform.android; } bool get isConnected => _status == CastingStatus.connected; bool get isPlaying => _playbackStatus == CastingPlaybackStatus.playing; bool get isBuffering => _playbackStatus == CastingPlaybackStatus.buffering; bool get isStopped => _playbackStatus == CastingPlaybackStatus.stopped; Future initialize() async { if (!isSupportedOnThisPlatform) { _setStatus(CastingStatus.unavailable); return; } _eventSubscription?.cancel(); _eventSubscription = _eventChannel.receiveBroadcastStream().listen( _handleEvent, onError: (Object error) { _lastError = error.toString(); _setStatus(CastingStatus.error); }, ); try { final dynamic status = await _methodChannel.invokeMethod( 'getStatus', ); _handleEvent(status); } on PlatformException catch (error) { _lastError = error.message ?? error.code; _setStatus(CastingStatus.error); } } Future showDevicePicker() async { if (!isSupportedOnThisPlatform) { _setStatus(CastingStatus.unavailable); return; } _setStatus(CastingStatus.connecting); try { await _methodChannel.invokeMethod('showDevicePicker'); } on PlatformException catch (error) { _lastError = error.message ?? error.code; _setStatus(CastingStatus.error); } } Future startCastingLiveStream({ required String streamUrl, required String title, required String subtitle, }) async { if (!isSupportedOnThisPlatform) { _setStatus(CastingStatus.unavailable); return; } try { await _methodChannel.invokeMethod('startCastingLiveStream', { 'streamUrl': streamUrl, 'title': title, 'subtitle': subtitle, }); } on PlatformException catch (error) { _lastError = error.message ?? error.code; _setStatus(CastingStatus.error); } } Future disconnect() async { if (!isSupportedOnThisPlatform) { _setStatus(CastingStatus.unavailable); return; } try { await _methodChannel.invokeMethod('disconnect'); } on PlatformException catch (error) { _lastError = error.message ?? error.code; _setStatus(CastingStatus.error); } } Future stopPlayback() async { if (!isSupportedOnThisPlatform) { _setStatus(CastingStatus.unavailable); return; } try { await _methodChannel.invokeMethod('stopCastingPlayback'); } on PlatformException catch (error) { _lastError = error.message ?? error.code; _setStatus(CastingStatus.error); } } void _handleEvent(dynamic event) { if (event is! Map) { return; } final String? status = event['status'] as String?; final String? playbackStatus = event['playbackStatus'] as String?; final String? deviceName = event['deviceName'] as String?; final String? message = event['message'] as String?; if (deviceName != null && deviceName.isNotEmpty) { _connectedDeviceName = deviceName; } if (message != null && message.isNotEmpty) { _lastError = message; } _setPlaybackStatus(_parsePlaybackStatus(playbackStatus)); switch (status) { case 'idle': _setStatus(CastingStatus.idle); break; case 'connecting': _setStatus(CastingStatus.connecting); break; case 'connected': _setStatus(CastingStatus.connected); break; case 'error': _setStatus(CastingStatus.error); break; case 'unavailable': default: _setStatus(CastingStatus.unavailable); break; } } void _setStatus(CastingStatus status) { if (_status == status) { return; } _status = status; notifyListeners(); } CastingPlaybackStatus _parsePlaybackStatus(String? playbackStatus) { switch (playbackStatus) { case 'playing': return CastingPlaybackStatus.playing; case 'buffering': return CastingPlaybackStatus.buffering; case 'stopped': return CastingPlaybackStatus.stopped; default: return CastingPlaybackStatus.unknown; } } void _setPlaybackStatus(CastingPlaybackStatus status) { if (_playbackStatus == status) { return; } _playbackStatus = status; notifyListeners(); } @override void dispose() { _eventSubscription?.cancel(); super.dispose(); } }