import 'package:flutter/material.dart'; const double roomGameDesignWidth = 750; double resolveRoomGameAvailableHeight( BuildContext context, { double reservedTop = 12, }) { final mediaQuery = MediaQuery.maybeOf(context); if (mediaQuery == null) { return 0; } final bottomInset = mediaQuery.viewInsets.bottom > mediaQuery.padding.bottom ? mediaQuery.viewInsets.bottom : mediaQuery.padding.bottom; final availableHeight = mediaQuery.size.height - mediaQuery.padding.top - bottomInset - reservedTop; if (!availableHeight.isFinite || availableHeight <= 0) { return mediaQuery.size.height; } return availableHeight.clamp(0.0, mediaQuery.size.height).toDouble(); } double clampRoomGameHeight( double value, double maxHeight, { double minHeight = 0, }) { if (!value.isFinite || value <= 0) { return maxHeight > 0 ? maxHeight : 0; } if (!maxHeight.isFinite || maxHeight <= 0) { return value; } if (minHeight > maxHeight) { return maxHeight; } return value.clamp(minHeight, maxHeight).toDouble(); } String resolveRoomGameScreenMode( Map launchParams, { required bool fullScreen, }) { final candidates = [ launchParams['screenMode']?.toString() ?? '', launchParams['screen_mode']?.toString() ?? '', launchParams['displayMode']?.toString() ?? '', launchParams['gameScreenMode']?.toString() ?? '', ]; for (final candidate in candidates) { final trimmed = candidate.trim(); if (trimmed.isNotEmpty) { return trimmed.toLowerCase(); } } return fullScreen ? 'full' : 'half'; } Map buildRoomGameViewportPayload( BuildContext context, { required double containerHeight, double? dialogHeight, double headerHeight = 0, double topCrop = 0, required String screenMode, required int safeHeight, double designWidth = roomGameDesignWidth, double? designHeight, double? actualWebViewHeight, }) { final mediaQuery = MediaQuery.maybeOf(context); final size = mediaQuery?.size ?? Size.zero; final padding = mediaQuery?.padding ?? EdgeInsets.zero; final viewInsets = mediaQuery?.viewInsets ?? EdgeInsets.zero; final targetDesignHeight = designHeight ?? (safeHeight > 0 ? safeHeight.toDouble() : null); final expectedHeight = targetDesignHeight != null && size.width > 0 && designWidth > 0 ? size.width * targetDesignHeight / designWidth : null; final availableHeight = resolveRoomGameAvailableHeight(context); return { 'containerWidth': _roundMetric(size.width), 'containerHeight': _roundMetric(containerHeight), 'visibleHeight': _roundMetric(containerHeight), 'webViewHeight': _roundMetric(actualWebViewHeight ?? containerHeight), 'dialogHeight': _roundMetric( dialogHeight ?? containerHeight + headerHeight, ), 'screenHeight': _roundMetric(size.height), 'headerHeight': _roundMetric(headerHeight), 'topCrop': _roundMetric(topCrop), 'safeAreaTop': _roundMetric(padding.top), 'safeAreaBottom': _roundMetric(padding.bottom), 'viewInsetBottom': _roundMetric(viewInsets.bottom), 'availableHeight': _roundMetric(availableHeight), 'devicePixelRatio': _roundMetric(mediaQuery?.devicePixelRatio ?? 1), 'screenMode': screenMode, 'isCompactViewport': expectedHeight != null ? containerHeight + 1 < expectedHeight : false, 'designWidth': _roundMetric(designWidth), if (targetDesignHeight != null) 'designHeight': _roundMetric(targetDesignHeight), 'safeHeight': safeHeight, }; } Map mergeRoomGameViewportPayload( Map payload, Map viewport, ) { if (viewport.isEmpty) { return payload; } return { ...payload, 'viewport': viewport, 'nativeViewport': viewport, 'containerWidth': viewport['containerWidth'], 'containerHeight': viewport['containerHeight'], 'visibleHeight': viewport['visibleHeight'], 'webViewHeight': viewport['webViewHeight'], 'dialogHeight': viewport['dialogHeight'], 'safeAreaTop': viewport['safeAreaTop'], 'safeAreaBottom': viewport['safeAreaBottom'], 'devicePixelRatio': viewport['devicePixelRatio'], 'screenMode': viewport['screenMode'], 'isCompactViewport': viewport['isCompactViewport'], }; } double _roundMetric(double value) { if (!value.isFinite) { return 0; } return double.parse(value.toStringAsFixed(2)); }