import 'package:flutter/foundation.dart'; class MallWebHostState { const MallWebHostState({ this.url, this.visible = false, this.isShowingQiyuPage = false, this.isQiyuKeyboardActive = false, }); final String? url; final bool visible; final bool isShowingQiyuPage; final bool isQiyuKeyboardActive; } /// Process-lifetime presentation state for the resident mall WebView. class MallWebHostController { MallWebHostController._(); static final instance = MallWebHostController._(); final state = ValueNotifier(const MallWebHostState()); String? _pendingPreloadUrl; Future Function()? _clearCache; Future Function()? _handleSystemBack; void open(Uri url) { _pendingPreloadUrl = null; state.value = MallWebHostState(url: url.toString(), visible: true); } void preload(Uri url) { final current = state.value; if (current.visible) { if (current.url == null || cacheKeyFor(current.url!) != cacheKeyFor(url.toString())) { _pendingPreloadUrl = url.toString(); } return; } if (current.url != null && cacheKeyFor(current.url!) == cacheKeyFor(url.toString())) { return; } state.value = MallWebHostState( url: url.toString(), visible: current.visible, ); } void dismiss() { final current = state.value; final pending = _pendingPreloadUrl; _pendingPreloadUrl = null; state.value = MallWebHostState(url: pending ?? current.url); } void setShowingQiyuPage(bool isShowingQiyuPage) { final current = state.value; if (current.isShowingQiyuPage == isShowingQiyuPage) return; state.value = MallWebHostState( url: current.url, visible: current.visible, isShowingQiyuPage: isShowingQiyuPage, isQiyuKeyboardActive: isShowingQiyuPage ? current.isQiyuKeyboardActive : false, ); } void setQiyuKeyboardActive(bool active) { final current = state.value; if (current.isQiyuKeyboardActive == active) return; state.value = MallWebHostState( url: current.url, visible: current.visible, isShowingQiyuPage: current.isShowingQiyuPage, isQiyuKeyboardActive: active, ); } void attachCacheClearer(Future Function() clearer) => _clearCache = clearer; void detachCacheClearer() => _clearCache = null; Future clear() async { await _clearCache?.call(); _pendingPreloadUrl = null; state.value = const MallWebHostState(); } static Uri uriForTheme(Uri uri, {required bool isDark}) { final query = Map>.from(uri.queryParametersAll); query['isNight'] = [isDark ? '1' : '0']; return uri.replace(queryParameters: query); } void attachSystemBackHandler(Future Function() handler) { _handleSystemBack = handler; } void detachSystemBackHandler() => _handleSystemBack = null; Future handleSystemBack() => _handleSystemBack?.call() ?? Future.value(); static String cacheKeyFor(String rawUrl) { final uri = Uri.tryParse(rawUrl); if (uri == null) return rawUrl; return uri .replace( queryParameters: uri.queryParameters['isNight'] == null ? const {} : {'isNight': uri.queryParameters['isNight']!}, fragment: '', ) .toString(); } }