Skip to content
Back to skills

Pushengage Flutter

ASecurity

Use ONLY when the active repo is a Flutter app (`pubspec.yaml` with a `flutter:` SDK block). Handles full PushEngage integration end-to-end (Dart init + Android native config + iOS native config + NSE) plus debugging existing integrations. Do not use for native iOS, native Android, or React Native apps even if PushEngage is mentioned.

  • 3 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added September 30, 2026
developmentrustgoswiftshellreactdebuggingrefactoringapidocumentation

Works with

  • claude code
  • cli
  • api

Security analysis

A100/100

Pro scans all 13 files and shows the line behind each finding

Scanned September 30, 2026

npx -y skills add awesomemotive/pushengage-skills --skill pushengage-flutter --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Pushengage Flutter?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Pushengage Flutter
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/awesomemotive-pushengage-flutter/badge)](https://www.skillsdirectory.com/skills/awesomemotive-pushengage-flutter)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: pushengage-flutter
description: Use ONLY when the active repo is a Flutter app (`pubspec.yaml` with a `flutter:` SDK block). Handles full PushEngage integration end-to-end (Dart init + Android native config + iOS native config + NSE) plus debugging existing integrations. Do not use for native iOS, native Android, or React Native apps even if PushEngage is mentioned.
license: MIT
compatibility: Works with any agent that supports the Agent Skills (SKILL.md) standard, including Claude Code and Cowork. Needs filesystem read access and a shell to detect the project type; some steps fetch allowlisted documentation URLs.
metadata:
  author: PushEngage
  version: "1.0.0"
---

# PushEngage — Flutter Spoke

You're working in a Flutter app. This skill walks the customer through integrating PushEngage from zero to verified test push, or debugs a broken integration.

> **Trigger gating.** This spoke fires only when `pubspec.yaml` contains a `flutter:` SDK block. The presence of `ios/Podfile` or `android/build.gradle` alone (without `pubspec.yaml`) does NOT route here — that's the native iOS or Android spoke. The hub's detection logic explicitly puts Flutter precedence ABOVE the native checks.

## Routing

| Customer intent | Read this file next |
|---|---|
| "Integrate PushEngage" / "Set up PushEngage" (greenfield or partial) | This file's "Integration flow" section below. |
| "PushEngage isn't working" / specific symptom | Run static audit (`audit-checks.md`) first, then escalate to `../pushengage-debug/SKILL.md` for Phase 2/3 (skill: `pushengage-debug`). |
| "Audit my PushEngage setup" | Read `audit-checks.md` and run the checks. |
| Concept / terminology question | Read `../pushengage/concepts.md`. |
| Best practices / rule lookup | Read `best-practices.md` (Flutter-specific) or `../pushengage/best-practices.md` (cross-platform). |
| API surface / "does method X exist" | Read `version-matrix.md` first; if not listed, fetch only URLs from `references.md`. |

## Integration flow (greenfield)

This is the happy path. Follow the parts strictly in order — earlier artifacts feed later steps.

### Part 1 — Discovery & prereqs

Before touching anything, gather:

1. **Target platforms** — does the customer ship to both stores (Android + iOS), or just one? Look for `ios/` and `android/` folders. Confirm with the customer (a Flutter project can have both folders even if they only deploy to one).
2. **Pub version pin** — read `pubspec.yaml`'s current `pushengage_flutter_sdk` pin (if any).
3. **`applicationId` (Android)** — grep `android/app/build.gradle*` for `applicationId`. Needed to register the Firebase app.
4. **iOS Bundle Identifier** — confirm with the customer; readable from Xcode's Signing & Capabilities tab on the Runner target.
5. **PushEngage account state** — ask:
   - Do you already have a PushEngage account?
   - Have you already created a site for this Flutter app? (One per platform — two sites total if shipping both.)
6. **Firebase project state** (Android-only step) — do they have a Firebase project for this app already, or do we create one?
7. **Apple Developer account state** (iOS-only step) — do they have it, and have they enabled Push Notifications on the App ID?
8. **Compose / UIKit / Material analog** — Flutter is the UI framework. No sub-flow split here (unlike Android's Compose-vs-Views split).

Output a one-paragraph integration plan in chat. Wait for the customer to confirm before any edits.

### Part 2 — Provider setup (out-of-code, before any code edits)

Do these BEFORE editing code. The dashboard's App ID UUIDs are needed for `setAppId(...)`; Firebase + APNs have real-world setup steps that block code progress.

The work splits by target platform:

- **Android target → `firebase-setup.md`.** Walks the customer through Firebase Console → register Android app → download `google-services.json` → generate Service Account JSON → capture Sender ID + `applicationId`. Defers to `../pushengage-android/firebase-setup.md` for the full click-path.
- **iOS target → `apns-setup.md`.** Walks the customer through Apple Developer portal → enable Push Notifications capability → Keychain CSR → APNs SSL cert → `.p12` export → capture Bundle Identifier. Defers to `../pushengage-ios/provider-setup.md` for the full click-path.

If both platforms: do them in parallel-ish — the steps don't conflict.

### Part 3 — Dashboard setup

Read `dashboard-setup.md`. A cross-platform Flutter app needs TWO PushEngage sites (one Android, one iOS) — each generates its own App ID UUID. Capture BOTH UUIDs and confirm them back to the customer before moving on.

Single-platform: just one site, one UUID.

### Part 4 — In-code edits

#### 4a — Install + native config

Read `install.md`. Apply:

- `pubspec.yaml` — bump or add `pushengage_flutter_sdk: ^1.0.0`. Run `flutter pub get`.
- Android: `android/settings.gradle`, `android/build.gradle`, `android/app/build.gradle`, `AndroidManifest.xml`, `google-services.json`.
- iOS: `ios/Podfile` (platform + post_install + NSE/NCE targets when applicable), `Info.plist`, `cd ios && pod install`.

**Auto-edit + announce posture** for all config files: `pubspec.yaml`, `Podfile`, `android/build.gradle*`, `settings.gradle*`, `AndroidManifest.xml`, `Info.plist`. Show the diff, then apply.

#### 4b — Dart + Swift init code

Read `init-code.md`. Apply:

- `lib/main.dart` — `PushEngage.setAppId(...)` in `main()` BEFORE `runApp`, with `Platform.isIOS ?` branching for cross-platform builds.
- `ios/Runner/AppDelegate.swift` — `swizzleInjection(isEnabled: true)` in `override init()`, optional `setBadgeCount(0)` and `setNotificationWillShowInForegroundHandler` in `didFinishLaunchingWithOptions`.
- Deep-link listener — `PushEngage.deepLinkStream.listen(...)` in the root widget's `initState`, cancelled in `dispose`.
- **No Android `Application` subclass needed** — the Flutter plugin handles native Android init through its plugin lifecycle.

**Diff + confirm posture** for `lib/main.dart` and `AppDelegate.swift` — entry-point code, bad edits break the app.

Insert the App ID UUID(s) captured in Part 3. Per XP-BP-01, recommend the customer later move them out of source (e.g., `--dart-define`).

#### 4c — Runtime permission flow

Read `permission-flow.md`. Set up a `requestNotificationPermission()` call site behind a user action (button, end-of-onboarding) — NOT in `main()` or app-root `initState`. Cross-platform via a single Dart call.

For Android: confirm `POST_NOTIFICATIONS` is declared in the Manifest (Step 4a covers this).

#### 4d — NSE setup (iOS, required for rich notifications)

If the customer is shipping to iOS, read `nse-setup.md`. Walk them through:

- Adding the NSE target via Xcode UI.
- Setting deployment target to iOS 12.
- Adding `pod 'PushEngageExtension', '1.0.0'` to the Podfile NSE target block (NOT to the `Runner` target).
- App Groups capability on BOTH targets + matching `PushEngage_App_Group_Key` in both Info.plists.

**Skip this step entirely if the customer is Android-only.**

#### 4e — NCE setup (optional)

Default: SKIP. Only run if the customer explicitly asks for custom in-notification UI. Read `nce-setup.md` if they do.

### Part 5 — Verification

The success criterion is **a real test notification reaches the customer's device** (one per platform if cross-platform).

For each target platform:

1. Customer connects a real device:
   - **Android:** must have Google Play Services. NOT a Play-Services-less emulator.
   - **iOS:** must be a physical device. APNs does NOT work on the iOS simulator.
2. Customer installs and runs the app once. Triggers the in-app permission request (per Part 4c).
3. Customer goes to PushEngage dashboard → corresponding site → Campaign → Push Broadcasts → sends to test device (or "All Subscribers" if it's the only one).
4. Ask: "Did the notification arrive on `<platform>`?"

**If yes** → go to Part 6.
**If no** → run static audit first (`audit-checks.md`), then hand off to `pushengage-debug/SKILL.md` with the symptom "test push didn't arrive on `<platform>`."

### Part 6 — Post-integration best-practices nudge

Short message. Pick 2–3 rule IDs that actually apply to what you just integrated. Examples:

- XP-BP-01 — move the App ID UUID(s) out of source (`--dart-define`, `String.fromEnvironment`).
- FLUTTER-BP-04 — defer `requestNotificationPermission()` to a meaningful moment, not on launch.
- FLUTTER-BP-05 — wrap `enableLogging(true)` in `kDebugMode`.
- FLUTTER-BP-02 — confirm the deep-link listener is in the root widget's `initState` and won't drift to a downstream route during future refactors.
- ANDROID-BP-13 (if Android target) — document OEM battery-optimization caveat in the customer's user-facing FAQ.

(Customize to what you actually saw. Don't dump all of them.)

## Debugging entry points

If the customer says any of the following — or if Part 5 verification fails — escalate to `pushengage-debug/SKILL.md`:

- "Notifications aren't arriving" (on one or both platforms).
- "Permission prompt never shows" (Android-specific → check the MERGED manifest for `POST_NOTIFICATIONS`; the SDK's library manifest normally supplies it — see FLUTTER-AUDIT-05).
- "iOS build fails with 'Application extensions cannot use PushEngage'" → FLUTTER-AUDIT-04.
- "Rich-push image doesn't load" → FLUTTER-AUDIT-03 (stale pod pin) and FLUTTER-AUDIT-09 (App Groups mismatch).
- "Cold-boot deep link is lost" → FLUTTER-AUDIT-12 / KI-F003.
- "Works on Android but not iOS" (or vice versa) → FLUTTER-AUDIT-07 / KI-F006 (hardcoded App ID).
- "`flutter pub get` succeeded but `PushEngage.X()` says no such method" → FLUTTER-AUDIT-01 / KI-F001 (stale `^0.0.1`).
- "Gradle build fails: `google-services.json` missing" → FLUTTER-AUDIT-06.

The debug skill handles audit + known-issues + symptom tree.

## Edit posture per file class

- **Auto-edit + announce:** `pubspec.yaml`, `Podfile`, `android/build.gradle*`, `android/settings.gradle*`, `android/app/build.gradle*`, `AndroidManifest.xml`, `Info.plist`, NSE `Info.plist`, file placements (`google-services.json` move).
- **Diff + confirm:** `lib/main.dart`, `ios/Runner/AppDelegate.swift`. Entry-point code — bad edits break the app.
- **Customer-driven (you instruct only):** Xcode UI work — NSE/NCE target creation, App Groups capability toggles, Push Notifications capability, Background Modes. Don't `.pbxproj`-edit programmatically.
- **Never touch:** anything outside the integration scope. No formatting passes, no Flutter / Dart / Gradle version bumps beyond what's required, no opportunistic refactoring.

## Where the Flutter spoke borrows from other spokes

The Flutter spoke deliberately references content files from the iOS and Android spokes to avoid duplication. These cross-spoke references are by file path only — you read the referenced content file, but does NOT load the other spoke's `SKILL.md`. The trigger contract (Flutter session loads Flutter skill) is preserved.

| What | Where |
|---|---|
| Firebase Console click-path | `../pushengage-android/firebase-setup.md` (referenced from this spoke's `firebase-setup.md`) |
| Apple Developer portal + Keychain click-path | `../pushengage-ios/provider-setup.md` (referenced from this spoke's `apns-setup.md`) |
| Long-form NCE template + category consistency rules | `../pushengage-ios/nce-setup.md` (referenced from this spoke's `nce-setup.md`) |
| Manual (non-swizzling) iOS forwards template | `../pushengage-ios/init-code.md` (referenced from this spoke's `init-code.md`) |

**Never** read the other spokes' `SKILL.md`s. Only their content files.

## What you must not do

- **Don't invent PushEngage method names.** If unsure a Dart method exists, check `version-matrix.md`. If still unsure, fetch an allowlisted URL from `references.md`. If still unsure, say so — don't guess.
- **Use `version-matrix.md` as the source of truth for version pins** (`^1.0.0` Flutter, `1.0.0` iOS pods). Older guides may show earlier pins like `^0.0.1`.
- **Don't auto-edit `lib/main.dart` or `AppDelegate.swift` without showing the diff and getting confirmation.** Entry-point code is diff + confirm.
- **Don't touch files outside the integration scope.** No refactoring, no Dart version bumps, no Flutter SDK upgrades, no Gradle version bumps that weren't strictly required.
- **Don't fetch URLs not in `references.md`.** The allowlist exists so the customer can trust where you got information.
- **Don't ask the customer to paste Service Account JSON or `.p12` contents in chat.** Both are secrets. They upload directly to the PushEngage dashboard; you only need to know the local path and confirm the upload succeeded.
- **Don't add `pod 'PushEngage'` to the main `Runner` target in Podfile** — it's pulled transitively. Only add it to NSE / NCE target blocks. (FLUTTER-BP-06.)
- **Don't create an Android `Application` subclass for PushEngage init** — the Flutter plugin handles it. If one already exists for other reasons, leave it alone.
- **Prefer `setAppId` in `main()` before `runApp`** over the SDK example's `initState` placement. The example sets it from `initState` because it enters the App ID at runtime — a valid pattern, not a bug — but for a known App ID, `main()` is cleaner. (FLUTTER-BP-01.)

Files in this skill

  • SKILL.md13.1 KB
  • apns-setup.md2.7 KB
  • audit-checks.md11.1 KB
  • best-practices.md9.7 KB
  • dashboard-setup.md3.3 KB
  • firebase-setup.md3.5 KB
  • init-code.md8.6 KB
  • install.md10 KB
  • nce-setup.md3.4 KB
  • nse-setup.md6.2 KB
  • permission-flow.md4.6 KB
  • references.md2.4 KB
  • version-matrix.md13.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.