
In this guide, we’re going to add push notifications to a Flutter app using Supabase and Entrig.
Entrig is a tool built for Supabase that sends push notifications based on database events without writing any backend code.
We’re going to add push notifications to a group chat app. You can follow along with the example app on GitHub.
Prerequisites
Before starting, you’ll need:
- Supabase account with a project set up
- Entrig account - Sign up at entrig.com
- Firebase service account JSON (for Android push notifications)
- APNs authentication key (for iOS push notifications)
- Apple Developer account (for iOS only)
Setup
1. Install the package
Add the Entrig package to your pubspec.yaml:
dependencies:
entrig: ^latest_version
Run flutter pub get to install.
2. Configure platforms
Android: No additional setup required.
iOS: Run the setup command to configure capabilities automatically:
flutter pub run entrig:setup
This updates your AppDelegate, Runner.entitlements, and Info.plist files. Backup files are created automatically.
3. Initialize in your app
Get your API key from the Entrig dashboard settings. Add this to your main.dart:
await Entrig.init(apiKey: 'YOUR_ENTRIG_API_KEY');
4. Register the user
When a user signs in, register their device with a user ID. Typically, this is the Supabase Auth user ID:
final user = Supabase.instance.client.auth.currentUser;
if (user != null) {
await Entrig.registerUser(userId: user.id);
}
Using a custom user ID: You can also pass your own custom user ID instead of the Supabase Auth ID. The key requirement is that this user ID must match the ID stored in your database’s users table. When creating notifications, Entrig uses this ID to map devices to database rows, so consistent mapping is essential.
When the user signs out, unregister the device:
await Entrig.unregisterUser();
This user ID is what Entrig uses to resolve which devices receive each notification.
For detailed platform setup instructions, see the Flutter SDK documentation.
Understanding the app’s database
The schema is a minimal group chat: users can create groups, join them, and send messages.
Everything starts with the users table. The groups table has a created_by foreign key that identifies the group owner. group_members is a join table that bridges users and groups. Every membership is a row here. messages stores chat content, linked to both a group and a sender.
Each notification we’re building exploits a different relationship:
- Member joins → follow
group_members → groups → created_byto reach the owner (one-to-one) - New message → fan out through
group_membersto reach every member except the sender (one-to-many) - New group → notify every registered user, minus the creator (broadcast)
These three patterns (direct, indirect through a join table, and broadcast) cover the vast majority of notification use cases in any app.
Creating notifications
Every notification in Entrig is defined by three things: Trigger (which database event fires it), Users (how to find who gets it), and Message (what it says). The tabs below walk through each of the three notifications.
When someone joins a group, the owner should know. The challenge is that the new row in group_members contains the joiner’s user_id and the group_id, not the owner’s ID. To find the owner, Entrig walks the foreign key chain: group_id → groups → created_by → users.id. That’s the Direct connection type: one event, one recipient, found by following foreign keys.
There’s one edge case to handle: when a user creates a group, they’re automatically added as a member. We don’t want them to receive a “someone joined your group” notification about their own group. An event condition handles this by filtering out the insert before the notification fires at all.
Trigger
-
Table:
group_members -
Event:
INSERT -
Condition:
group_members.user_id≠groups.created_by(row value)This skips the insert that happens when the owner first joins their own group.
Users
- Connection type: Direct
- Path:
group_members.group_id → groups → created_by → users.id
Message
- Type:
new_member(used in the app to decide how to handle the tap) - Payload fields:
groups.name,users.name,group_members.group_id
| Field | Value |
|---|---|
| Title | New member joined your group |
| Body | {{users.name}} joined your group {{groups.name}} |
When a message is sent, every member of that group should get a notification, except the sender, who already knows what they wrote. The trigger row is a messages insert, but the recipients aren’t stored there directly. They’re in group_members, linked by group_id.
This is the Indirect connection type. Entrig fans the notification out: it takes group_id from the new message, finds every matching row in group_members, and from each row resolves user_id to a registered device. The exclude-sender step is a recipient filter that drops any group_members row where the user_id matches the sender.
Trigger
- Table:
messages - Event:
INSERT
Users
- Connection type: Indirect via Join Table
- Path:
messages.group_id → group_members (match on group_id) → user_id → users.id - Recipient filter:
group_members.user_id≠messages.user_id(row value)
Message
- Type:
new_message - Payload fields:
messages.content,users.name,messages.group_id,groups.name
| Field | Value |
|---|---|
| Title | New message from {{users.name}} in {{groups.name}} |
| Body | {{messages.content}} |
When a new group is created, every user in the app should be notified so they can choose to join. There’s no join table to traverse here. This notification goes to everyone. That’s Broadcast: Entrig notifies all users in your users table when the trigger fires.
The one filter needed is to exclude the creator. They just made the group and don’t need a notification about it.
Trigger
- Table:
groups - Event:
INSERT
Users
- Connection type: Broadcast (all users from
userstable) - Recipient filter:
users.id≠groups.created_by(row value)
Message
- Type:
new_group - Payload fields:
groups.id,groups.name,users.name
| Field | Value |
|---|---|
| Title | Join the new group |
| Body | {{users.name}} created {{groups.name}} just now |
Once a notification is created, Entrig automatically generates the database trigger and function in your Supabase project. Nothing to deploy manually.
Handling Notifications in the SDK
All three notifications include a group ID in the payload. Use event.type to route the user to the right screen. Each notification type warrants a different response:
Entrig.onNotificationOpened.listen((NotificationEvent event) {
switch (event.type) {
case 'new_member':
// show a dialog: event.data['user_name'] joined event.data['group_name']
// navigate to the group chat on confirm using event.data['group_id']
break;
case 'new_message':
// navigate directly to the group chat using event.data['group_id']
break;
case 'new_group':
// show a dialog: event.data['user_name'] created event.data['group_name']
// navigate to the new group on confirm using event.data['id']
break;
}
});
event.type is the value you set in the dashboard for each notification. event.data contains the payload fields you selected: group IDs, names, message content, whatever you need to construct the UI.
Conclusion
The three patterns here (Direct, Indirect, Broadcast) map to the three fundamental ways recipients relate to an event in a relational database. Once you understand which pattern fits your use case, the rest is configuration. The same logic applies beyond chat: order updates, task assignments, product announcements.
Full SDK reference at entrig.com/docs/sdks/flutter.