initialize static method
Future<void>
initialize({
- required String url,
- Dio? dio,
- StorageInterface? store,
- String? scheme,
- bool refreshSessionOnAppResume = true,
- bool refreshSessionOnReconnect = true,
Implementation
static Future<void> initialize({
required String url,
Dio? dio,
StorageInterface? store,
/// Native deep-link scheme, for example `myapp`.
///
/// Required for native social authentication. It is sent to Better Auth as
/// `expo-origin` and used as the default OAuth callback scheme.
String? scheme,
/// Refresh the auth stream whenever the app returns to the foreground.
bool refreshSessionOnAppResume = true,
/// Refresh the auth stream whenever the device regains network
/// connectivity (offline -> online transition). Mirrors the Expo
/// online-manager behavior.
bool refreshSessionOnReconnect = true,
}) async {
if (_initialized) return;
baseUrl = url;
appScheme = scheme;
if (store == null && !kIsWeb) {
// Encrypted, chunked cookie persistence by default on native platforms.
storage = SecureStorage();
} else {
storage = store;
}
dioClient =
dio ??
Dio(
BaseOptions(
headers: {
'content-type': 'application/json',
if (!kIsWeb) 'user-agent': 'FlutterBetterAuth/1.0.0',
},
validateStatus: (status) => status != null && status < 300,
),
);
final origin = scheme == null || scheme.isEmpty ? null : '$scheme://';
dioClient.options.headers.putIfAbsent('content-type', () => 'application/json');
if (!kIsWeb) {
dioClient.options.headers.putIfAbsent(
'user-agent',
() => 'FlutterBetterAuth/1.0.0',
);
}
dioClient.options.headers['x-skip-oauth-proxy'] = 'true';
if (origin != null) {
dioClient.options.headers['expo-origin'] = origin;
}
cookieJar = CustomPersistCookieJar(
store: storage,
storage: MemoryStorage(),
);
if (kIsWeb) {
// The browser owns the Cookie header; let it persist/send cookies.
enableWebCredentials(dioClient);
} else {
dioClient.interceptors.add(CookieManager(cookieJar));
}
// Bearer-token fallback (works cross-origin where cookies are blocked).
try {
_bearerToken = await _bearerStore.read(key: _bearerKey);
} catch (_) {
_bearerToken = null;
}
dioClient.interceptors.add(BearerTokenInterceptor());
dioClient.interceptors.add(RemoveNullsInterceptor());
dioClient.interceptors.add(
InterceptorsWrapper(
onResponse: (response, handler) async {
final path = response.requestOptions.path;
if (response.statusCode == 200) {
if (path.contains('/sign-in') ||
path.contains('/sign-up') ||
path.contains('/update-user') ||
path.contains('/change-email') ||
path.contains('/change-password')) {
unawaited(_refreshSession());
} else if (path.contains('/sign-out')) {
await cookieJar.clearFor(Uri.parse(baseUrl));
setBearerToken(null);
_authStreamController.add(null);
}
}
handler.next(response);
},
onError: (error, handler) {
if (error.response?.statusCode == 401) {
_authStreamController.add(null);
}
handler.next(error);
},
),
);
_client = BetterAuthClient(dioClient, baseUrl: baseUrl);
_initialized = true;
if (refreshSessionOnAppResume) {
_lifecycleObserver = _BetterAuthLifecycleObserver();
WidgetsBinding.instance.addObserver(_lifecycleObserver!);
}
if (refreshSessionOnReconnect && !kIsWeb) {
_wasOnline = true;
_connectivitySub = Connectivity().onConnectivityChanged.listen((results) {
final online = results.any((r) => r != ConnectivityResult.none);
// Only refetch on the offline -> online edge, not every change.
if (online && !_wasOnline) {
unawaited(_refreshSession());
}
_wasOnline = online;
});
}
}