# Flutter iOS

>[!IMPORTANT]
>[Flutter Obfuscation issue](https://github.com/pendo-io/pendo-mobile-sdk/issues/196#issue-2605284796)<br><br>
>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"
>
>Supported Navigation Libraries:
>
>- GoRouter 13.0 or higher
>- AutoRoute 7.0 or higher

## Step 1. Add Pendo dependency 
In the root folder of your flutter app add the Pendo package: `flutter pub add pendo_sdk`.

## 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 the Pendo Session where your visitor is being identified (e.g., login, register, etc.).
    ```dart
    import 'package:pendo_sdk/pendo_sdk.dart';
    final String visitorId = 'John Smith';
    final String accountId = 'Acme Inc.';
    final dynamic visitorData = {'Age': '25', 'Country': 'USA'};
    final dynamic accountData = {'Tier': '1', 'Size': 'Enterprise'};

    await PendoSDK.startSession(visitorId, accountId, visitorData, accountData);
    ```

>[!TIP]
>To begin a session for an  <a href="https://support.pendo.io/hc/en-us/articles/360032202751" target="_blank">anonymous visitor</a>, 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 <br>
    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.

<ins>**Navigation Types:**<ins><br>

* **_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<App> {
        @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. <br>
    Make sure to add it once (e.g., adding it in the build method will be less desired)<br>

    ```dart
    import 'package:pendo_sdk/pendo_sdk.dart';

    @AutoRouterConfig()
    class AppRouter extends RootStackRouter {
        @override
        List<AutoRoute> get routes => [];
    }

    final AppRouter _router = AppRouter()..config().addPendoListenerToDelegate();

    class _AppState extends State<App> {
        @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<br>
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:<br>

```dart
 import 'package:pendo_sdk/pendo_sdk.dart';
 await PendoSDK.track('name', { 'firstProperty': 'firstPropertyValue', 'secondProperty': 'secondPropertyValue'});
```

## Step 3. Mobile device connectivity and testing

>[!NOTE]
> Find your scheme ID in the Pendo UI under `Settings` > `Subscription settings` > select an app > `App Details`.

These steps enable  <a href="https://support.pendo.io/hc/en-us/articles/360033487792-Creating-a-Mobile-Guide#test-guide-on-device-0-6" target="_blank">guide testing capabilities</a>.

1. **Add Pendo URL scheme to **info.plist** file:**

   Under App Target > Info > URL Types, create a new URL by clicking the + button.  
   Set **Identifier** to pendo-pairing or any name of your choosing.  
   Set **URL Scheme** to `YOUR_SCHEME_ID_HERE`.

    <img src="https://user-images.githubusercontent.com/56674958/144723345-15c54098-28db-414c-90da-ef4a5256ae6a.png" width="500" height="300" alt="Mobile Tagging">

2. **To enable pairing from the device:**

    In the AppDelegate file add or modify the **openURL** function:

    <details open>
    <summary> <b>Swift Instructions</b><i> - Click to expand or collapse</i></summary>

    ```swift
    import Pendo

    @UIApplicationMain
    class AppDelegate:  FlutterAppDelegate {
        override func application(_ app: UIApplication,open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
            if url.scheme?.range(of: "pendo") != nil {
                PendoManager.shared().initWith(url)
                return true
            }
            // your code here...
            return true
        }
    }
    ```
    </details>

    <details>
    <summary> <b>Objective-C Instructions</b><i> - Click to expand or collapse</i></summary>

    ```objectivec
    @import Pendo;
    //your code
    - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
        if ([[url scheme] containsString:@"pendo"]) {
            [[PendoManager sharedManager] initWithUrl:url];
            return YES;
        }
        //  your code here ...
        return YES;
    }
    ```
    </details>

## Step 4. Verify installation

1. Test using Xcode:  
Run the app while attached to Xcode.  
Review the Xcode console and look for the following message:  
`Pendo Mobile 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 <a href="https://app.pendo.io/admin" target="_blank">subscription settings</a>.


## 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<CUSTOM_STATEFULL_WIDGET> implements PendoNavigationState { // Added for Pendo Support

// Override this method to let Pendo get the info about the current selected state
@override
  List<PendoCustomNavigationInfo> getPendoCustomNavigationInfo() {
    List<PendoCustomNavigationInfo> 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).