Files
jinzhi/lib/features/web/application/mall_web_host_controller.dart
T
jingyun 3b089e2205 feat: 对齐常驻商城与七鱼客服交互
- 同步商城 H5 登录态与原生 token
- 完善商城/原生 tab 切换、动画及返回交互
- 修复七鱼客服键盘避让和状态栏恢复
- 调整开屏跳过按钮与登录入口交互
2026-09-07 15:47:01 +08:00

122 lines
3.3 KiB
Dart

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<void> Function()? _clearCache;
Future<void> 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<void> Function() clearer) =>
_clearCache = clearer;
void detachCacheClearer() => _clearCache = null;
Future<void> clear() async {
await _clearCache?.call();
_pendingPreloadUrl = null;
state.value = const MallWebHostState();
}
static Uri uriForTheme(Uri uri, {required bool isDark}) {
final query = Map<String, List<String>>.from(uri.queryParametersAll);
query['isNight'] = [isDark ? '1' : '0'];
return uri.replace(queryParameters: query);
}
void attachSystemBackHandler(Future<void> Function() handler) {
_handleSystemBack = handler;
}
void detachSystemBackHandler() => _handleSystemBack = null;
Future<void> handleSystemBack() =>
_handleSystemBack?.call() ?? Future<void>.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 <String, String>{}
: <String, String>{'isNight': uri.queryParameters['isNight']!},
fragment: '',
)
.toString();
}
}