Files
jinzhi/lib/features/auth/application/login_launcher.dart
T
2026-09-04 11:41:22 +08:00

114 lines
3.5 KiB
Dart

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<void> 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<void>(
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<void> openSmsLogin(
BuildContext context, {
String? next,
Future<void> 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();
}
}