import 'dart:async'; import 'dart:io'; import 'dart:ui'; import 'package:extended_image/extended_image.dart'; import 'package:flutter/painting.dart'; import 'package:yumi/app/constants/sc_global_config.dart'; import 'package:yumi/shared/data_sources/sources/local/user_manager.dart'; bool _requiresAuthenticatedImageHeaders(String url) { return url.contains("/external/oss/local/"); } Map? buildNetworkImageHeaders(String url) { if (!_requiresAuthenticatedImageHeaders(url)) { return null; } final uri = Uri.tryParse(url); final headers = { "req-lang": SCGlobalConfig.lang, "X-Forwarded-Proto": "http", "User-Agent": "Dart/3.7.2(dart:io)", "req-imei": SCGlobalConfig.imei, "req-app-intel": "version=${SCGlobalConfig.version};build=${SCGlobalConfig.build};model=${SCGlobalConfig.model};sysVersion=${SCGlobalConfig.sysVersion};channel=${SCGlobalConfig.channel}", "req-client": Platform.isAndroid ? "Android" : "iOS", "req-sys-origin": "origin=${SCGlobalConfig.origin};originChild=${SCGlobalConfig.originChild}", }; if ((uri?.host ?? "").isNotEmpty) { headers["Host"] = uri!.host; } final token = AccountStorage().getToken(); if (token.isNotEmpty) { headers["Authorization"] = "Bearer $token"; } headers.removeWhere((key, value) => value.trim().isEmpty); return headers; } int? _resolveImageProviderCacheDimension(double? logicalSize) { if (logicalSize == null || !logicalSize.isFinite || logicalSize <= 0) { return null; } final devicePixelRatio = PlatformDispatcher.instance.implicitView?.devicePixelRatio ?? 2.0; final maxDimension = SCGlobalConfig.isLowPerformanceDevice ? 1280 : 1920; final pixelSize = (logicalSize * devicePixelRatio).round(); return pixelSize.clamp(1, maxDimension).toInt(); } ImageProvider buildCachedImageProvider( String url, { double? logicalWidth, double? logicalHeight, }) { late final ImageProvider provider; if (url.startsWith("http")) { provider = ExtendedNetworkImageProvider( url, headers: buildNetworkImageHeaders(url), cache: true, cacheMaxAge: const Duration(days: 7), ); } else if (url.startsWith('/')) { provider = FileImage(File(url)); } else { provider = AssetImage(url); } return ResizeImage.resizeIfNeeded( _resolveImageProviderCacheDimension(logicalWidth), _resolveImageProviderCacheDimension(logicalHeight), provider, ); } Future warmImageResource( String resource, { double? logicalWidth, double? logicalHeight, Duration timeout = const Duration(seconds: 6), }) async { if (resource.isEmpty) { return false; } final provider = buildCachedImageProvider( resource, logicalWidth: logicalWidth, logicalHeight: logicalHeight, ); final stream = provider.resolve(ImageConfiguration.empty); final completer = Completer(); late final ImageStreamListener listener; listener = ImageStreamListener( (image, synchronousCall) { if (!completer.isCompleted) { completer.complete(true); } stream.removeListener(listener); }, onError: (Object error, StackTrace? stackTrace) { if (!completer.isCompleted) { completer.complete(false); } stream.removeListener(listener); }, ); stream.addListener(listener); return completer.future.timeout( timeout, onTimeout: () { stream.removeListener(listener); return false; }, ); } Future warmNetworkImage( String url, { double? logicalWidth, double? logicalHeight, Duration timeout = const Duration(seconds: 6), }) { return warmImageResource( url, logicalWidth: logicalWidth, logicalHeight: logicalHeight, timeout: timeout, ); }