|
|
|
@ -25,195 +25,136 @@ import 'package:get_storage/get_storage.dart'; |
|
|
|
import 'extension/my_cupertino_localizations.dart'; |
|
|
|
|
|
|
|
void main() async { |
|
|
|
// 设置全局错误处理 |
|
|
|
FlutterError.onError = (FlutterErrorDetails details) { |
|
|
|
FlutterError.presentError(details); |
|
|
|
if (kReleaseMode) { |
|
|
|
// 在生产环境中,可以将错误发送到错误收集服务 |
|
|
|
print('Flutter Error: ${details.exception}'); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// 处理异步错误 |
|
|
|
PlatformDispatcher.instance.onError = (error, stack) { |
|
|
|
print('Platform Error: $error'); |
|
|
|
print('Stack: $stack'); |
|
|
|
return true; |
|
|
|
}; |
|
|
|
|
|
|
|
try { |
|
|
|
WidgetsFlutterBinding.ensureInitialized(); |
|
|
|
// 初始化GetStorage |
|
|
|
await GetStorage.init(); |
|
|
|
|
|
|
|
// 设置环境配置 - 根据是否为release模式 |
|
|
|
EnvConfig.setEnvironment(Environment.dev); |
|
|
|
|
|
|
|
// 初始化RTC,如果失败也不阻止应用启动 |
|
|
|
try { |
|
|
|
await RTCManager.instance.initialize(appId: '4c2ea9dcb4c5440593a418df0fdd512d'); |
|
|
|
} catch (e) { |
|
|
|
print('RTC初始化失败: $e'); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 注意:IM初始化改为异步,在应用启动后执行,避免阻塞应用启动 |
|
|
|
// IM初始化将在 MyApp 的 initState 中异步执行 |
|
|
|
|
|
|
|
// 初始化全局依赖 |
|
|
|
final networkService = NetworkService(); |
|
|
|
Get.put(networkService); |
|
|
|
Get.put(networkService.userApi); |
|
|
|
Get.put(networkService.homeApi); |
|
|
|
// 初始化全局 Overlay 控制器 |
|
|
|
Get.put(OverlayController()); |
|
|
|
|
|
|
|
SystemChrome.setSystemUIOverlayStyle( |
|
|
|
const SystemUiOverlayStyle( |
|
|
|
statusBarColor: Colors.transparent, |
|
|
|
statusBarIconBrightness: Brightness.dark, |
|
|
|
statusBarBrightness: Brightness.light, |
|
|
|
systemNavigationBarColor: Colors.white, |
|
|
|
systemNavigationBarIconBrightness: Brightness.dark, |
|
|
|
), |
|
|
|
); |
|
|
|
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); |
|
|
|
|
|
|
|
if (Platform.isIOS) { |
|
|
|
SystemChrome.setEnabledSystemUIMode( |
|
|
|
SystemUiMode.manual, |
|
|
|
overlays: [SystemUiOverlay.top], |
|
|
|
); |
|
|
|
} |
|
|
|
} catch (e, stackTrace) { |
|
|
|
print('应用初始化失败: $e'); |
|
|
|
print('堆栈跟踪: $stackTrace'); |
|
|
|
// 即使初始化失败,也尝试启动应用 |
|
|
|
} |
|
|
|
|
|
|
|
// 初始化全局 Overlay 控制器 |
|
|
|
Get.put(OverlayController()); |
|
|
|
// 初始化GetStorage |
|
|
|
await GetStorage.init(); |
|
|
|
|
|
|
|
runApp( |
|
|
|
GetMaterialApp( |
|
|
|
locale: Locale('zh', 'CN'), // 使用GetX的locale |
|
|
|
supportedLocales: const [Locale('zh', 'CN')], |
|
|
|
|
|
|
|
/// 国际化配置 代理 |
|
|
|
localizationsDelegates: const [ |
|
|
|
ChineseArabicMonthDelegate(), |
|
|
|
GlobalMaterialLocalizations.delegate, |
|
|
|
GlobalWidgetsLocalizations.delegate, |
|
|
|
GlobalCupertinoLocalizations.delegate, // iOS |
|
|
|
], |
|
|
|
theme: ThemeData( |
|
|
|
scaffoldBackgroundColor: Colors.white, |
|
|
|
// 设置导航栏样式 |
|
|
|
appBarTheme: AppBarTheme( |
|
|
|
systemOverlayStyle: SystemUiOverlayStyle( |
|
|
|
systemNavigationBarColor: Colors.white, |
|
|
|
systemNavigationBarIconBrightness: Brightness.light, |
|
|
|
ScreenUtilInit( |
|
|
|
designSize: const Size(375, 812), |
|
|
|
minTextAdapt: true, |
|
|
|
builder: (_, child) { |
|
|
|
return GetMaterialApp( |
|
|
|
locale: Locale('zh', 'CN'), // 使用GetX的locale |
|
|
|
supportedLocales: const [Locale('zh', 'CN')], |
|
|
|
|
|
|
|
/// 国际化配置 代理 |
|
|
|
localizationsDelegates: const [ |
|
|
|
ChineseArabicMonthDelegate(), |
|
|
|
GlobalMaterialLocalizations.delegate, |
|
|
|
GlobalWidgetsLocalizations.delegate, |
|
|
|
GlobalCupertinoLocalizations.delegate, // iOS |
|
|
|
], |
|
|
|
theme: ThemeData( |
|
|
|
scaffoldBackgroundColor: Colors.white, |
|
|
|
// 设置导航栏样式 |
|
|
|
appBarTheme: AppBarTheme( |
|
|
|
systemOverlayStyle: SystemUiOverlayStyle( |
|
|
|
systemNavigationBarColor: Colors.white, |
|
|
|
systemNavigationBarIconBrightness: Brightness.light, |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
builder: (context, child) { |
|
|
|
final smartDialogBuilder = FlutterSmartDialog.init(); |
|
|
|
return smartDialogBuilder( |
|
|
|
context, |
|
|
|
Stack( |
|
|
|
children: [ |
|
|
|
child ?? const SizedBox(), |
|
|
|
// 全局 overlay 组件 |
|
|
|
Obx(() { |
|
|
|
try { |
|
|
|
if (Get.isRegistered<OverlayController>()) { |
|
|
|
final overlayController = Get.find<OverlayController>(); |
|
|
|
// 视频通话小窗 |
|
|
|
if (overlayController.showVideoCallOverlay.value) { |
|
|
|
return VideoCallOverlayWidget( |
|
|
|
targetUserId: overlayController.videoCallTargetUserId ?? '', |
|
|
|
targetUserName: overlayController.videoCallTargetUserName, |
|
|
|
targetAvatarUrl: overlayController.videoCallTargetAvatarUrl, |
|
|
|
message: overlayController.videoCallTargetMessage, |
|
|
|
onClose: () { |
|
|
|
overlayController.hideVideoCall(); |
|
|
|
}, |
|
|
|
); |
|
|
|
} |
|
|
|
// 直播房间小窗 |
|
|
|
if (overlayController.showOverlay.value) { |
|
|
|
return DraggableOverlayWidget( |
|
|
|
size: 60, |
|
|
|
backgroundColor: const Color.fromRGBO(0, 0, 0, 0.6), |
|
|
|
onClose: () { |
|
|
|
overlayController.hide(); |
|
|
|
}, |
|
|
|
); |
|
|
|
builder: (context, child) { |
|
|
|
final smartDialogBuilder = FlutterSmartDialog.init(); |
|
|
|
return smartDialogBuilder( |
|
|
|
context, |
|
|
|
Stack( |
|
|
|
children: [ |
|
|
|
child ?? const SizedBox(), |
|
|
|
// 全局 overlay 组件 |
|
|
|
Obx(() { |
|
|
|
try { |
|
|
|
if (Get.isRegistered<OverlayController>()) { |
|
|
|
final overlayController = Get.find<OverlayController>(); |
|
|
|
// 视频通话小窗 |
|
|
|
if (overlayController.showVideoCallOverlay.value) { |
|
|
|
return VideoCallOverlayWidget( |
|
|
|
targetUserId: overlayController.videoCallTargetUserId ?? '', |
|
|
|
targetUserName: overlayController.videoCallTargetUserName, |
|
|
|
targetAvatarUrl: overlayController.videoCallTargetAvatarUrl, |
|
|
|
message: overlayController.videoCallTargetMessage, |
|
|
|
onClose: () { |
|
|
|
overlayController.hideVideoCall(); |
|
|
|
}, |
|
|
|
); |
|
|
|
} |
|
|
|
// 直播房间小窗 |
|
|
|
if (overlayController.showOverlay.value) { |
|
|
|
return DraggableOverlayWidget( |
|
|
|
size: 60, |
|
|
|
backgroundColor: const Color.fromRGBO(0, 0, 0, 0.6), |
|
|
|
onClose: () { |
|
|
|
overlayController.hide(); |
|
|
|
}, |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
Get.log('获取OverlayController失败: $e'); |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
Get.log('获取OverlayController失败: $e'); |
|
|
|
} |
|
|
|
return const SizedBox.shrink(); |
|
|
|
}), |
|
|
|
], |
|
|
|
), |
|
|
|
return const SizedBox.shrink(); |
|
|
|
}), |
|
|
|
], |
|
|
|
), |
|
|
|
); |
|
|
|
}, |
|
|
|
home: Agreement(), |
|
|
|
); |
|
|
|
}, |
|
|
|
home: MyApp(), |
|
|
|
), |
|
|
|
) |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
class MyApp extends StatefulWidget { |
|
|
|
const MyApp({super.key}); |
|
|
|
class Agreement extends StatefulWidget { |
|
|
|
const Agreement({super.key}); |
|
|
|
|
|
|
|
@override |
|
|
|
State<MyApp> createState() => _MyAppState(); |
|
|
|
State<Agreement> createState() => _AgreementState(); |
|
|
|
} |
|
|
|
|
|
|
|
class _MyAppState extends State<MyApp> { |
|
|
|
Fluwx fluwx = Fluwx(); |
|
|
|
bool _screenUtilInitialized = false; |
|
|
|
bool _showAgreementDialog = false; |
|
|
|
class _AgreementState extends State<Agreement> { |
|
|
|
|
|
|
|
bool show = false; |
|
|
|
final storage = GetStorage(); |
|
|
|
|
|
|
|
@override |
|
|
|
void initState() { |
|
|
|
super.initState(); |
|
|
|
_initFluwx(); |
|
|
|
// 异步初始化IM,避免阻塞应用启动 |
|
|
|
_initIMAsync(); |
|
|
|
// 检查是否已同意协议 |
|
|
|
_checkAgreement(); |
|
|
|
} |
|
|
|
|
|
|
|
/// 检查是否已同意用户协议(只在第一次安装时显示) |
|
|
|
void _checkAgreement() { |
|
|
|
final storage = GetStorage(); |
|
|
|
void _checkAgreement() async { |
|
|
|
// 检查是否已经同意过协议,如果已同意则不显示弹框 |
|
|
|
final hasAgreed = storage.read<bool>('hasAgreedUserAgreement') ?? false; |
|
|
|
print(hasAgreed); |
|
|
|
print("hasAgreed"); |
|
|
|
if (!hasAgreed) { |
|
|
|
// 第一次安装,延迟显示弹框,确保UI已初始化 |
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) { |
|
|
|
if (mounted) { |
|
|
|
setState(() { |
|
|
|
_showAgreementDialog = true; |
|
|
|
}); |
|
|
|
} |
|
|
|
show = true; |
|
|
|
setState(() { |
|
|
|
|
|
|
|
}); |
|
|
|
} else { |
|
|
|
// 已经同意过协议,不显示弹框 |
|
|
|
_showAgreementDialog = false; |
|
|
|
await initApp(); |
|
|
|
Get.offAll(() => StartPage(), ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 处理同意协议(保存同意状态,确保下次不再显示) |
|
|
|
void _onAgreeAgreement() { |
|
|
|
final storage = GetStorage(); |
|
|
|
void _onAgreeAgreement() async { |
|
|
|
// 保存用户已同意协议的状态,确保只在第一次安装时显示 |
|
|
|
storage.write('hasAgreedUserAgreement', true); |
|
|
|
if (mounted) { |
|
|
|
setState(() { |
|
|
|
_showAgreementDialog = false; |
|
|
|
}); |
|
|
|
} |
|
|
|
await storage.write('hasAgreedUserAgreement', true); |
|
|
|
final hasAgreed = storage.read<bool>('hasAgreedUserAgreement') ?? false; |
|
|
|
print(hasAgreed); |
|
|
|
print("hasAgreed"); |
|
|
|
await initApp(); |
|
|
|
Get.offAll(() => StartPage(), ); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/// 处理不同意协议 - 退出应用 |
|
|
|
@ -227,6 +168,125 @@ class _MyAppState extends State<MyApp> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
initApp() async { |
|
|
|
// 设置全局错误处理 |
|
|
|
FlutterError.onError = (FlutterErrorDetails details) { |
|
|
|
FlutterError.presentError(details); |
|
|
|
if (kReleaseMode) { |
|
|
|
// 在生产环境中,可以将错误发送到错误收集服务 |
|
|
|
print('Flutter Error: ${details.exception}'); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// 处理异步错误 |
|
|
|
PlatformDispatcher.instance.onError = (error, stack) { |
|
|
|
print('Platform Error: $error'); |
|
|
|
print('Stack: $stack'); |
|
|
|
return true; |
|
|
|
}; |
|
|
|
|
|
|
|
try { |
|
|
|
WidgetsFlutterBinding.ensureInitialized(); |
|
|
|
|
|
|
|
// 设置环境配置 - 根据是否为release模式 |
|
|
|
EnvConfig.setEnvironment(Environment.dev); |
|
|
|
|
|
|
|
// 初始化RTC,如果失败也不阻止应用启动 |
|
|
|
try { |
|
|
|
await RTCManager.instance.initialize(appId: '4c2ea9dcb4c5440593a418df0fdd512d'); |
|
|
|
} catch (e) { |
|
|
|
print('RTC初始化失败: $e'); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 注意:IM初始化改为异步,在应用启动后执行,避免阻塞应用启动 |
|
|
|
// IM初始化将在 MyApp 的 initState 中异步执行 |
|
|
|
|
|
|
|
// 初始化全局依赖 |
|
|
|
final networkService = NetworkService(); |
|
|
|
Get.put(networkService); |
|
|
|
Get.put(networkService.userApi); |
|
|
|
Get.put(networkService.homeApi); |
|
|
|
|
|
|
|
SystemChrome.setSystemUIOverlayStyle( |
|
|
|
const SystemUiOverlayStyle( |
|
|
|
statusBarColor: Colors.transparent, |
|
|
|
statusBarIconBrightness: Brightness.dark, |
|
|
|
statusBarBrightness: Brightness.light, |
|
|
|
systemNavigationBarColor: Colors.white, |
|
|
|
systemNavigationBarIconBrightness: Brightness.dark, |
|
|
|
), |
|
|
|
); |
|
|
|
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); |
|
|
|
|
|
|
|
if (Platform.isIOS) { |
|
|
|
SystemChrome.setEnabledSystemUIMode( |
|
|
|
SystemUiMode.manual, |
|
|
|
overlays: [SystemUiOverlay.top], |
|
|
|
); |
|
|
|
} |
|
|
|
} catch (e, stackTrace) { |
|
|
|
print('应用初始化失败: $e'); |
|
|
|
print('堆栈跟踪: $stackTrace'); |
|
|
|
// 即使初始化失败,也尝试启动应用 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@override |
|
|
|
Widget build(BuildContext context) { |
|
|
|
return Container( |
|
|
|
width: MediaQuery.of(context).size.width, |
|
|
|
height: MediaQuery.of(context).size.height, |
|
|
|
color: Colors.white, |
|
|
|
child: show ? UserAgreementDialog( |
|
|
|
onAgree: _onAgreeAgreement, |
|
|
|
onDisagree: _onDisagreeAgreement, |
|
|
|
) : SizedBox(), |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
class StartPage extends StatefulWidget { |
|
|
|
const StartPage({super.key}); |
|
|
|
|
|
|
|
@override |
|
|
|
State<StartPage> createState() => _StartPageState(); |
|
|
|
} |
|
|
|
|
|
|
|
class _StartPageState extends State<StartPage> { |
|
|
|
|
|
|
|
@override |
|
|
|
void initState() { |
|
|
|
super.initState(); |
|
|
|
} |
|
|
|
|
|
|
|
@override |
|
|
|
Widget build(BuildContext context) { |
|
|
|
return MyApp(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
class MyApp extends StatefulWidget { |
|
|
|
const MyApp({super.key}); |
|
|
|
|
|
|
|
@override |
|
|
|
State<MyApp> createState() => _MyAppState(); |
|
|
|
} |
|
|
|
|
|
|
|
class _MyAppState extends State<MyApp> { |
|
|
|
Fluwx fluwx = Fluwx(); |
|
|
|
// bool _screenUtilInitialized = false; |
|
|
|
bool _showAgreementDialog = false; |
|
|
|
|
|
|
|
@override |
|
|
|
void initState() { |
|
|
|
super.initState(); |
|
|
|
_initFluwx(); |
|
|
|
// 异步初始化IM,避免阻塞应用启动 |
|
|
|
_initIMAsync(); |
|
|
|
} |
|
|
|
|
|
|
|
/// 异步初始化IM SDK,避免阻塞应用启动 |
|
|
|
void _initIMAsync() { |
|
|
|
// 使用 Future.delayed 确保在应用完全启动后再初始化 |
|
|
|
@ -278,10 +338,10 @@ class _MyAppState extends State<MyApp> { |
|
|
|
@override |
|
|
|
Widget build(BuildContext context) { |
|
|
|
// ScreenUtil.init 只应该初始化一次,不应该在每次build时调用 |
|
|
|
if (!_screenUtilInitialized) { |
|
|
|
ScreenUtil.init(context, designSize: const Size(375, 812)); |
|
|
|
_screenUtilInitialized = true; |
|
|
|
} |
|
|
|
// if (!_screenUtilInitialized) { |
|
|
|
// ScreenUtil.init(context, designSize: const Size(375, 812)); |
|
|
|
// _screenUtilInitialized = true; |
|
|
|
// } |
|
|
|
|
|
|
|
// 判断token是否为空 |
|
|
|
final storage = GetStorage(); |
|
|
|
@ -305,10 +365,7 @@ class _MyAppState extends State<MyApp> { |
|
|
|
return Stack( |
|
|
|
children: [ |
|
|
|
homeWidget, |
|
|
|
UserAgreementDialog( |
|
|
|
onAgree: _onAgreeAgreement, |
|
|
|
onDisagree: _onDisagreeAgreement, |
|
|
|
), |
|
|
|
|
|
|
|
], |
|
|
|
); |
|
|
|
} |
|
|
|
|