import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:get_it_mixin/get_it_mixin.dart'; import 'package:go_router/go_router.dart'; import 'package:badges/badges.dart' as badges; import 'package:hive_flutter/hive_flutter.dart'; import 'package:together_mobile/database/box_type.dart'; import 'package:together_mobile/database/hive_database.dart'; import 'package:together_mobile/models/apply_list_model.dart'; import 'package:together_mobile/models/init_get_it.dart'; import 'package:together_mobile/models/route_state_model.dart'; import 'package:together_mobile/models/user_model.dart'; import 'package:together_mobile/models/websocket_model.dart'; import 'package:together_mobile/notification_api.dart'; import 'package:together_mobile/utils/app_dir.dart'; class HomeScreenWithNavBar extends StatefulWidget with GetItStatefulWidgetMixin { HomeScreenWithNavBar({ super.key, required this.notificationAppLaunchDetails, required this.initIndex, required this.child, this.listController, }); final int initIndex; final Widget child; // used to get back to top final ScrollController? listController; final NotificationAppLaunchDetails? notificationAppLaunchDetails; bool get didNotificationLanunchApp => notificationAppLaunchDetails?.didNotificationLaunchApp ?? false; @override State createState() => _HomeScreenWithNavBarState(); } class _HomeScreenWithNavBarState extends State with GetItStateMixin, WidgetsBindingObserver { Future _openBox() async { if (!getIt.get().isInitialised) { List encryptionKeyUint8List = await getEncryptKey(); await Hive.initFlutter(await getBoxDir()); Box chatSettingBox = await Hive.openBox('chat_setting'); final openedChats = chatSettingBox.values.where((element) => element.isOpen); for (var chatBox in openedChats) { await Hive.openBox( 'message_${chatBox.contactId}', encryptionCipher: HiveAesCipher(encryptionKeyUint8List), compactionStrategy: (entries, deletedEntries) => entries > 200, ); } } return Future(() => true); } bool _notificationsEnabled = false; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); _isAndroidPermissionGranted(); _requestPermissions(); _configureDidReceiveLocalNotificationSubject(); _configureSelectNotificationSubject(); } @override void dispose() { super.dispose(); WidgetsBinding.instance.removeObserver(this); didReceiveNotificationStream.close(); selectNotificationStream.close(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); switch (state) { case AppLifecycleState.resumed: print('应用进入前台=================='); messages.clear(); flutterLocalNotificationsPlugin.cancelAll(); getIt.get().changeVisibility(true); break; case AppLifecycleState.inactive: print('应用处于闲置状态,切换到后台触发===================='); getIt.get().changeVisibility(false); break; case AppLifecycleState.paused: print('应用处于不可见状态========================'); getIt.get().changeVisibility(false); break; case AppLifecycleState.detached: print('当前页面即将退出==================='); getIt.get().changeVisibility(false); break; } } Future _isAndroidPermissionGranted() async { if (Platform.isAndroid) { final bool granted = await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.areNotificationsEnabled() ?? false; setState(() { _notificationsEnabled = granted; }); } } Future _requestPermissions() async { if (Platform.isIOS || Platform.isMacOS) { await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< IOSFlutterLocalNotificationsPlugin>() ?.requestPermissions( alert: true, badge: true, sound: true, ); await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< MacOSFlutterLocalNotificationsPlugin>() ?.requestPermissions( alert: true, badge: true, sound: true, ); } else if (Platform.isAndroid) { final AndroidFlutterLocalNotificationsPlugin? androidImplementation = flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>(); final bool? grantedNotificationPermission = await androidImplementation?.requestPermission(); setState(() { _notificationsEnabled = grantedNotificationPermission ?? false; }); } } void _configureDidReceiveLocalNotificationSubject() { didReceiveNotificationStream.stream.listen( (ReceivedNotification? receivedNotification) async { await showDialog( context: context, builder: (BuildContext context) => AlertDialog( title: receivedNotification!.title != null ? Text(receivedNotification.title!) : null, content: receivedNotification.body != null ? Text(receivedNotification.body!) : null, actions: [ TextButton( onPressed: () {}, child: const Text('ok'), ), ], ), ); }, ); } void _configureSelectNotificationSubject() { // this listener will be call after taps a notification selectNotificationStream.stream.listen((String? payload) async { messages.clear(); if (payload != null) { Map payloadData = json.decode(payload); switch (payloadData['event']) { case 'friend-message': String friendId = payloadData['friendId']!; context.goNamed( 'Message', queryParameters: { 'friendId': friendId, 'type': '0', }, ); break; case 'group-chat-message': String groupChatId = payloadData['groupChatId']!; context.goNamed( 'Message', queryParameters: { 'groupChatId': groupChatId, 'type': '1', }, ); } } }); } @override Widget build(BuildContext context) { int applyCount = watchOnly((ApplyList applyList) => applyList.count); return Scaffold( body: widget.child, bottomNavigationBar: BottomNavigationBar( backgroundColor: Theme.of(context).bottomNavigationBarTheme.backgroundColor, items: [ BottomNavigationBarItem( icon: FutureBuilder( future: _openBox(), builder: (context, snapshot) { if (snapshot.hasData) { return ValueListenableBuilder( valueListenable: Hive.box('chat_setting').listenable(), builder: (context, chatSettingBox, _) { final values = chatSettingBox.values; int unreadCount = 0; for (var value in values) { unreadCount += value.unreadCount; } return badges.Badge( badgeContent: Text( unreadCount > 99 ? '$unreadCount+' : '$unreadCount', style: TextStyle( fontSize: 11, color: Theme.of(context).colorScheme.inversePrimary, ), ), position: badges.BadgePosition.topEnd(top: -8, end: -12), showBadge: unreadCount > 0, child: const Icon(Icons.message), ); }, ); } else { return const Icon(Icons.message); } }, ), label: '消息', ), BottomNavigationBarItem( icon: badges.Badge( badgeContent: Text( applyCount > 99 ? '$applyCount+' : '$applyCount', style: TextStyle( fontSize: 11, color: Theme.of(context).colorScheme.inversePrimary, ), ), position: badges.BadgePosition.topStart(top: -8, start: -12), showBadge: applyCount > 0, child: const Icon(Icons.contacts), ), label: '通讯录', ), const BottomNavigationBarItem( icon: Icon(Icons.star), label: '更多', ), ], onTap: (int index) => _onItemTapped(index, context), currentIndex: widget.initIndex, ), ); } void _onItemTapped(int index, BuildContext context) { switch (index) { case 0: messages.clear(); flutterLocalNotificationsPlugin.cancelAll(); GoRouter.of(context).go('/chat'); break; case 1: GoRouter.of(context).go('/contact'); break; case 2: GoRouter.of(context).go('/more'); break; } } }