1
0
forked from baron/baron-sso

Flutter Web WASM 빌드 오류 수정 및 라이브러리 마이그레이션

This commit is contained in:
2026-02-19 15:12:32 +09:00
parent 86f3e7a21c
commit 466e7f1e54
9 changed files with 51 additions and 379 deletions

View File

@@ -1,5 +1,5 @@
import 'locale_storage_stub.dart'
if (dart.library.html) 'locale_storage_web.dart';
if (dart.library.js_interop) 'locale_storage_web.dart';
abstract class LocaleStorage {
static String? read() => localeStorage.read();

View File

@@ -1,6 +1,6 @@
// ignore_for_file: avoid_web_libraries_in_flutter
import 'dart:html' as html;
import 'package:web/web.dart' as web;
import 'package:flutter/foundation.dart';
class LocaleStorageImpl {
@@ -26,11 +26,11 @@ class LocaleStorageImpl {
String? _read(String key) {
if (!_forceMemory && !_forceSession) {
try {
return html.window.localStorage[key];
return web.window.localStorage.getItem(key);
} catch (_) {
// localStorage 접근이 차단된 경우 sessionStorage로 fallback.
try {
return html.window.sessionStorage[key];
return web.window.sessionStorage.getItem(key);
} catch (_) {
// sessionStorage도 차단된 경우 메모리 fallback 사용.
}
@@ -38,7 +38,7 @@ class LocaleStorageImpl {
}
if (!_forceMemory) {
try {
return html.window.sessionStorage[key];
return web.window.sessionStorage.getItem(key);
} catch (_) {
// sessionStorage도 차단된 경우 메모리 fallback 사용.
}
@@ -49,12 +49,12 @@ class LocaleStorageImpl {
void _write(String key, String value) {
if (!_forceMemory && !_forceSession) {
try {
html.window.localStorage[key] = value;
web.window.localStorage.setItem(key, value);
return;
} catch (_) {
// localStorage 접근이 차단된 경우 sessionStorage로 fallback.
try {
html.window.sessionStorage[key] = value;
web.window.sessionStorage.setItem(key, value);
return;
} catch (_) {
// sessionStorage도 차단된 경우 메모리 fallback 사용.
@@ -63,7 +63,7 @@ class LocaleStorageImpl {
}
if (!_forceMemory) {
try {
html.window.sessionStorage[key] = value;
web.window.sessionStorage.setItem(key, value);
return;
} catch (_) {
// sessionStorage도 차단된 경우 메모리 fallback 사용.
@@ -75,12 +75,12 @@ class LocaleStorageImpl {
void _remove(String key) {
if (!_forceMemory && !_forceSession) {
try {
html.window.localStorage.remove(key);
web.window.localStorage.removeItem(key);
return;
} catch (_) {
// localStorage 접근이 차단된 경우 sessionStorage로 fallback.
try {
html.window.sessionStorage.remove(key);
web.window.sessionStorage.removeItem(key);
return;
} catch (_) {
// sessionStorage도 차단된 경우 메모리 fallback 사용.
@@ -89,7 +89,7 @@ class LocaleStorageImpl {
}
if (!_forceMemory) {
try {
html.window.sessionStorage.remove(key);
web.window.sessionStorage.removeItem(key);
return;
} catch (_) {
// sessionStorage도 차단된 경우 메모리 fallback 사용.

View File

@@ -1,5 +1,5 @@
import 'auth_token_store_stub.dart'
if (dart.library.html) 'auth_token_store_web.dart';
if (dart.library.js_interop) 'auth_token_store_web.dart';
class AuthTokenStore {
static String? getToken() => authTokenStore.getToken();

View File

@@ -1,5 +1,5 @@
import 'web_auth_integration_stub.dart'
if (dart.library.html) 'web_auth_integration_web.dart';
if (dart.library.js_interop) 'web_auth_integration_web.dart';
abstract class WebAuthIntegration {
static void sendLoginSuccess(String token) {

View File

@@ -1,8 +1,10 @@
// ignore_for_file: avoid_web_libraries_in_flutter, deprecated_member_use
import 'dart:async';
import 'dart:html' as html;
import 'dart:convert';
import 'package:web/web.dart' as web;
import 'package:flutter/foundation.dart';
import 'dart:js_interop';
import 'auth_token_store.dart';
void implSendLoginSuccess(String token) {
@@ -11,7 +13,7 @@ void implSendLoginSuccess(String token) {
effectiveToken = AuthTokenStore.getToken() ?? "";
}
final fullUrl = html.window.location.href;
final fullUrl = web.window.location.href;
final uri = Uri.base;
// Try to find redirect_uri from standard parsing first, then manual string search
@@ -21,8 +23,8 @@ void implSendLoginSuccess(String token) {
if (redirectUri == null) {
// Manual fallback for cases where Uri.base misses params
final searchParams = html.window.location.search;
if (searchParams != null && searchParams.isNotEmpty) {
final searchParams = web.window.location.search;
if (searchParams.isNotEmpty) {
final sUri = Uri.parse(
'?${searchParams.startsWith('?') ? searchParams.substring(1) : searchParams}',
);
@@ -56,16 +58,18 @@ void implSendLoginSuccess(String token) {
final finalUri = target.replace(queryParameters: query);
debugPrint('Redirecting to: ${finalUri.toString()}');
html.window.location.href = finalUri.toString();
web.window.location.href = finalUri.toString();
return;
}
final message = {'type': 'LOGIN_SUCCESS', 'token': effectiveToken};
final opener = html.window.opener;
final opener = web.window.opener;
if (opener != null) {
try {
opener.postMessage(message, '*');
// Use JSON string for safer cross-origin/WASM messaging if direct object fails
final jsonMsg = jsonEncode(message);
(opener as web.Window).postMessage(jsonMsg.toJS, '*'.toJS);
debugPrint('Sent login success message to opener');
} catch (e) {
debugPrint('Failed to postMessage: $e');
@@ -74,7 +78,7 @@ void implSendLoginSuccess(String token) {
// Close the popup after a short delay to ensure message sending
Timer(const Duration(milliseconds: 500), () {
try {
html.window.close();
web.window.close();
} catch (e) {
debugPrint('Failed to close window: $e');
}
@@ -84,9 +88,9 @@ void implSendLoginSuccess(String token) {
// No opener and no redirect: fall back to local navigation
debugPrint('No opener found. Redirecting to /.');
html.window.location.href = '/';
web.window.location.href = '/';
}
bool implIsPopup() {
return html.window.opener != null;
return web.window.opener != null;
}

View File

@@ -1,49 +1,17 @@
// ignore_for_file: avoid_web_libraries_in_flutter, deprecated_member_use
import 'dart:html' as html;
import 'package:web/web.dart' as web;
import 'package:flutter/foundation.dart';
// ignore_for_file: avoid_web_libraries_in_flutter
import 'dart:async';
import 'dart:js_interop';
@JS('window')
external _JSWindow get _window;
@JS('document')
external _JSDocument get _document;
@JS()
extension type _JSWindow(JSObject _) implements JSObject {
external void alert(JSString message);
external void close();
external JSObject? get opener;
external _JSLocation get location;
}
@JS()
extension type _JSDocument(JSObject _) implements JSObject {
external set title(JSString value);
}
@JS()
extension type _JSOpener(JSObject _) implements JSObject {
external _JSLocation get location;
}
@JS()
extension type _JSLocation(JSObject _) implements JSObject {
external set href(JSString value);
}
class WebWindow {
void setTitle(String title) {
try {
_document.title = title.toJS;
web.document.title = title;
} catch (_) {}
}
void redirectTo(String url) {
final currentHref = html.window.location.href;
final currentHref = web.window.location.href;
Uri? targetUri;
try {
targetUri = Uri.parse(url);
@@ -51,67 +19,55 @@ class WebWindow {
debugPrint("[WebWindow] redirectTo parse failed: url=$url");
}
final currentPort = int.tryParse(html.window.location.port);
final sameOrigin =
targetUri != null &&
targetUri.scheme == html.window.location.protocol.replaceAll(':', '') &&
targetUri.host == html.window.location.hostname &&
(!targetUri.hasPort || targetUri.port == currentPort);
debugPrint(
"[WebWindow] redirectTo start: current=$currentHref, target=$url, target_host=${targetUri?.host ?? ''}, target_path=${targetUri?.path ?? ''}, same_origin=$sameOrigin",
"[WebWindow] redirectTo start: current=$currentHref, target=$url",
);
print("[WebWindow] FINAL REDIRECT ATTEMPT. URL: $url");
// Explicitly use the href setter on the window.location object.
// This is the most standard-compliant way for JS Interop in WASM.
// Most direct and safe way for WASM: location.href assignment via package:web
Future.delayed(Duration.zero, () {
try {
print("[WebWindow] Executing JS href assignment for: $url");
_window.location.href = url.toJS;
web.window.location.href = url;
} catch (e) {
print("[WebWindow] CRITICAL JS ERROR: $e");
}
});
// 이동이 차단되거나 즉시 원위치되는 경우를 추적하기 위한 후속 로그입니다.
// Check after delay
Future<void>.delayed(const Duration(milliseconds: 800), () {
final nowHref = html.window.location.href;
final nowHref = web.window.location.href;
if (nowHref == currentHref) {
debugPrint(
"[WebWindow] redirectTo no-op detected: current URL did not change after navigation attempt",
);
} else {
debugPrint(
"[WebWindow] redirectTo post-check: location changed to $nowHref",
"[WebWindow] redirectTo no-op detected: current URL did not change",
);
}
});
}
String currentHref() {
return html.window.location.href;
return web.window.location.href;
}
String currentSearch() {
return html.window.location.search ?? '';
return web.window.location.search;
}
void alert(String message) {
try {
_window.alert(message.toJS);
web.window.alert(message);
} catch (_) {}
}
void close() {
try {
_window.close();
web.window.close();
} catch (_) {}
}
bool hasOpener() {
try {
return _window.opener != null;
return web.window.opener != null;
} catch (_) {
return false;
}
@@ -119,9 +75,11 @@ class WebWindow {
bool redirectOpenerTo(String url) {
try {
final opener = _window.opener;
final opener = web.window.opener;
if (opener == null) return false;
(_JSOpener(opener)).location.href = url.toJS;
// In package:web, Window is not directly accessible from JSObject opener
// This is a known tricky part for WASM. We'll use a safer approach.
(opener as web.Window).location.href = url;
return true;
} catch (_) {
return false;