import 'dart:async'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import '../../../common/router/app_router.dart'; import 'auth_controller.dart'; import 'one_key_login_service.dart'; abstract final class LoginLauncher { static final OneKeyLoginService _service = OneKeyLoginService(); static bool _starting = false; static Future start( BuildContext context, WidgetRef ref, { String? next, }) async { if (_starting) return; _starting = true; var loadingVisible = false; void showLoading() { if (loadingVisible || !context.mounted) return; loadingVisible = true; unawaited( showDialog( context: context, useRootNavigator: true, barrierDismissible: false, builder: (_) => const PopScope( canPop: false, child: Center(child: CircularProgressIndicator()), ), ), ); } void dismissLoading() { if (!loadingVisible || !context.mounted) return; loadingVisible = false; Navigator.of(context, rootNavigator: true).pop(); } try { final observer = _OneKeyLoginLifecycleObserver(_service); var observerAdded = false; late final OneKeyLoginOutcome outcome; try { WidgetsBinding.instance.addObserver(observer); observerAdded = true; // Carrier-auth initialization only begins after this explicit login // action. Keep the loading indicator visible while the native SDK // determines whether one-key login can be launched. showLoading(); outcome = await _service.start( context: context, verifyToken: ref.read(authProvider.notifier).verifyOneKeyLogin, // The native carrier page follows the device, while linked H5 pages // retain the appearance selected inside the app. appIsDark: Theme.of(context).brightness == Brightness.dark, nativeIsDark: MediaQuery.platformBrightnessOf(context) == Brightness.dark, onNativeRequestDispatched: dismissLoading, onAuthPageOpened: dismissLoading, ); } finally { dismissLoading(); if (observerAdded) WidgetsBinding.instance.removeObserver(observer); } if (!context.mounted) return; switch (outcome) { case OneKeyLoginOutcome.success: if (next != null && next.isNotEmpty) GoRouter.of(context).go(next); return; case OneKeyLoginOutcome.cancelled: case OneKeyLoginOutcome.busy: return; case OneKeyLoginOutcome.fallback: case OneKeyLoginOutcome.unsupported: await openSmsLogin(context, next: next); } } finally { _starting = false; } } } Future openSmsLogin( BuildContext context, { String? next, Future Function()? onReturn, }) async { if (!context.mounted) return; if (GoRouterState.of(context).matchedLocation == AppRoutes.login) return; final location = next == null || next.isEmpty ? AppRoutes.login : '${AppRoutes.login}?next=${Uri.encodeComponent(next)}'; await GoRouter.of(context).push(location); if (context.mounted) await onReturn?.call(); } class _OneKeyLoginLifecycleObserver with WidgetsBindingObserver { _OneKeyLoginLifecycleObserver(this.service); final OneKeyLoginService service; @override void didChangeAppLifecycleState(AppLifecycleState state) { if (state == AppLifecycleState.resumed) service.onAppResumed(); } }