import 'dart:async'; import 'dart:io' show Platform; import 'dart:ui' show window; import 'package:flutter/widgets.dart'; import 'package:screen_brightness/screen_brightness.dart'; import 'package:screen_state/screen_state.dart'; import 'package:volume_controller/volume_controller.dart'; /// Native runtime observation fields backed by platform plugins. class RuntimeObservationMonitor { final List> _subscriptions; final WidgetsBindingObserver? _metricsObserver; const RuntimeObservationMonitor( this._subscriptions, { WidgetsBindingObserver? metricsObserver, }) : _metricsObserver = metricsObserver; Future dispose() async { for (final subscription in _subscriptions) { await subscription.cancel(); } final metricsObserver = _metricsObserver; if (metricsObserver != null) { try { WidgetsBinding.instance.removeObserver(metricsObserver); } catch (_) {} } try { VolumeController().removeListener(); } catch (_) {} } } Future> collectRuntimeObservationFields() async { return { ..._screenObservationFields(), 'screen_brightness': await _readScreenBrightness(), 'volume': await _readVolume(), 'is_screen_on': _initialScreenOn(), }; } RuntimeObservationMonitor startRuntimeObservationMonitoring( void Function(Map fields) onFields, ) { final subscriptions = >[]; WidgetsBindingObserver? metricsObserver; try { metricsObserver = _MetricsObserver( onMetricsChanged: () => onFields(_screenObservationFields()), ); WidgetsBinding.instance.addObserver(metricsObserver); } catch (_) {} try { subscriptions.add(ScreenBrightness().onCurrentBrightnessChanged.listen( (value) => onFields({ 'screen_brightness': _normalizedPercent(value), }), onError: (_) { onFields({'screen_brightness': -1}); })); } catch (_) {} try { subscriptions.add(VolumeController().listener((value) { onFields({'volume': _normalizedPercent(value)}); })); } catch (_) {} try { if (Platform.isAndroid || Platform.isIOS) { subscriptions.add(Screen().screenStateStream.listen((event) { onFields({'is_screen_on': _screenStateValue(event)}); }, onError: (_) { onFields({'is_screen_on': -1}); })); } } catch (_) {} return RuntimeObservationMonitor( subscriptions, metricsObserver: metricsObserver, ); } Map _screenObservationFields() { try { final size = window.physicalSize; final width = size.width.round(); final height = size.height.round(); final pixelRatio = (window.devicePixelRatio * 100).round(); final dpi = (window.devicePixelRatio * 160).round(); return { 'screen_width': width, 'screen_height': height, 'screen_dpi': dpi, 'device_pixel_ratio': pixelRatio, 'orientation': width >= height ? 'landscape' : 'portrait', }; } catch (_) { return const { 'screen_width': -1, 'screen_height': -1, 'screen_dpi': -1, 'device_pixel_ratio': -1, 'orientation': '', }; } } Future _readScreenBrightness() async { try { return _normalizedPercent(await ScreenBrightness().current); } catch (_) { return -1; } } Future _readVolume() async { try { return _normalizedPercent(await VolumeController().getVolume()); } catch (_) { return -1; } } int _initialScreenOn() { try { return Platform.isAndroid || Platform.isIOS ? 1 : -1; } catch (_) { return -1; } } int _screenStateValue(ScreenStateEvent event) { switch (event) { case ScreenStateEvent.SCREEN_ON: case ScreenStateEvent.SCREEN_UNLOCKED: return 1; case ScreenStateEvent.SCREEN_OFF: return 0; } } int _normalizedPercent(num value) { final numeric = value.toDouble(); if (!numeric.isFinite) return -1; final scaled = numeric <= 1 ? numeric * 100 : numeric; return scaled.clamp(0, 100).round(); } class _MetricsObserver with WidgetsBindingObserver { final void Function() onMetricsChanged; const _MetricsObserver({required this.onMetricsChanged}); @override void didChangeMetrics() { onMetricsChanged(); } }