@cometchat/skills 3.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +44 -17
- package/bin/install.js +389 -43
- package/package.json +19 -9
- package/skills/cometchat/SKILL.md +1066 -265
- package/skills/cometchat-android-v5/SKILL.md +120 -0
- package/skills/cometchat-android-v5-components/SKILL.md +430 -0
- package/skills/cometchat-android-v5-core/SKILL.md +468 -0
- package/skills/cometchat-android-v5-customization/SKILL.md +305 -0
- package/skills/cometchat-android-v5-extensions/SKILL.md +124 -0
- package/skills/cometchat-android-v5-features/SKILL.md +130 -0
- package/skills/cometchat-android-v5-placement/SKILL.md +276 -0
- package/skills/cometchat-android-v5-production/SKILL.md +94 -0
- package/skills/cometchat-android-v5-push/SKILL.md +620 -0
- package/skills/cometchat-android-v5-testing/SKILL.md +187 -0
- package/skills/cometchat-android-v5-theming/SKILL.md +166 -0
- package/skills/cometchat-android-v5-troubleshooting/SKILL.md +73 -0
- package/skills/cometchat-android-v6/SKILL.md +107 -0
- package/skills/cometchat-android-v6-builder-settings/SKILL.md +145 -0
- package/skills/cometchat-android-v6-compose-components/SKILL.md +202 -0
- package/skills/cometchat-android-v6-compose-customization/SKILL.md +280 -0
- package/skills/cometchat-android-v6-compose-placement/SKILL.md +213 -0
- package/skills/cometchat-android-v6-compose-theming/SKILL.md +224 -0
- package/skills/cometchat-android-v6-core/SKILL.md +354 -0
- package/skills/cometchat-android-v6-events/SKILL.md +212 -0
- package/skills/cometchat-android-v6-extensions/SKILL.md +191 -0
- package/skills/cometchat-android-v6-features/SKILL.md +130 -0
- package/skills/cometchat-android-v6-kotlin-components/SKILL.md +197 -0
- package/skills/cometchat-android-v6-kotlin-customization/SKILL.md +268 -0
- package/skills/cometchat-android-v6-kotlin-placement/SKILL.md +248 -0
- package/skills/cometchat-android-v6-kotlin-theming/SKILL.md +202 -0
- package/skills/cometchat-android-v6-production/SKILL.md +187 -0
- package/skills/cometchat-android-v6-push/SKILL.md +274 -0
- package/skills/cometchat-android-v6-testing/SKILL.md +279 -0
- package/skills/cometchat-android-v6-troubleshooting/SKILL.md +249 -0
- package/skills/cometchat-angular-components/SKILL.md +706 -0
- package/skills/cometchat-angular-core/SKILL.md +444 -0
- package/skills/cometchat-angular-customization/SKILL.md +424 -0
- package/skills/cometchat-angular-features/SKILL.md +447 -0
- package/skills/cometchat-angular-patterns/SKILL.md +447 -0
- package/skills/cometchat-angular-placement/SKILL.md +685 -0
- package/skills/cometchat-angular-production/SKILL.md +415 -0
- package/skills/cometchat-angular-theming/SKILL.md +365 -0
- package/skills/cometchat-angular-troubleshooting/SKILL.md +329 -0
- package/skills/cometchat-astro-patterns/SKILL.md +3 -3
- package/skills/cometchat-components/SKILL.md +11 -0
- package/skills/cometchat-core/SKILL.md +11 -6
- package/skills/cometchat-customization/references/component-catalog.md +1 -1
- package/skills/cometchat-features/SKILL.md +176 -108
- package/skills/cometchat-flutter-v5/SKILL.md +167 -0
- package/skills/cometchat-flutter-v5-calls/SKILL.md +192 -0
- package/skills/cometchat-flutter-v5-conversations/SKILL.md +117 -0
- package/skills/cometchat-flutter-v5-core/SKILL.md +221 -0
- package/skills/cometchat-flutter-v5-customization/SKILL.md +227 -0
- package/skills/cometchat-flutter-v5-events/SKILL.md +155 -0
- package/skills/cometchat-flutter-v5-messages/SKILL.md +258 -0
- package/skills/cometchat-flutter-v5-production/SKILL.md +207 -0
- package/skills/cometchat-flutter-v5-push/SKILL.md +284 -0
- package/skills/cometchat-flutter-v5-theming/SKILL.md +160 -0
- package/skills/cometchat-flutter-v5-troubleshooting/SKILL.md +129 -0
- package/skills/cometchat-flutter-v5-users-groups/SKILL.md +151 -0
- package/skills/cometchat-flutter-v6/SKILL.md +445 -0
- package/skills/cometchat-flutter-v6-calls/SKILL.md +333 -0
- package/skills/cometchat-flutter-v6-components/SKILL.md +365 -0
- package/skills/cometchat-flutter-v6-conversations/SKILL.md +210 -0
- package/skills/cometchat-flutter-v6-core/SKILL.md +305 -0
- package/skills/cometchat-flutter-v6-customization/SKILL.md +666 -0
- package/skills/cometchat-flutter-v6-events/SKILL.md +216 -0
- package/skills/cometchat-flutter-v6-features/SKILL.md +406 -0
- package/skills/cometchat-flutter-v6-messages/SKILL.md +333 -0
- package/skills/cometchat-flutter-v6-migration/SKILL.md +381 -0
- package/skills/cometchat-flutter-v6-placement/SKILL.md +398 -0
- package/skills/cometchat-flutter-v6-production/SKILL.md +632 -0
- package/skills/cometchat-flutter-v6-theming/SKILL.md +260 -0
- package/skills/cometchat-flutter-v6-troubleshooting/SKILL.md +726 -0
- package/skills/cometchat-flutter-v6-users-groups/SKILL.md +216 -0
- package/skills/cometchat-ios/SKILL.md +323 -0
- package/skills/cometchat-ios-components/SKILL.md +651 -0
- package/skills/cometchat-ios-core/SKILL.md +822 -0
- package/skills/cometchat-ios-customization/SKILL.md +825 -0
- package/skills/cometchat-ios-features/SKILL.md +725 -0
- package/skills/cometchat-ios-placement/SKILL.md +750 -0
- package/skills/cometchat-ios-production/SKILL.md +730 -0
- package/skills/cometchat-ios-push/SKILL.md +582 -0
- package/skills/cometchat-ios-theming/SKILL.md +631 -0
- package/skills/cometchat-ios-troubleshooting/SKILL.md +983 -0
- package/skills/cometchat-native-bare-patterns/SKILL.md +470 -0
- package/skills/cometchat-native-components/SKILL.md +594 -0
- package/skills/cometchat-native-core/SKILL.md +407 -0
- package/skills/cometchat-native-customization/SKILL.md +594 -0
- package/skills/cometchat-native-expo-patterns/SKILL.md +485 -0
- package/skills/cometchat-native-features/SKILL.md +608 -0
- package/skills/cometchat-native-placement/SKILL.md +500 -0
- package/skills/cometchat-native-production/SKILL.md +694 -0
- package/skills/cometchat-native-push/SKILL.md +593 -0
- package/skills/cometchat-native-testing/SKILL.md +583 -0
- package/skills/cometchat-native-theming/SKILL.md +656 -0
- package/skills/cometchat-native-troubleshooting/SKILL.md +433 -0
- package/skills/cometchat-nextjs-patterns/SKILL.md +5 -5
- package/skills/cometchat-production/SKILL.md +60 -0
- package/skills/cometchat-react-router-patterns/SKILL.md +23 -21
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cometchat-flutter-v5-production
|
|
3
|
+
description: "Use when preparing a CometChat Flutter UIKit v5 app for production. Covers auth tokens, ProGuard, environment config, security hardening."
|
|
4
|
+
license: "MIT"
|
|
5
|
+
compatibility: "cometchat_chat_uikit ^5.2.14; cometchat_calls_uikit ^5.0.15"
|
|
6
|
+
allowed-tools: "executeBash, readFile, fileSearch, listDirectory"
|
|
7
|
+
metadata:
|
|
8
|
+
author: "CometChat"
|
|
9
|
+
version: "3.0.0"
|
|
10
|
+
tags: "cometchat flutter v5 production security auth tokens proguard environment"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# CometChat Flutter UIKit v5 — Production
|
|
14
|
+
|
|
15
|
+
Hardening a CometChat Flutter app for production deployment.
|
|
16
|
+
|
|
17
|
+
## Auth Tokens vs Auth Key
|
|
18
|
+
|
|
19
|
+
**Development:** `CometChatUIKit.login(uid)` uses `authKey` from `UIKitSettingsBuilder`. This is convenient but insecure — the authKey is embedded in the app binary.
|
|
20
|
+
|
|
21
|
+
**Production:** Use server-minted auth tokens via `CometChatUIKit.loginWithAuthToken(authToken)`:
|
|
22
|
+
|
|
23
|
+
```dart
|
|
24
|
+
// 1. Your backend generates an auth token via CometChat REST API
|
|
25
|
+
// POST https://{appId}.api-{region}.cometchat.io/v3/users/{uid}/auth_tokens
|
|
26
|
+
// Header: apiKey: YOUR_API_KEY
|
|
27
|
+
|
|
28
|
+
// 2. Your Flutter app receives the token and logs in
|
|
29
|
+
CometChatUIKit.loginWithAuthToken(authToken,
|
|
30
|
+
onSuccess: (user) { ... },
|
|
31
|
+
onError: (e) { ... },
|
|
32
|
+
);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This keeps the API key on your server, never in the client.
|
|
36
|
+
|
|
37
|
+
## Environment Configuration
|
|
38
|
+
|
|
39
|
+
Store credentials outside source code:
|
|
40
|
+
|
|
41
|
+
```dart
|
|
42
|
+
class AppCredentials {
|
|
43
|
+
static String _appId = '';
|
|
44
|
+
static String _authKey = '';
|
|
45
|
+
static String _region = '';
|
|
46
|
+
|
|
47
|
+
// Load from SharedPreferences, environment, or remote config
|
|
48
|
+
static String get appId => _appId.isEmpty
|
|
49
|
+
? SharedPreferencesClass.getString('appId')
|
|
50
|
+
: _appId;
|
|
51
|
+
|
|
52
|
+
static Future<void> setAppId(String value) async {
|
|
53
|
+
await SharedPreferencesClass.setString('appId', value);
|
|
54
|
+
_appId = value;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The master app supports QR code scanning to load credentials dynamically (`CometChatQRScreen`).
|
|
60
|
+
|
|
61
|
+
## Android Build Requirements
|
|
62
|
+
|
|
63
|
+
### gradle.properties
|
|
64
|
+
|
|
65
|
+
```properties
|
|
66
|
+
android.useAndroidX=true
|
|
67
|
+
android.enableJetifier=true
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### minSdk 26
|
|
71
|
+
|
|
72
|
+
```groovy
|
|
73
|
+
// android/app/build.gradle
|
|
74
|
+
defaultConfig {
|
|
75
|
+
minSdk 26 // Required by cometchat_calls_sdk
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### ProGuard / R8 Keep Rules
|
|
80
|
+
|
|
81
|
+
Create `android/app/proguard-rules.pro`:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
-keep class com.cometchat.** { *; }
|
|
85
|
+
-keep interface com.cometchat.** { *; }
|
|
86
|
+
-dontwarn com.cometchat.calls.**
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Reference in `build.gradle`:
|
|
90
|
+
|
|
91
|
+
```groovy
|
|
92
|
+
buildTypes {
|
|
93
|
+
release {
|
|
94
|
+
minifyEnabled true
|
|
95
|
+
shrinkResources true
|
|
96
|
+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Signing
|
|
102
|
+
|
|
103
|
+
```groovy
|
|
104
|
+
signingConfigs {
|
|
105
|
+
release {
|
|
106
|
+
storeFile file('your_keystore.jks')
|
|
107
|
+
storePassword 'STORE_PASSWORD'
|
|
108
|
+
keyAlias 'KEY_ALIAS'
|
|
109
|
+
keyPassword 'KEY_PASSWORD'
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## iOS Build Requirements
|
|
115
|
+
|
|
116
|
+
### Info.plist Permissions
|
|
117
|
+
|
|
118
|
+
```xml
|
|
119
|
+
<key>NSCameraUsageDescription</key>
|
|
120
|
+
<string>Camera access for video calls and photo sharing</string>
|
|
121
|
+
<key>NSMicrophoneUsageDescription</key>
|
|
122
|
+
<string>Microphone access for voice and video calls</string>
|
|
123
|
+
<key>NSPhotoLibraryUsageDescription</key>
|
|
124
|
+
<string>Photo library access for sharing images</string>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Background Modes (for VoIP)
|
|
128
|
+
|
|
129
|
+
Enable in Xcode: Background Modes → Voice over IP, Remote notifications, Background fetch.
|
|
130
|
+
|
|
131
|
+
### Podfile
|
|
132
|
+
|
|
133
|
+
Ensure minimum iOS deployment target matches CometChat requirements.
|
|
134
|
+
|
|
135
|
+
## Firebase Setup
|
|
136
|
+
|
|
137
|
+
```dart
|
|
138
|
+
// In main(), before runApp:
|
|
139
|
+
await Firebase.initializeApp(
|
|
140
|
+
options: DefaultFirebaseOptions.currentPlatform,
|
|
141
|
+
);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Crashlytics
|
|
145
|
+
|
|
146
|
+
```dart
|
|
147
|
+
FlutterError.onError = (errorDetails) {
|
|
148
|
+
FirebaseCrashlytics.instance.recordFlutterFatalError(errorDetails);
|
|
149
|
+
};
|
|
150
|
+
PlatformDispatcher.instance.onError = (error, stack) {
|
|
151
|
+
FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
|
|
152
|
+
return true;
|
|
153
|
+
};
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Push Notification Provider IDs
|
|
157
|
+
|
|
158
|
+
Configure provider IDs for FCM and APNs in the CometChat dashboard:
|
|
159
|
+
|
|
160
|
+
```dart
|
|
161
|
+
static String get fcmProviderId => 'your-fcm-provider-id';
|
|
162
|
+
static String get apnProviderId => 'your-apn-provider-id';
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
These must match what's configured in the CometChat dashboard under Notifications → Push Notifications.
|
|
166
|
+
|
|
167
|
+
## Demo Meta Info
|
|
168
|
+
|
|
169
|
+
For internal tracking (optional):
|
|
170
|
+
|
|
171
|
+
```dart
|
|
172
|
+
CometChat.setDemoMetaInfo(jsonObject: {
|
|
173
|
+
"name": "flutter-sample-app",
|
|
174
|
+
"type": "sample",
|
|
175
|
+
"version": "5.2.11",
|
|
176
|
+
"platform": "flutter",
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Logout Flow
|
|
181
|
+
|
|
182
|
+
```dart
|
|
183
|
+
// 1. Unregister push tokens
|
|
184
|
+
PNRegistry.unregisterPNService();
|
|
185
|
+
|
|
186
|
+
// 2. Sign out from Firebase/Google (if applicable)
|
|
187
|
+
await FirebaseAuth.instance.signOut();
|
|
188
|
+
await GoogleSignIn().signOut();
|
|
189
|
+
|
|
190
|
+
// 3. Logout from CometChat
|
|
191
|
+
await CometChatUIKit.logout(
|
|
192
|
+
onSuccess: (_) { /* navigate to login */ },
|
|
193
|
+
onError: (e) { /* show error */ },
|
|
194
|
+
);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Checklist — Production
|
|
198
|
+
|
|
199
|
+
- [ ] Auth tokens minted server-side, not authKey in client
|
|
200
|
+
- [ ] Credentials not hardcoded in source (use SharedPreferences or remote config)
|
|
201
|
+
- [ ] ProGuard rules added for release builds
|
|
202
|
+
- [ ] minSdk 26 set
|
|
203
|
+
- [ ] iOS permissions in Info.plist
|
|
204
|
+
- [ ] Firebase Crashlytics configured
|
|
205
|
+
- [ ] Push notification provider IDs match CometChat dashboard
|
|
206
|
+
- [ ] Logout unregisters push tokens before CometChat logout
|
|
207
|
+
- [ ] Signing config set for release builds
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cometchat-flutter-v5-push
|
|
3
|
+
description: "Use when implementing push notifications with CometChat Flutter UIKit v5. Covers FCM (Android), APNs (iOS), VoIP calls, token lifecycle, local notifications, and tap-to-navigate."
|
|
4
|
+
license: "MIT"
|
|
5
|
+
compatibility: "cometchat_chat_uikit ^5.2.14; cometchat_calls_uikit ^5.0.15; firebase_messaging; flutter_local_notifications; flutter_callkit_incoming"
|
|
6
|
+
allowed-tools: "executeBash, readFile, fileSearch, listDirectory"
|
|
7
|
+
metadata:
|
|
8
|
+
author: "CometChat"
|
|
9
|
+
version: "3.0.0"
|
|
10
|
+
tags: "cometchat flutter v5 push notifications fcm apns voip callkit firebase"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# CometChat Flutter UIKit v5 — Push Notifications
|
|
14
|
+
|
|
15
|
+
Push notification setup for Android (FCM) and iOS (APNs + VoIP).
|
|
16
|
+
|
|
17
|
+
## Dependencies
|
|
18
|
+
|
|
19
|
+
```yaml
|
|
20
|
+
dependencies:
|
|
21
|
+
firebase_core: ^3.9.0
|
|
22
|
+
firebase_messaging: ^15.1.6
|
|
23
|
+
flutter_local_notifications: ^18.0.0
|
|
24
|
+
flutter_callkit_incoming: # for VoIP call notifications
|
|
25
|
+
app_badge_plus: ^1.2.6 # badge count
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Architecture Overview
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
notifications/
|
|
32
|
+
├── models/
|
|
33
|
+
│ ├── payload.dart # PayloadData model for parsing FCM data
|
|
34
|
+
│ ├── call_action.dart # CallAction enum (initiated, cancelled, unanswered)
|
|
35
|
+
│ ├── call_type.dart # CallType enum (audio, video)
|
|
36
|
+
│ └── notification_message_type.dart # Message type constants
|
|
37
|
+
├── services/
|
|
38
|
+
│ ├── android_notification_service/
|
|
39
|
+
│ │ ├── firebase_services.dart # FCM init, listeners, token management
|
|
40
|
+
│ │ ├── local_notification_handler.dart # Local notification display + tap handling
|
|
41
|
+
│ │ ├── voip_notification_handler.dart # VoIP call display, accept, decline
|
|
42
|
+
│ │ └── notification_launch_handler.dart # Terminated state launch handling
|
|
43
|
+
│ ├── iOS_notification_service/
|
|
44
|
+
│ │ └── apns_services.dart # APNs connector, VoIP token, CallKit
|
|
45
|
+
│ └── cometchat_service/
|
|
46
|
+
│ └── cometchat_services.dart # PNRegistry (token registration/unregistration)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Token Registration — `CometChatNotifications.registerPushToken`
|
|
50
|
+
|
|
51
|
+
The kit's only public push surface is `CometChatNotifications.registerPushToken(platform, {providerId, fcmToken, deviceToken, voipToken, onSuccess, onError})` (and `unregisterPushToken({onSuccess, onError})`). The sample app wraps this in an extension named `PNRegistry on CometChatService` (see `sample_app_push_notifications/lib/notifications/services/cometchat_service/cometchat_services.dart`) that picks the right provider ID + platform constant for FCM-Android / FCM-iOS / APNs / APNs-VoIP. **Copy that helper into your project, or call `CometChatNotifications.registerPushToken` directly** — `PNRegistry` is a sample-app extension, not importable from any cometchat package.
|
|
52
|
+
|
|
53
|
+
```dart
|
|
54
|
+
// Direct kit API:
|
|
55
|
+
import 'package:cometchat_chat_uikit/cometchat_chat_uikit.dart';
|
|
56
|
+
|
|
57
|
+
await CometChatNotifications.registerPushToken(
|
|
58
|
+
PushPlatforms.FCM_FLUTTER_ANDROID, // platform — first positional arg
|
|
59
|
+
providerId: fcmProviderId, // dashboard FCM provider ID
|
|
60
|
+
fcmToken: token, // use fcmToken / deviceToken / voipToken depending on platform
|
|
61
|
+
onSuccess: (response) => debugPrint('registered: $response'),
|
|
62
|
+
onError: (e) => debugPrint('register failed: $e'),
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// On logout:
|
|
66
|
+
await CometChatNotifications.unregisterPushToken(
|
|
67
|
+
onSuccess: (_) {},
|
|
68
|
+
onError: (e) => debugPrint('unregister failed: $e'),
|
|
69
|
+
);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```dart
|
|
73
|
+
// Or use the sample-app PNRegistry helper after copying it into your project:
|
|
74
|
+
PNRegistry.registerPNService(token, true, false); // (token, isFcm, isVoip)
|
|
75
|
+
PNRegistry.unregisterPNService();
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Platform mapping:
|
|
79
|
+
- FCM Android → `PushPlatforms.FCM_FLUTTER_ANDROID`
|
|
80
|
+
- FCM iOS → `PushPlatforms.FCM_FLUTTER_IOS`
|
|
81
|
+
- APNs Device → `PushPlatforms.APNS_FLUTTER_DEVICE`
|
|
82
|
+
- APNs VoIP → `PushPlatforms.APNS_FLUTTER_VOIP`
|
|
83
|
+
|
|
84
|
+
Provider IDs come from `AppCredentials.fcmProviderId` / `AppCredentials.apnProviderId` (your own constants — these are dashboard-configured values, not kit exports).
|
|
85
|
+
|
|
86
|
+
The remaining examples below assume you've copied `PNRegistry` from the sample app. If you call `CometChatNotifications.registerPushToken` directly, swap the call sites accordingly.
|
|
87
|
+
|
|
88
|
+
## Android — FCM Setup
|
|
89
|
+
|
|
90
|
+
### 1. Background handler (must be top-level function)
|
|
91
|
+
|
|
92
|
+
```dart
|
|
93
|
+
@pragma('vm:entry-point')
|
|
94
|
+
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage rMessage) async {
|
|
95
|
+
LocalNotificationService.showNotification(rMessage.data, rMessage, "", false);
|
|
96
|
+
await VoipNotificationHandler.displayIncomingCall(rMessage);
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 2. Initialize in dashboard/home screen
|
|
101
|
+
|
|
102
|
+
```dart
|
|
103
|
+
class FirebaseService {
|
|
104
|
+
Future<void> init(BuildContext context) async {
|
|
105
|
+
_firebaseMessaging = FirebaseMessaging.instance;
|
|
106
|
+
await requestPermissions();
|
|
107
|
+
await initListeners(context);
|
|
108
|
+
|
|
109
|
+
String? token = await _firebaseMessaging.getToken();
|
|
110
|
+
if (token != null) {
|
|
111
|
+
PNRegistry.registerPNService(token, true, false);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 3. Listener setup
|
|
118
|
+
|
|
119
|
+
```dart
|
|
120
|
+
// Background messages
|
|
121
|
+
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
|
|
122
|
+
|
|
123
|
+
// Token refresh
|
|
124
|
+
_firebaseMessaging.onTokenRefresh.listen((token) {
|
|
125
|
+
PNRegistry.registerPNService(token, true, false);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Foreground messages
|
|
129
|
+
FirebaseMessaging.onMessage.listen((message) {
|
|
130
|
+
LocalNotificationService.showNotification(message.data, message, conversationId, isAgentic);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Tap from background
|
|
134
|
+
FirebaseMessaging.onMessageOpenedApp.listen((message) {
|
|
135
|
+
openNotification(context, message, conversationId);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Tap from terminated state
|
|
139
|
+
FirebaseMessaging.instance.getInitialMessage().then((message) {
|
|
140
|
+
if (message != null) openNotification(context, message, conversationId);
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## iOS — APNs Setup
|
|
145
|
+
|
|
146
|
+
```dart
|
|
147
|
+
final _connector = ApnsPushConnector();
|
|
148
|
+
_connector.shouldPresent = (x) => Future.value(false);
|
|
149
|
+
|
|
150
|
+
_connector.configure(
|
|
151
|
+
onLaunch: (message) async { openNotification(message, context, ""); },
|
|
152
|
+
onResume: (message) async { openNotification(message, context, conversationId); },
|
|
153
|
+
onMessage: (message) async { _showNotification(message.data, message, conversationId, isAgentic); },
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
_connector.requestNotificationPermissions();
|
|
157
|
+
|
|
158
|
+
// APNs device token
|
|
159
|
+
_connector.token.addListener(() {
|
|
160
|
+
PNRegistry.registerPNService(_connector.token.value!, false, false);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// VoIP token
|
|
164
|
+
FlutterCallkitIncoming.getDevicePushTokenVoIP().then((voipToken) {
|
|
165
|
+
PNRegistry.registerPNService(voipToken, false, true);
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## VoIP Call Notifications
|
|
170
|
+
|
|
171
|
+
### Display incoming call (both platforms)
|
|
172
|
+
|
|
173
|
+
```dart
|
|
174
|
+
static Future<void> displayIncomingCall(RemoteMessage rMessage) async {
|
|
175
|
+
PayloadData callPayload = PayloadData.fromJson(rMessage.data);
|
|
176
|
+
if (callPayload.type == 'call' && callPayload.callAction == CallAction.initiated) {
|
|
177
|
+
CallKitParams params = CallKitParams(
|
|
178
|
+
id: callPayload.sessionId,
|
|
179
|
+
nameCaller: callPayload.senderName,
|
|
180
|
+
type: (callPayload.callType == CallType.audio) ? 0 : 1,
|
|
181
|
+
duration: 45000,
|
|
182
|
+
);
|
|
183
|
+
await FlutterCallkitIncoming.showCallkitIncoming(params);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Accept/Decline via CallKit events
|
|
189
|
+
|
|
190
|
+
```dart
|
|
191
|
+
FlutterCallkitIncoming.onEvent.listen((CallEvent? callEvent) {
|
|
192
|
+
switch (callEvent?.event) {
|
|
193
|
+
case Event.actionCallAccept:
|
|
194
|
+
VoipNotificationHandler.acceptVoipCall(callEvent, context);
|
|
195
|
+
break;
|
|
196
|
+
case Event.actionCallDecline:
|
|
197
|
+
VoipNotificationHandler.declineVoipCall(callEvent);
|
|
198
|
+
break;
|
|
199
|
+
case Event.actionCallTimeout:
|
|
200
|
+
case Event.actionCallEnded:
|
|
201
|
+
VoipNotificationHandler.endCall(sessionId: callEvent?.body['id']);
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Local Notification Display
|
|
208
|
+
|
|
209
|
+
Uses `flutter_local_notifications` with inbox-style grouping per conversation:
|
|
210
|
+
|
|
211
|
+
```dart
|
|
212
|
+
// Skip if user is viewing the same conversation
|
|
213
|
+
if (conversationId == notifConversationId) return;
|
|
214
|
+
|
|
215
|
+
// Skip call-type notifications (handled by CallKit)
|
|
216
|
+
if (data["type"] == "call") return;
|
|
217
|
+
|
|
218
|
+
// Show with stable ID per conversation (replaces previous)
|
|
219
|
+
final notificationId = conversationId.hashCode;
|
|
220
|
+
await flutterLocalNotificationsPlugin.show(notificationId, title, body, details, payload: jsonPayload);
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Tap-to-Navigate
|
|
224
|
+
|
|
225
|
+
```dart
|
|
226
|
+
static void handleNotificationTap(NotificationResponse? response) async {
|
|
227
|
+
if (response?.payload != null) {
|
|
228
|
+
final body = jsonDecode(response!.payload!);
|
|
229
|
+
NotificationDataModel model = NotificationDataModel.fromJson(body);
|
|
230
|
+
|
|
231
|
+
User? user; Group? group;
|
|
232
|
+
if (model.receiverType == "user") {
|
|
233
|
+
user = await CometChat.getUser(model.sender);
|
|
234
|
+
} else {
|
|
235
|
+
group = await CometChat.getGroup(model.receiver);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (model.type == "chat" && (user != null || group != null)) {
|
|
239
|
+
Navigator.of(CallNavigationContext.navigatorKey.currentContext!).push(
|
|
240
|
+
MaterialPageRoute(builder: (_) => MessagesSample(user: user, group: group)),
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Terminated State Handling
|
|
248
|
+
|
|
249
|
+
```dart
|
|
250
|
+
// In main()
|
|
251
|
+
final launchDetails = await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
|
|
252
|
+
if (launchDetails?.didNotificationLaunchApp == true) {
|
|
253
|
+
NotificationLaunchHandler.pendingNotificationResponse = launchDetails!.notificationResponse;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// In dashboard initState()
|
|
257
|
+
Future.delayed(Duration(milliseconds: 300), () {
|
|
258
|
+
final response = NotificationLaunchHandler.pendingNotificationResponse;
|
|
259
|
+
if (response != null) {
|
|
260
|
+
NotificationLaunchHandler.pendingNotificationResponse = null;
|
|
261
|
+
LocalNotificationService.handleNotificationTap(response, isTerminatedState: true);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Logout — Unregister Token
|
|
267
|
+
|
|
268
|
+
```dart
|
|
269
|
+
PNRegistry.unregisterPNService();
|
|
270
|
+
// Then: CometChatUIKit.logout(...)
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Checklist — Push Notifications
|
|
274
|
+
|
|
275
|
+
- [ ] Firebase initialized before CometChat init
|
|
276
|
+
- [ ] FCM token registered via `PNRegistry.registerPNService(token, true, false)`
|
|
277
|
+
- [ ] APNs device + VoIP tokens registered on iOS
|
|
278
|
+
- [ ] Background handler is top-level `@pragma('vm:entry-point')` function
|
|
279
|
+
- [ ] Token refresh listener re-registers token
|
|
280
|
+
- [ ] Local notifications skip current active conversation
|
|
281
|
+
- [ ] Call notifications handled via `FlutterCallkitIncoming`, not local notifications
|
|
282
|
+
- [ ] Tap-to-navigate uses `CallNavigationContext.navigatorKey.currentContext`
|
|
283
|
+
- [ ] Tokens unregistered on logout via `PNRegistry.unregisterPNService()`
|
|
284
|
+
- [ ] Terminated state launch handled via `NotificationLaunchHandler`
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cometchat-flutter-v5-theming
|
|
3
|
+
description: "Use when customizing the visual appearance of CometChat Flutter UIKit v5 components. Triggers on CometChatThemeHelper, CometChatColorPalette, CometChatSpacing, CometChatTypography, Style classes, merge()."
|
|
4
|
+
license: "MIT"
|
|
5
|
+
compatibility: "cometchat_uikit_shared ^5.2.3; flutter >=2.5.0"
|
|
6
|
+
allowed-tools: "executeBash, readFile, fileSearch, listDirectory"
|
|
7
|
+
metadata:
|
|
8
|
+
author: "CometChat"
|
|
9
|
+
version: "3.0.0"
|
|
10
|
+
tags: "cometchat flutter v5 theme colors typography spacing dark-mode styling"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# CometChat Flutter UIKit v5 — Theming & Styling
|
|
14
|
+
|
|
15
|
+
How to customize the visual appearance of all CometChat v5 components.
|
|
16
|
+
|
|
17
|
+
## Theme System Architecture
|
|
18
|
+
|
|
19
|
+
Three layers, resolved via Flutter's `ThemeExtension` system:
|
|
20
|
+
|
|
21
|
+
1. `CometChatColorPalette` — colors (primary, neutral, alert, background, text, icon, button, border)
|
|
22
|
+
2. `CometChatSpacing` — spacing, padding, margin, and radius tokens
|
|
23
|
+
3. `CometChatTypography` — text styles (heading1-4, body, caption1-2, button, link, title)
|
|
24
|
+
|
|
25
|
+
Access via static helpers:
|
|
26
|
+
```dart
|
|
27
|
+
final colors = CometChatThemeHelper.getColorPalette(context);
|
|
28
|
+
final spacing = CometChatThemeHelper.getSpacing(context);
|
|
29
|
+
final typography = CometChatThemeHelper.getTypography(context);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Important:** `getColorPalette()` creates a new object every call, resolving each token via `Theme.of(context)`. Cache in `didChangeDependencies()`, never call in `build()`.
|
|
33
|
+
|
|
34
|
+
## Applying a Custom Theme
|
|
35
|
+
|
|
36
|
+
Register `CometChatColorPalette` as a `ThemeExtension` on your `ThemeData`:
|
|
37
|
+
|
|
38
|
+
```dart
|
|
39
|
+
// ✅ CORRECT
|
|
40
|
+
MaterialApp(
|
|
41
|
+
theme: ThemeData(
|
|
42
|
+
extensions: [
|
|
43
|
+
CometChatColorPalette(
|
|
44
|
+
primary: const Color(0xFF6852D6),
|
|
45
|
+
textPrimary: const Color(0xFF141414),
|
|
46
|
+
textSecondary: const Color(0xFF727272),
|
|
47
|
+
),
|
|
48
|
+
],
|
|
49
|
+
),
|
|
50
|
+
darkTheme: ThemeData(
|
|
51
|
+
extensions: [
|
|
52
|
+
CometChatColorPalette(
|
|
53
|
+
primary: const Color(0xFF604CC3),
|
|
54
|
+
textPrimary: const Color(0xFFFFFFFF),
|
|
55
|
+
textSecondary: const Color(0xFFA0A0A0),
|
|
56
|
+
),
|
|
57
|
+
],
|
|
58
|
+
),
|
|
59
|
+
)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Dark Mode
|
|
63
|
+
|
|
64
|
+
```dart
|
|
65
|
+
CometChatThemeMode.mode = ThemeMode.dark; // Force dark
|
|
66
|
+
CometChatThemeMode.mode = ThemeMode.system; // Follow system
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Commonly Used Color Tokens
|
|
70
|
+
|
|
71
|
+
| Token | Description | Light Default | Dark Default |
|
|
72
|
+
|-------|-------------|---------------|--------------|
|
|
73
|
+
| `primary` | Brand color | `#6852D6` | `#604CC3` |
|
|
74
|
+
| `background1`–`4` | Surface backgrounds | neutral50→neutral300 | neutral50→neutral300 |
|
|
75
|
+
| `textPrimary` | Main text | neutral900 | neutral900 |
|
|
76
|
+
| `textSecondary` | Secondary text | neutral600 | neutral600 |
|
|
77
|
+
| `borderLight` | Subtle borders | neutral200 | neutral200 |
|
|
78
|
+
| `iconPrimary` | Main icons | neutral900 | neutral900 |
|
|
79
|
+
| `iconSecondary` | Secondary icons | neutral500 | neutral500 |
|
|
80
|
+
| `iconHighlight` | Highlighted icons | primary | primary |
|
|
81
|
+
| `error` | Error states | `#F44649` | `#C73C3E` |
|
|
82
|
+
| `white` / `black` | Fixed (NOT brightness-aware) | `Colors.white` / `Colors.black` | Same |
|
|
83
|
+
| `transparent` | Transparent | `Colors.transparent` | Same |
|
|
84
|
+
| `neutral600` | Used for date styles | — | — |
|
|
85
|
+
|
|
86
|
+
Extended primary shades (`extendedPrimary50`–`900`) are auto-generated from `primary`. Override individually if needed.
|
|
87
|
+
|
|
88
|
+
## Commonly Used Spacing Tokens
|
|
89
|
+
|
|
90
|
+
| Token | Default |
|
|
91
|
+
|-------|---------|
|
|
92
|
+
| `padding` / `spacing` / `radius` | 2 |
|
|
93
|
+
| `padding1` / `spacing1` / `radius1` | 4 |
|
|
94
|
+
| `padding2` / `spacing2` / `radius2` | 8 |
|
|
95
|
+
| `padding3` / `spacing3` / `radius3` | 12 |
|
|
96
|
+
| `padding4` / `spacing4` / `radius4` | 16 |
|
|
97
|
+
| `padding5` / `spacing5` / `radius5` | 20 |
|
|
98
|
+
| `padding6` / `spacing6` | 24 |
|
|
99
|
+
| `radiusMax` / `spacingMax` | 1000 |
|
|
100
|
+
|
|
101
|
+
## Typography Tokens
|
|
102
|
+
|
|
103
|
+
`CometChatTypography` provides: `heading1`–`heading4`, `body`, `caption1`, `caption2`, `button`, `link`, `title`.
|
|
104
|
+
|
|
105
|
+
Each has `.bold`, `.medium`, `.regular` variants:
|
|
106
|
+
```dart
|
|
107
|
+
typography.body?.regular?.fontSize
|
|
108
|
+
typography.heading2?.bold?.fontWeight
|
|
109
|
+
typography.caption1?.regular?.fontFamily
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Theme Caching Pattern (from package source)
|
|
113
|
+
|
|
114
|
+
All UIKit components cache theme unconditionally in `didChangeDependencies()`:
|
|
115
|
+
|
|
116
|
+
```dart
|
|
117
|
+
// ✅ CORRECT — matches actual package pattern
|
|
118
|
+
@override
|
|
119
|
+
void didChangeDependencies() {
|
|
120
|
+
super.didChangeDependencies();
|
|
121
|
+
colorPalette = CometChatThemeHelper.getColorPalette(context);
|
|
122
|
+
spacing = CometChatThemeHelper.getSpacing(context);
|
|
123
|
+
typography = CometChatThemeHelper.getTypography(context);
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Do NOT use a `_themeInitialized` flag — it prevents theme updates on system theme changes.
|
|
128
|
+
|
|
129
|
+
## Component Style Classes — merge() Pattern
|
|
130
|
+
|
|
131
|
+
Every component has a `CometChat{Component}Style` extending `ThemeExtension`. Resolved internally:
|
|
132
|
+
|
|
133
|
+
```dart
|
|
134
|
+
// Inside component's didChangeDependencies():
|
|
135
|
+
style = CometChatThemeHelper.getTheme<CometChatConversationsStyle>(
|
|
136
|
+
context: context,
|
|
137
|
+
defaultTheme: CometChatConversationsStyle.of,
|
|
138
|
+
).merge(widget.conversationsStyle);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Pass overrides via constructor:
|
|
142
|
+
```dart
|
|
143
|
+
CometChatConversations(
|
|
144
|
+
conversationsStyle: CometChatConversationsStyle(
|
|
145
|
+
backgroundColor: Colors.black,
|
|
146
|
+
),
|
|
147
|
+
)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Gotchas
|
|
151
|
+
|
|
152
|
+
- `colorPalette.white` / `black` / `transparent` are NOT brightness-aware — use `neutral50` for brightness-aware white
|
|
153
|
+
- Extended primary shades auto-generated from `primary` by blending with white (light) or black (dark)
|
|
154
|
+
|
|
155
|
+
## Checklist — Theming
|
|
156
|
+
|
|
157
|
+
- [ ] Colors from `CometChatThemeHelper.getColorPalette(context)`, never hardcoded
|
|
158
|
+
- [ ] Theme cached in `didChangeDependencies()`, not `build()`
|
|
159
|
+
- [ ] Custom theme registered as `ThemeExtension` on both `theme` and `darkTheme`
|
|
160
|
+
- [ ] Component styles passed via constructor props
|