together_mobile/lib/screens/home/home_screen.dart

306 lines
9.8 KiB
Dart
Raw Normal View History

2023-09-09 16:48:47 +08:00
import 'dart:convert';
import 'dart:io';
2023-06-21 17:44:28 +08:00
import 'package:flutter/material.dart';
2023-09-09 16:48:47 +08:00
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:get_it_mixin/get_it_mixin.dart';
2023-06-21 17:44:28 +08:00
import 'package:go_router/go_router.dart';
2023-09-09 16:48:47 +08:00
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';
2023-09-09 16:48:47 +08:00
import 'package:together_mobile/models/route_state_model.dart';
import 'package:together_mobile/models/user_model.dart';
2023-09-09 16:48:47 +08:00
import 'package:together_mobile/models/websocket_model.dart';
import 'package:together_mobile/notification_api.dart';
import 'package:together_mobile/utils/app_dir.dart';
2023-06-21 17:44:28 +08:00
class HomeScreenWithNavBar extends StatefulWidget
with GetItStatefulWidgetMixin {
HomeScreenWithNavBar({
2023-06-21 17:44:28 +08:00
super.key,
2023-09-09 16:48:47 +08:00
required this.notificationAppLaunchDetails,
2023-06-21 17:44:28 +08:00
required this.initIndex,
required this.child,
this.listController,
});
final int initIndex;
final Widget child;
// used to get back to top
final ScrollController? listController;
2023-09-09 16:48:47 +08:00
final NotificationAppLaunchDetails? notificationAppLaunchDetails;
bool get didNotificationLanunchApp =>
notificationAppLaunchDetails?.didNotificationLaunchApp ?? false;
2023-06-21 17:44:28 +08:00
@override
State<HomeScreenWithNavBar> createState() => _HomeScreenWithNavBarState();
}
class _HomeScreenWithNavBarState extends State<HomeScreenWithNavBar>
2023-09-09 16:48:47 +08:00
with GetItStateMixin, WidgetsBindingObserver {
Future<bool> _openBox() async {
if (!getIt.get<UserProfile>().isInitialised) {
List<int> encryptionKeyUint8List = await getEncryptKey();
2023-06-21 17:44:28 +08:00
await Hive.initFlutter(await getBoxDir());
Box<ChatSetting> chatSettingBox =
await Hive.openBox<ChatSetting>('chat_setting');
final openedChats =
chatSettingBox.values.where((element) => element.isOpen);
for (var chatBox in openedChats) {
await Hive.openBox<MessageT>(
'message_${chatBox.contactId}',
encryptionCipher: HiveAesCipher(encryptionKeyUint8List),
compactionStrategy: (entries, deletedEntries) => entries > 200,
);
}
}
return Future(() => true);
}
2023-06-21 17:44:28 +08:00
2023-09-09 16:48:47 +08:00
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<RouteState>().changeVisibility(true);
break;
case AppLifecycleState.inactive:
print('应用处于闲置状态,切换到后台触发====================');
getIt.get<RouteState>().changeVisibility(false);
break;
case AppLifecycleState.paused:
print('应用处于不可见状态========================');
getIt.get<RouteState>().changeVisibility(false);
break;
case AppLifecycleState.detached:
print('当前页面即将退出===================');
getIt.get<RouteState>().changeVisibility(false);
break;
}
}
Future<void> _isAndroidPermissionGranted() async {
if (Platform.isAndroid) {
final bool granted = await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.areNotificationsEnabled() ??
false;
setState(() {
_notificationsEnabled = granted;
});
}
}
Future<void> _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: <Widget>[
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<String, dynamic> 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',
},
);
}
}
});
}
2023-06-21 17:44:28 +08:00
@override
Widget build(BuildContext context) {
int applyCount = watchOnly((ApplyList applyList) => applyList.count);
2023-06-21 17:44:28 +08:00
return Scaffold(
body: widget.child,
bottomNavigationBar: BottomNavigationBar(
backgroundColor:
Theme.of(context).bottomNavigationBarTheme.backgroundColor,
items: [
2023-06-21 17:44:28 +08:00
BottomNavigationBarItem(
icon: FutureBuilder(
future: _openBox(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ValueListenableBuilder(
valueListenable:
Hive.box<ChatSetting>('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,
),
),
2023-09-09 16:48:47 +08:00
position:
badges.BadgePosition.topEnd(top: -8, end: -12),
showBadge: unreadCount > 0,
child: const Icon(Icons.message),
);
},
);
} else {
return const Icon(Icons.message);
}
},
),
2023-06-21 17:44:28 +08:00
label: '消息',
),
BottomNavigationBarItem(
icon: badges.Badge(
badgeContent: Text(
applyCount > 99 ? '$applyCount+' : '$applyCount',
style: TextStyle(
fontSize: 11,
color: Theme.of(context).colorScheme.inversePrimary,
),
),
2023-09-09 16:48:47 +08:00
position: badges.BadgePosition.topStart(top: -8, start: -12),
showBadge: applyCount > 0,
child: const Icon(Icons.contacts),
),
2023-06-21 17:44:28 +08:00
label: '通讯录',
),
const BottomNavigationBarItem(
2023-06-21 17:44:28 +08:00
icon: Icon(Icons.star),
label: '更多',
),
],
onTap: (int index) => _onItemTapped(index, context),
currentIndex: widget.initIndex,
),
);
}
void _onItemTapped(int index, BuildContext context) {
switch (index) {
case 0:
2023-09-09 16:48:47 +08:00
messages.clear();
flutterLocalNotificationsPlugin.cancelAll();
2023-06-21 17:44:28 +08:00
GoRouter.of(context).go('/chat');
break;
case 1:
GoRouter.of(context).go('/contact');
break;
case 2:
GoRouter.of(context).go('/more');
break;
}
}
}