Render startup immediately and defer native work with a smooth home transition

This commit is contained in:
Codex
2026-10-14 17:26:24 +08:00
parent bb7a7a2e58
commit ce21cdc10c
17 changed files with 641 additions and 144 deletions
+70 -69
View File
@@ -21,78 +21,79 @@ enum SchoolConnectionState {
}
class SchoolSession extends ChangeNotifier {
SchoolSession() {
web = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(
NavigationDelegate(
onNavigationRequest: (request) async {
final uri = Uri.tryParse(request.url);
final upgrade = uri == null || !request.isMainFrame
? null
: SchoolNavigationPolicy.secureIdentityRedirect(uri);
if (upgrade != null) {
final generation = _pageGeneration;
try {
await web.loadRequest(upgrade);
} catch (_) {
if (generation == _pageGeneration) {
_failure('学校身份认证暂时无法打开,请重试。');
}
}
return NavigationDecision.prevent;
}
if (uri == null ||
!SchoolNavigationPolicy.canNavigate(
uri,
isMainFrame: request.isMainFrame,
)) {
if (kDebugMode && request.isMainFrame) {
debugPrint(
'Blocked school navigation origin: ${uri?.scheme}://${uri?.host}',
);
}
if (request.isMainFrame) _failure('学校跳转到了其他地址,请返回学校认证入口重试。');
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
onPageStarted: (_) {
_poll?.cancel();
_manualRead = false;
_pageGeneration++;
_reader.invalidate();
pendingDraft = null;
pendingExams = null;
if (!hasOpened || _routeClosed) return;
_loading = true;
notifyListeners();
},
onPageFinished: _loaded,
onWebResourceError: (error) async {
if (error.isForMainFrame != true || error.url == null) return;
SchoolSession();
// Creating Android WebView starts its browser engine. Do it on school access,
// not while the student is opening an offline timetable.
late final WebViewController web = _createWeb();
WebViewController _createWeb() => WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(
NavigationDelegate(
onNavigationRequest: (request) async {
final uri = Uri.tryParse(request.url);
final upgrade = uri == null || !request.isMainFrame
? null
: SchoolNavigationPolicy.secureIdentityRedirect(uri);
if (upgrade != null) {
final generation = _pageGeneration;
final current = await web.currentUrl();
if (hasOpened &&
generation == _pageGeneration &&
error.url == current) {
_failure('学校页面暂时未能加载,请检查网络后重试。');
try {
await web.loadRequest(upgrade);
} catch (_) {
if (generation == _pageGeneration) {
_failure('学校身份认证暂时无法打开,请重试。');
}
}
},
onHttpError: (error) async {
final generation = _pageGeneration;
final current = Uri.tryParse(await web.currentUrl() ?? '');
if (hasOpened &&
generation == _pageGeneration &&
error.request != null &&
error.request!.uri == current) {
_failure('学校请求未完成,请稍后重试。');
return NavigationDecision.prevent;
}
if (uri == null ||
!SchoolNavigationPolicy.canNavigate(
uri,
isMainFrame: request.isMainFrame,
)) {
if (kDebugMode && request.isMainFrame) {
debugPrint(
'Blocked school navigation origin: ${uri?.scheme}://${uri?.host}',
);
}
},
),
);
}
late final WebViewController web;
if (request.isMainFrame) _failure('学校跳转到了其他地址,请返回学校认证入口重试。');
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
onPageStarted: (_) {
_poll?.cancel();
_manualRead = false;
_pageGeneration++;
_reader.invalidate();
pendingDraft = null;
pendingExams = null;
if (!hasOpened || _routeClosed) return;
_loading = true;
notifyListeners();
},
onPageFinished: _loaded,
onWebResourceError: (error) async {
if (error.isForMainFrame != true || error.url == null) return;
final generation = _pageGeneration;
final current = await web.currentUrl();
if (hasOpened &&
generation == _pageGeneration &&
error.url == current) {
_failure('学校页面暂时未能加载,请检查网络后重试。');
}
},
onHttpError: (error) async {
final generation = _pageGeneration;
final current = Uri.tryParse(await web.currentUrl() ?? '');
if (hasOpened &&
generation == _pageGeneration &&
error.request != null &&
error.request!.uri == current) {
_failure('学校请求未完成,请稍后重试。');
}
},
),
);
final _reader = SchoolReader(SchoolPageAdapter());
final _flow = SchoolLoginFlow();
Timer? _timeout;