- 登录:工号+密码走 account-service /api/login,自动登录、被踢下线回登录页 - 消息:会话列表(未读角标/免打扰/时间)、单聊群聊、文字/语音/图片/文件消息、失败重发、历史分页 - 通讯录:新的同事/我的群聊入口、按部门分组(好友 ex 字段)、搜索 - 通话:一对一语音通话(信令走 OpenIM 自定义消息 + LiveKit,token 走 /api/rtc_token) - 界面按确认效果图 m1/m2/m3 实现,主色 #3B87F5,四态视图齐全,无英文残留
114 lines
3.3 KiB
Dart
114 lines
3.3 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;
|
|
try {
|
|
await im.init();
|
|
final credential = await LoginScreen.readCredential();
|
|
if (credential != null) {
|
|
await im.login(userID: credential.$1, token: credential.$2);
|
|
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)),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|