Files
tongxunruanjian/mobile/lib/main.dart
T
KIMI 3b5c76cac4 fix: 修复手机连不上服务器时启动页无限转圈(B-58)
- 启动页先读本地凭证:无凭证(首次打开)直接进登录页,不再先等 SDK 初始化
- SDK init/login 加 20s 超时;init 用共享 Future 防止超时后重复触发 initSDK
- 登录页对 IM 登录超时给出明确提示(确认手机连着公司内网 WiFi)
- 修复 flutter create 模板遗留的 widget_test(引用不存在的 MyApp),改为登录页渲染冒烟测试
- 版本号 1.0.1+2
2026-08-15 14:54:52 +08:00

120 lines
3.7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'nav.dart';
import 'screens/call_screen.dart';
import 'screens/home_screen.dart';
import 'screens/login_screen.dart';
import 'services/call_service.dart';
import 'services/im_service.dart';
import 'theme.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const ChanglianApp());
}
class ChanglianApp extends StatefulWidget {
const ChanglianApp({super.key});
@override
State<ChanglianApp> createState() => _ChanglianAppState();
}
class _ChanglianAppState extends State<ChanglianApp> {
@override
void initState() {
super.initState();
// 被踢下线 / token 失效:清凭证、回登录页
IMService.instance.onForceLogout = () async {
await LoginScreen.clearCredential();
navigatorKey.currentState?.pushAndRemoveUntil(
MaterialPageRoute(builder: (_) => const LoginScreen()),
(route) => false,
);
};
// 来电时弹出通话界面
CallService.instance.onIncomingCall = () {
navigatorKey.currentState?.push(
MaterialPageRoute(builder: (_) => const CallScreen(), fullscreenDialog: true),
);
};
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '畅联',
navigatorKey: navigatorKey,
theme: buildAppTheme(),
debugShowCheckedModeBanner: false,
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('zh', 'CN')],
locale: const Locale('zh', 'CN'),
home: const SplashGate(),
);
}
}
/// 启动页:初始化 SDK,有本地凭证则自动登录进主页,否则进登录页
class SplashGate extends StatefulWidget {
const SplashGate({super.key});
@override
State<SplashGate> createState() => _SplashGateState();
}
class _SplashGateState extends State<SplashGate> {
@override
void initState() {
super.initState();
_boot();
}
Future<void> _boot() async {
final im = IMService.instance;
// 先读本地凭证:没有凭证(首次打开)直接去登录页,
// 不在启动页等 SDK 初始化——服务器连不上时 initSDK 可能长时间不返回,
// 不能让启动页一直转圈。
final credential = await LoginScreen.readCredential();
if (credential == null) {
if (!mounted) return;
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => const LoginScreen()));
return;
}
try {
await im.init().timeout(const Duration(seconds: 20));
await im.login(userID: credential.$1, token: credential.$2).timeout(const Duration(seconds: 20));
CallService.instance.start();
if (!mounted) return;
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => const HomeScreen()));
return;
} catch (_) {
// 自动登录失败(token 失效、服务器连不上等):清凭证回登录页
await LoginScreen.clearCredential();
}
if (!mounted) return;
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => const LoginScreen()));
}
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('畅联', style: TextStyle(fontSize: 32, fontWeight: FontWeight.w600, color: AppColors.primary)),
SizedBox(height: AppGap.x6),
SizedBox(width: 28, height: 28, child: CircularProgressIndicator(strokeWidth: 3, color: AppColors.primary)),
],
),
),
);
}
}