In-app messages
Show in-app messages from the Grovs dashboard in your Flutter app, hold them back on some screens, and react to taps and answers.
In-app messages are dashboard campaigns: a banner, modal, full-screen cover, system alert, question or your own HTML. The native SDK downloads live campaigns, picks one on the device for each app open, screen view or event, and draws it above your Flutter UI. To create campaigns, see In-app messages.
No code is required. Once the plugin is configured, campaigns show. Use the calls below to keep messages off some screens, pause them during a flow, or react to what the user did.
Triggers
A campaign shows on app open, on a screen view or on a custom event from track. The plugin turns native screen tracking off, so screen views come only from Grovs.navigatorObserver and trackScreenView. Without either, screen-view campaigns never show.
Sync, priority, frequency caps and offline behavior are the native SDKs'. See How messages reach the device.
React to messages
onInAppMessage is a broadcast stream of InAppMessageEvent, a sealed class. Switch over it:
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:grovs_flutter_plugin/grovs.dart';
StreamSubscription<InAppMessageEvent>? _inAppSubscription;
_inAppSubscription = Grovs().onInAppMessage.listen((event) {
switch (event) {
case InAppMessageShown(:final campaignId):
debugPrint('Shown $campaignId');
case InAppMessageClicked(:final buttonId, :final payload):
// buttonId is null for a tap on the message itself
debugPrint('Clicked $buttonId, route with ${payload?['screen']}');
case InAppMessageDismissed(:final reason):
debugPrint('Dismissed: ${reason.name}');
case InAppMessageAnswered(:final value, :final comment):
debugPrint('Answered $value, comment: $comment');
case InAppMessageFailed(:final message):
debugPrint('Not shown: $message');
}
});| Event | When it's sent |
|---|---|
InAppMessageShown(campaignId) | The message is on screen and counted as viewed. |
InAppMessageClicked(campaignId, buttonId, payload) | A button or the message was tapped. buttonId is null for a tap on the message. payload carries the pairs of a key-value action. |
InAppMessageDismissed(campaignId, reason) | The message closed without a click. reason is an InAppDismissReason: closeButton, backdrop, swipe, app, orientation, or unknown for a value this plugin doesn't know. |
InAppMessageAnswered(campaignId, value, comment) | A question was answered and the message closed, in place of the dismissal. value is an int (1 to 5) for stars, or the chosen option's ID for a choice. comment is null unless one was sent. |
InAppMessageFailed(message, campaignId) | The message couldn't be drawn (no visible screen, say). It's tried again at its next trigger. campaignId is null on iOS. |
campaignId is set on every event except InAppMessageFailed on iOS. Events with an unknown type or a missing required field are dropped.
Delivery
- Up to 20 events sent before you subscribe are buffered and replayed to the first listener.
- On Android, an event sent while no Flutter engine is attached is dropped. On iOS it's kept for the next engine.
- Nothing is sent while the SDK is disabled, and the buffer is cleared.
The plugin registers itself as the native in-app listener (Grovs.inAppDelegate on iOS, Grovs.setInAppListener on Android). Don't set your own natively, or events stop reaching Dart. There's no Dart hook to veto a single message; use pause or excluded screens.
Button actions
Each button, and the message itself, has one action: close, open a URL, key-value, ask for push permission, ask for an app rating, or open the store page. The SDK performs it, reports the click and sends InAppMessageClicked. For key-value actions, route in your own code from payload. Details: iOS button actions.
Hold messages back
final grovs = Grovs();
// Hold messages back, for example during checkout
await grovs.pauseInAppMessages();
// Show messages again, starting with the one that waited, if it still qualifies
await grovs.resumeInAppMessages();
// Drop the waiting message and stay paused. dismissCurrent also closes the one on screen.
await grovs.discardInAppMessages(dismissCurrent: true);
// No message shows on these screens; a message that comes due waits until the user leaves
await grovs.setInAppExcludedScreens(['Checkout', 'Login']);Excluded screens match the names Grovs records: the name you pass to trackScreenView, or the name GrovsNavigatorObserver sends after screen aliases are applied. Case is ignored.
Android setup
-
App ratings. Star questions and the rating action use Google Play In-App Review. Add it in
android/app/build.gradle.kts:Kotlindependencies { implementation("com.google.android.play:review:2.0.2") }Without it, campaigns with a rating action are skipped and the review step after a high star rating is left out.
-
Push permission button. On Android 13 and later, campaigns that ask for push permission need
POST_NOTIFICATIONSin your manifest.firebase_messagingalready declares it.
iOS needs no extra setup.
Questions and custom HTML
A message can ask for a star rating or one choice from a list, optionally followed by a comment. An answered campaign isn't shown again on that device. Custom HTML messages run in a web view with a window.grovs bridge. Both work as in a native app; see Questions and Custom HTML messages.
Test messages
Send test on a dashboard campaign sends it to the devices of one user, matched by the identifier from setUserIdentifier. Call setUserIdentifier first. The message shows at the next sync, which can take a few minutes. To see it sooner, send the app to the background and open it again.
Debugging
await grovs.setDebugLevel('info'); // needed on iOS to see the logs
await grovs.setInAppDebugLogging(true);setInAppDebugLogging(true) logs each campaign considered for an event and why it was skipped. On Android the logs go to Logcat under the tag GrovsInApp, whatever the debug level.
Reserved event names
The SDK reports message views, clicks, dismissals and answers as events, so track refuses in_app_viewed, in_app_clicked, in_app_dismissed, in_app_answered, in_app_commented, in_app_held, push_open, push_delivered, push_held, journey_entered, journey_held and journey_path, along with the system names.
See also
- Push notifications: receive push campaigns
- Events & screen tracking: the events and screens that trigger messages
- API reference: every in-app call and type
- Upgrading to 3.2