Appearance and text
Match Bubbl's notification screen and privacy view to your app with your own colours, sizes and words, in light and dark mode.
Bubbl's notification screen (the card, the survey wizard and media) and its privacy view take their look and words from your app. The defaults follow the system's light and dark appearance. You change them the way each platform expects: Android resources, and on iOS colour sets and a strings table. Nothing is set in code.
In Flutter and React Native apps, put the Android resources in your app module (android/app/src/main/res) and the iOS colour sets and strings table in your iOS app target (the Runner target in Flutter).
Expo apps regenerate those folders at prebuild, so they add the files another way: see Expo apps.
Some of the privacy view comes from the dashboard instead, on every platform and with no app change: see Set in the dashboard.
Colours#
The same nine names on both platforms:
| Name | Used for |
|---|---|
bubbl_accent | Buttons, links and highlights |
bubbl_on_accent | Text on accent-coloured buttons |
bubbl_surface | The card |
bubbl_on_surface | Headline and body text |
bubbl_on_surface_muted | Secondary text |
bubbl_outline | Borders and dividers |
bubbl_scrim | The dimmed backdrop behind the card |
bubbl_media_surface | The area behind media |
bubbl_error | Error text, such as a required answer missing |
Android: declare a colour with the same name in your app's res/values/colors.xml, and in res/values-night/colors.xml for dark mode. Yours wins over Bubbl's.
<resources>
<color name="bubbl_accent">#FF0A7A6E</color>
<color name="bubbl_on_accent">#FFFFFFFF</color>
</resources>iOS: add a colour set with the same name to your app's asset catalogue, with light and dark variants. Bubbl looks each one up in your app's main bundle and falls back to its own.
Sizes (Android)#
On Android you can also override these dimensions in res/values/dimens.xml. On iOS they're fixed at the same values.
| Name | Default |
|---|---|
bubbl_card_max_width | 480dp |
bubbl_card_radius | 20dp |
bubbl_card_padding | 20dp |
bubbl_media_max_height | 240dp |
Text#
Every piece of text Bubbl shows can be reworded or translated. On Android each is a bubbl_ string resource: override it in res/values/strings.xml and translate it in res/values-<language>/ as usual. On iOS, add a strings table named Bubbl (Bubbl.strings) to your app with the same keys, and localise it as usual.
| Key | Default |
|---|---|
bubbl_close | Close |
bubbl_next | Next |
bubbl_back | Back |
bubbl_done | Done |
bubbl_submit | Send answers |
bubbl_thanks | Thanks for your answers |
bubbl_answer_required | Please answer this question to carry on |
bubbl_question_progress | Question %1$d of %2$d |
bubbl_yes | Yes |
bubbl_no | No |
bubbl_answer_hint | Your answer |
bubbl_number_hint | A number |
bubbl_slider_unanswered | Drag to answer |
bubbl_rating_star | %1$d of 5 |
bubbl_play_media | Play |
bubbl_pause_media | Pause |
bubbl_open_media | Open |
bubbl_media_description | Image for (headline) |
bubbl_permission_notifications_title | Stay in the loop |
bubbl_permission_notifications_body | Allow notifications to hear about offers and updates as they happen. |
bubbl_permission_location_title | Offers near you |
bubbl_permission_location_body | Allow your location to get offers and messages when you're near the places they're for. |
bubbl_permission_background_title | Offers even when the app is closed |
bubbl_permission_background_body | Tells the user which option to choose for "all the time" location. The default names Android's and iOS's own button. |
bubbl_privacy_policy | Privacy policy |
bubbl_continue | Continue |
bubbl_not_now | Not now |
bubbl_media_description takes the headline as its one argument: %1$s on Android, %1$@ on iOS. The privacy view's text in the dashboard (Configuration › Plugin › Callbacks & privacy › Privacy text), when set, is used in place of the built-in body text. See Permissions and privacy.
Android only#
bubbl_channel_name("Offers and updates") andbubbl_channel_description("Messages and surveys from this app"): the notification channel users see in system settings.bubbl_background_channel_name("Background updates") andbubbl_background_working("Checking for updates"): shown only on Android 11 and older, briefly and silently, while an update runs in the background.bubbl_media_position("%1$s / %2$s"): an audio clip's position and length.
Status bar icon#
Bubbl's status bar icon is your app's tech.bubbl.sdk.notification_icon meta-data in AndroidManifest.xml. Without it, Bubbl uses Firebase's default notification icon, then your app icon.
<application ...>
<meta-data
android:name="tech.bubbl.sdk.notification_icon"
android:resource="@drawable/ic_notification" />
</application>Set in the dashboard#
Three things about the privacy view come from Bubbl's server with the SDK's configuration, so they apply to every app, Expo included, without a new build: whether it shows (automatically, always or never), its body text, and its privacy-policy link. They're set under Configuration › Plugin › Callbacks & privacy. Colours, sizes and every other piece of text stay in the app. See Permissions and privacy.
Expo apps#
Expo writes android/ and ios/ afresh at every npx expo prebuild (EAS Build runs it too), so files you add there by hand are lost. Bubbl's config plugin has no colour or text options: the only words it sets are iOS's location permission prompts, with ios.locationWhenInUse and ios.locationAlways. BubblOptions takes no appearance settings either. To ship overrides from an Expo app, do one of these:
- Add a small config plugin of your own that writes the files at prebuild, like the one below.
- Stop regenerating the native folders (keep
android/andios/in your repository) and add the files as a React Native app does.
This plugin sets colours on both platforms. Colours are written #AARRGGBB, as on Android.
const { withDangerousMod } = require('expo/config-plugins');
const fs = require('fs');
const path = require('path');
const colors = { bubbl_accent: '#FF0A7A6E', bubbl_on_accent: '#FFFFFFFF' };
const withAndroidColors = (config) =>
withDangerousMod(config, ['android', (config) => {
const dir = path.join(config.modRequest.platformProjectRoot, 'app/src/main/res/values');
fs.mkdirSync(dir, { recursive: true });
const items = Object.entries(colors).map(([name, value]) => ` <color name="${name}">${value}</color>`);
fs.writeFileSync(path.join(dir, 'bubbl_colors.xml'), `<?xml version="1.0" encoding="utf-8"?>\n<resources>\n${items.join('\n')}\n</resources>\n`);
return config;
}]);
const withIosColors = (config) =>
withDangerousMod(config, ['ios', (config) => {
const catalog = path.join(config.modRequest.platformProjectRoot, config.modRequest.projectName, 'Images.xcassets');
for (const [name, value] of Object.entries(colors)) {
const [a, r, g, b] = value.slice(1).match(/../g);
const dir = path.join(catalog, `${name}.colorset`);
fs.mkdirSync(dir, { recursive: true });
const components = { alpha: (parseInt(a, 16) / 255).toFixed(3), red: `0x${r}`, green: `0x${g}`, blue: `0x${b}` };
fs.writeFileSync(path.join(dir, 'Contents.json'), JSON.stringify({
colors: [{ idiom: 'universal', color: { 'color-space': 'srgb', components } }],
info: { author: 'xcode', version: 1 },
}, null, 2));
}
return config;
}]);
module.exports = (config) => withIosColors(withAndroidColors(config));Then list it after Bubbl's plugin in app.json, and run npx expo prebuild --clean:
{
"expo": {
"plugins": ["@bubblsdk/react-native-sdk", "./plugins/withBubblAppearance"]
}
}- Dark mode: on Android, write a second file to
res/values-night. On iOS, add a dark entry to the colour set'scolors(withappearances: [{ appearance: 'luminosity', value: 'dark' }]). - Text on Android: write
bubbl_strings to a file inres/valuesthe same way, and translations tores/values-<language>. - Text on iOS: a
Bubbl.stringsfile must be part of the app target, not just inios/: write it withwithDangerousMod, then add it to the project's resources in awithXcodeProjectstep (for example withIOSConfig.XcodeUtils.addResourceFileToGroup). The colour sets above need no such step, because the asset catalogue is already in the target.
What you can't change#
The screen's layout is Bubbl's: the close button in the card's top-right corner, one survey question per step, and the SANDBOX ribbon on Sandbox notifications. For a completely different design, draw notifications yourself: see Drawing notifications yourself.