# Flutter Android
>[!IMPORTANT]
>[Flutter Obfuscation issue](https://github.com/pendo-io/pendo-mobile-sdk/issues/196#issue-2605284796)
>Migration from the track-event solution to the low-code solution:
> 1. Refer to [Step 2](#step-2-pendo-sdk-integration) in the installation guide, and verify the addition of the navigationObserver and clickListener.
> 2. The migration process will take up to 24 hours to complete and be reflected in Pendo, during the processing time, you will not be able to tag Pages and Features.
>[!IMPORTANT]
>Requirements:
>- Flutter: ">=3.16.0"
>- SDK: ">=3.2.0 < 4.0.0"
>- Android Gradle Plugin `8.0` or higher
>- Kotlin version `1.9.0` or higher
>- JAVA version `11` or higher
>- minSdkVersion `21` or higher
>- compileSDKVersion `35` or higher
>
>Supported Navigation Libraries:
>
>- GoRouter 13.0 or higher
>- AutoRoute 7.0 or higher
## Step 1. Install Pendo SDK
1. In the **application folder**, run the following command:
```shell
flutter pub add pendo_sdk
```
2. In the application **android/build.gradle** file:
- **Add the Pendo Repository to the repositories section under the allprojects section or to the settings.gradle if using dependencyResolutionManagement:**
```java
allprojects {
repositories {
maven {
url = uri("https://software.mobile.pendo.io/artifactory/androidx-release")
}
mavenCentral()
}
```
- Minimum SDK Version:
If applicable, set your app to be **minSdkVersion 21** or higher:
```java
android {
minSdkVersion 21
}
```
3. In the application **AndroidManifest.xml** file:
Add the following `` to the manifest in the `` tag:
```xml
```
4. Using ProGuard / R8
- If you are using **ProGuard**, the rules that need to be added to ProGuard can be found here: [pendo-proguard.cfg](/android/pnddocs/pendo-proguard.cfg).
- If you are using **ProGuard(D8/DX only)** to perform compile-time code optimization, and have `{Android SDK Location}/tools/proguard/proguard-android-optimize.txt`, add `!code/allocation/variable` to the `-optimizations` line in your `app/proguard-rules.pro` file.
The optimizations line should look like this:
`-optimizations *other optimizations*,!code/allocation/variable`
## Step 2. Integrate with the Pendo SDK
>[!NOTE]
>Find your API key in the Pendo UI under `Settings` > `Subscription settings` > select an app > `App Details`.
1. For optimal integration place the following code at the beginning of your app's execution:
```dart
import 'package:pendo_sdk/pendo_sdk.dart';
var pendoKey = 'YOUR_API_KEY_HERE';
await PendoSDK.setup(pendoKey);
```
2. Initialize Pendo where your visitor is being identified (e.g., login, register, etc.).
```dart
import 'package:pendo_sdk/pendo_sdk.dart';
final String visitorId = 'VISITOR-UNIQUE-ID';
final String accountId = 'ACCOUNT-UNIQUE-ID';
final Map visitorData = {'Age': '25', 'Country': 'USA'};
final Map accountData = {'Tier': '1', 'Size': 'Enterprise'};
await PendoSDK.startSession(visitorId, accountId, visitorData, accountData);
```
**Notes:**
**visitorId**: a user identifier (e.g., John Smith)
**visitorData**: the user metadata (e.g., email, phone, country, etc.)
**accountId**: an affiliation of the user to a specific company or group (e.g., Acme inc.)
**accountData**: the account metadata (e.g., tier, level, ARR, etc.)
>[!TIP]
>To begin a session for an anonymous visitor, pass ```null``` or an empty string ```''``` as the Visitor ID. You can call the `startSession` API more than once and transition from an anonymous session to an identified session (or even switch between multiple identified sessions).
3. Add Navigation Observers
When using `Flutter Navigator API` add PendoNavigationObserver for each app Navigator:
```dart
import 'package:pendo_sdk/pendo_sdk.dart';
// Observes the MaterialApp/CupertinoApp main Navigator
return MaterialApp(
...
navigatorObservers: [
PendoNavigationObserver()
],);
// Observes the nested widget Navigator
return Navigator(
...
observers: [
PendoNavigationObserver()
],);
```
> [!TIP]
> The Pendo SDK uses the `Route` name to uniquely identify each `Route`. Pendo highly recommends that you give a unique name to each route in the `RouteSettings`. The unique names must also be applied to the `showModalBottomSheet` api.
**Navigation Types:**
* **_GoRouter_**
When using `GoRouter`, change the `setup` API call to include the correct navigation library, like so:
```dart
PendoSDK.setup(pendoKey, navigationLibrary: NavigationLibrary.GoRouter);
```
When using `GoRouter`, apply the `addPendoListenerToDelegate()` to your `GoRouter` instance.
Make sure to add it once (e.g., adding it in the build method will be less desired)
```dart
import 'package:pendo_sdk/pendo_sdk.dart';
final GoRouter _router = GoRouter()..addPendoListenerToDelegate()
class _AppState extends State {
@override
Widget build(BuildContext context) {
return PendoActionListener(
child: MaterialApp.router(
routerConfig: _router,
),
);
}
}
```
> [!TIP]
> Pendo SDK uses routerDelegate listener to track route change analytics, make sure your route is included in the GoRouter routes
* **_AutoRoute_**
When using `AutoRoute`, change the `setup` API call to include the correct navigation library, like so:
```dart
PendoSDK.setup(pendoKey, navigationLibrary: NavigationLibrary.AutoRoute);
```
When using `AutoRoute`, apply the `addPendoListenerToDelegate()` to your `AutoRoute.config()` instance.
Make sure to add it once (e.g., adding it in the build method will be less desired)
```dart
import 'package:pendo_sdk/pendo_sdk.dart';
@AutoRouterConfig()
class AppRouter extends RootStackRouter {
@override
List get routes => [];
}
final AppRouter _router = AppRouter()..config().addPendoListenerToDelegate();
class _AppState extends State {
@override
Widget build(BuildContext context) {
return PendoActionListener(
child: MaterialApp.router(
routerConfig: _router.config(),
),
);
}
}
```
> [!TIP]
> Pendo SDK uses routerDelegate listener to track route change analytics, make sure your route is included in the AutoRoute routes.
4. Add a click listener
Wrap the main widget with a PendoActionListener in the root of the project:
```dart
import 'package:pendo_sdk/pendo_sdk.dart';
Widget build(BuildContext context) {
return PendoActionListener( // Use the PendoActionListener to track action clicks
child: MaterialApp(
title: 'Title',
home: Provider(
create: (context) => MyHomePageStore()..initList(),
child: MyHomePage(title: Strings.appName),
),
navigatorObservers: [PendoNavigationObserver()], // Use Pendo Observer to track the Navigator stack transitions
);
)
}
```
>[!TIP]
>You can use track events to programmatically notify Pendo of custom events of interest:
```dart
import 'package:pendo_sdk/pendo_sdk.dart';
await PendoSDK.track('name', { 'firstProperty': 'firstPropertyValue', 'secondProperty': 'secondPropertyValue'});
```
## Step 3. Mobile device connectivity for testing
>[!NOTE]
>Find your scheme ID in the Pendo UI under `Settings` > `Subscription settings` > select an app > `App Details`.
This step enables guide testing capabilities.
Add the following **activity** to the application **AndroidManifest.xml** in the `` tag:
```xml
```
## Step 4. Verify installation
1. Test using Android Studio:
Run the app while attached to the Android Studio.
Review the Android Studio logcat and look for the following message:
`Pendo SDK was successfully integrated and connected to the server.`
2. In the Pendo UI, go to `Settings` > `Subscription Settings`.
3. Select your application from the list.
4. Select the `Install Settings tab` and follow the instructions under `Verify Your Installation` to ensure you have successfully integrated the Pendo SDK.
5. Confirm that you can see your app as `Integrated` under subscription settings.
## Using custom navigation widgets
`With the release of version 3.6.2 custom navigation widget support was added.`
To integrate custom navigation widgets (e.g., TabBar, BottomNavigationBar, PageView), follow these steps:
1. Implement `PendoCustomNavigationWidget` in your `StatefulWidget` class.
2. Implement `PendoNavigationState` in the corresponding state class.
3. Override `getPendoCustomNavigationInfo()` to provide details about the current navigation state.
Example:
```dart
class CUSTOM_STATEFULL_WIDGET extends StatefulWidget implements PendoCustomNavigationWidget { // Added for Pendo Support
...
}
class _CUSTOM_STATEFULL_WIDGET_STATE extends State implements PendoNavigationState { // Added for Pendo Support
// Override this method to let Pendo get the info about the current selected state
@override
List getPendoCustomNavigationInfo() {
List info = [];
info.add(PendoCustomNavigationInfo(
navigationWidgetType: PendoNavigationWidgetType.Top, // specifies widget location on the screen. this must be provided
currentSelectedIndex: 1, // current selected index, optional
numberOfIndexes: 5, // total number of indexes, optional
currentSelectedTitle: 'first item', // unique title, optional
selectedIconCode: 1234)); // unique number representing the image on the selected item, optional
info.add(PendoCustomNavigationInfo(
navigationWidgetType: PendoNavigationWidgetType.Middle,
currentSelectedIndex: 2,
numberOfIndexes: 3,
currentSelectedTitle: 'second item',
selectedIconCode: 2468));
info.add(PendoCustomNavigationInfo(
navigationWidgetType: PendoNavigationWidgetType.Bottom,
currentSelectedIndex: 3,
numberOfIndexes: 6,
currentSelectedTitle: 'third item',
selectedIconCode: 1357));
return info;
}
}
```
## Limitations
- [Notes, Known Issues & Limitations](/other/flutter-notes-known-issues-limitations.md).
- To support hybrid mode in Flutter, please open a ticket.
## Developer documentation
- API documentation available [here](/api-documentation/flutter-apis.md).
- See [Native application with Flutter components](/other/native-with-flutter-components.md) integration instructions.
## Troubleshooting
- For technical issues, please [review open issues](https://github.com/pendo-io/pendo-mobile-sdk/issues) or [submit a new issue](https://github.com/pendo-io/pendo-mobile-sdk/issues).
- See our [release notes](https://developers.pendo.io/category/mobile-sdk/).
- For additional documentation, visit our [Help Center](https://support.pendo.io/hc/en-us/categories/23324531103771-Mobile-implementation).