> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gameball.co/llms.txt
> Use this file to discover all available pages before exploring further.

# How Do I Show the Profile Widget in the Flutter SDK (v3)?

> Display the Gameball customer profile widget in your Flutter app

# Show Profile Widget

Display the Gameball customer profile widget that contains customer details, available reward campaigns, leaderboard, and more.

## Basic Usage

<div className="security-banner">
  <div className="security-banner-icon">🔒</div>

  <div className="security-banner-content">
    <strong>Secure API Access:</strong> To benefit from v4.1 secure API endpoints, pass the <code>sessionToken</code> parameter when showing the profile widget (required in upcoming SDK v4). This enables automatic routing to v4.1 secure endpoints. <a href="/api-reference/introduction-v4.1">Learn more about v4.1 →</a>
  </div>
</div>

```dart theme={null}
import 'package:gameball_sdk/gameball_sdk.dart';
import 'package:flutter/material.dart';

final profileRequest = ShowProfileRequestBuilder()
    .customerId("customer-123")
    .build();

final gameballApp = GameballApp.getInstance();
gameballApp.showProfile(context, profileRequest);

// With session token override
gameballApp.showProfile(context, profileRequest, sessionToken: "customer-token");
```

## Request Parameters

<ParamField body="context" type="BuildContext" required>
  The Flutter BuildContext from your widget.
</ParamField>

<ParamField body="customerId" type="string">
  Unique identifier for the customer whose profile to display. Optional for guest mode.
</ParamField>

<ParamField body="openDetail" type="string">
  Specific detail section to open (e.g., "rewards", "profile", "achievements"). Opens the main view by default.
</ParamField>

<ParamField body="hideNavigation" type="bool">
  Hide the navigation bar in the profile widget. Defaults to `false`.
</ParamField>

<ParamField body="showCloseButton" type="bool">
  Show a close button in the profile widget. Defaults to `true`.
</ParamField>

<ParamField body="closeButtonColor" type="string">
  Close button color in hex format (e.g., "#FF6B6B"). Only applies if `showCloseButton` is `true`.
</ParamField>

<ParamField body="sessionToken" type="string">
  Optional session token to override the global token for this specific request. Required in upcoming SDK v4.
</ParamField>

<ParamField body="mobile" type="string">
  Customer mobile number, used for channel merging. Available from v3.2.0.
</ParamField>

<ParamField body="email" type="string">
  Customer email address, used for channel merging. Available from v3.2.0.
</ParamField>

<ParamField body="externalLinkCallback" type="void Function(String)">
  Handler for links the widget tags with `gbExternalBrowser=true`. When provided, the link is delegated to your app; otherwise the SDK opens it in the system browser. Available from v3.2.0.
</ParamField>

<ParamField body="widgetEventCallback" type="void Function(Map<String, dynamic>?, Exception?)">
  Receives events the widget posts, such as game completion. Called with `(null, exception)` on a parse failure. Available from v3.2.0.
</ParamField>

<Info>
  In Flutter SDK, passing a sessionToken parameter updates the global token. Pass `null` to clear, or omit to use the current global token.
</Info>

### Validation Rules

No required fields (guest mode supported). Provide `customerId` for authenticated profiles.

## Advanced Configuration

### Open Specific Section

Open a specific section of the profile widget directly:

```dart theme={null}
final profileRequest = ShowProfileRequestBuilder()
    .customerId("customer-123")
    .openDetail("rewards")  // Open the rewards section
    .build();

gameballApp.showProfile(context, profileRequest);
```

### Guest Mode (v3.1.1+)

```dart theme={null}
// No customerId required
final guestRequest = ShowProfileRequestBuilder()
    .showCloseButton(true)
    .build();

gameballApp.showProfile(context, guestRequest);
```

### Custom Close Button

Add a custom-styled close button:

```dart theme={null}
final profileRequest = ShowProfileRequestBuilder()
    .customerId("customer-123")
    .showCloseButton(true)
    .closeButtonColor("#FF6B6B")
    .build();

gameballApp.showProfile(context, profileRequest);
```

### Hide Navigation

Display the widget without navigation controls:

```dart theme={null}
final profileRequest = ShowProfileRequestBuilder()
    .customerId("customer-123")
    .hideNavigation(true)
    .build();

gameballApp.showProfile(context, profileRequest);
```

### Handle External Links (v3.2.0+)

Links the widget tags with `gbExternalBrowser=true` open in the system browser by default. Provide an `externalLinkCallback` to handle them yourself instead (for example, to open an in-app browser or route to a custom screen):

```dart theme={null}
final profileRequest = ShowProfileRequestBuilder()
    .customerId("customer-123")
    .externalLinkCallback((url) {
      // Open the link your own way, e.g. an in-app browser or your router
    })
    .build();

gameballApp.showProfile(context, profileRequest);
```

<Note>
  When `externalLinkCallback` is set, the SDK delegates the link to your app and does not open the system browser.
</Note>

### Channel Merging (v3.2.0+)

Pass the customer's `mobile` and `email` so Gameball can merge the customer's channels:

```dart theme={null}
final profileRequest = ShowProfileRequestBuilder()
    .customerId("customer-123")
    .mobile("+201234567890")
    .email("customer@example.com")
    .build();

gameballApp.showProfile(context, profileRequest);
```

## Widget Events (v3.2.0+)

Pass a `widgetEventCallback` to react to events the widget posts, such as a customer finishing a game. Each event arrives as a `Map<String, dynamic>` with a top-level `type` and a nested `metadata` map. On a parse failure the callback is invoked with `(null, exception)`:

```dart theme={null}
final profileRequest = ShowProfileRequestBuilder()
    .customerId("customer-123")
    .widgetEventCallback((event, error) {
      if (error != null) return;                     // Parse failure

      final type = event?['type'] as String?;        // e.g. "gameCompleted"
      final metadata = event?['metadata'] as Map<String, dynamic>?;

      if (type == 'gameCompleted') {
        final hasWon = metadata?['hasWon'] as bool? ?? false;
        final rewardName = metadata?['rewardName'] as String?;
        if (hasWon) {
          // Refresh the customer's balance, show a win animation, etc.
        }
      }
    })
    .build();

gameballApp.showProfile(context, profileRequest);
```

### `gameCompleted` Metadata

| Field          | Type      | Description                                                                                                                                                                               |
| :------------- | :-------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `hasWon`       | `bool`    | Whether the customer won a reward this round                                                                                                                                              |
| `rewardType`   | `String?` | Reward category: `Default`, `Friend`, `Bonus`, `CustomText`, `Streak`, `NoReward`                                                                                                         |
| `discountType` | `String?` | Coupon kind when the win is a coupon: `Fixed`, `Percentage`, `FreeShipping`, `FreeProduct`, `Custom`, `RechargeFixed`, `RechargePercentage`, `ExternalReward`. `null` for non-coupon wins |
| `rewardName`   | `String?` | Localized, human-readable reward name                                                                                                                                                     |
| `campaignId`   | `String`  | Challenge or campaign identifier                                                                                                                                                          |
| `campaignType` | `String?` | Game type: `spinTheWheel`, `slotMachine`, `quiz`, `scratchCard`, `matchCards`, `catcher`, `ticTacToe`, `shooter`, `puzzle`, `tapTarget`, `highwayDrive`                                   |

<Note>
  All `gameCompleted` values arrive as `String` or `bool`. There are no numeric fields.
</Note>

## Dismiss the Widget (v3.2.0+)

Dismiss the widget programmatically from your app. The call is a no-op when no widget is showing:

```dart theme={null}
GameballApp.getInstance().hideProfile();
```

The widget can also dismiss itself from the web side by calling `window.GameballWidget.closeWidget()`.

## Implementation Examples

### Show Profile Button

Display the profile widget when user clicks a button:

```dart theme={null}
import 'package:flutter/material.dart';
import 'package:gameball_sdk/gameball_sdk.dart';

class ProfileScreen extends StatelessWidget {
  final String customerId;

  const ProfileScreen({Key? key, required this.customerId}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('My Profile'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () => _showGameballProfile(context),
          child: Text('View Rewards & Achievements'),
        ),
      ),
    );
  }

  void _showGameballProfile(BuildContext context) {
    final profileRequest = ShowProfileRequestBuilder()
        .customerId(customerId)
        .showCloseButton(true)
        .closeButtonColor("#4CAF50")
        .build();

    GameballApp.getInstance().showProfile(context, profileRequest);
  }
}
```

### Bottom Navigation Integration

Integrate with bottom navigation:

```dart theme={null}
class MainScreen extends StatefulWidget {
  @override
  _MainScreenState createState() => _MainScreenState();
}

class _MainScreenState extends State<MainScreen> {
  int _selectedIndex = 0;

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });

    if (index == 2) {  // Rewards tab
      final profileRequest = ShowProfileRequestBuilder()
          .customerId("customer-123")
          .build();

      GameballApp.getInstance().showProfile(context, profileRequest);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.shopping_cart),
            label: 'Shop',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.card_giftcard),
            label: 'Rewards',
          ),
        ],
        currentIndex: _selectedIndex,
        onTap: _onItemTapped,
      ),
      body: _buildBody(),
    );
  }

  Widget _buildBody() {
    switch (_selectedIndex) {
      case 0:
        return HomeTab();
      case 1:
        return ShopTab();
      default:
        return Container();
    }
  }
}
```

### Floating Action Button

Add a rewards button as a floating action button:

```dart theme={null}
class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home')),
      body: YourContentWidget(),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {
          final profileRequest = ShowProfileRequestBuilder()
              .customerId("customer-123")
              .openDetail("rewards")
              .build();

          GameballApp.getInstance().showProfile(context, profileRequest);
        },
        icon: Icon(Icons.card_giftcard),
        label: Text('My Rewards'),
      ),
    );
  }
}
```

## Widget Content

The profile widget displays:

<CardGroup cols={2}>
  <Card title="Customer Info" icon="user">
    Display name, points balance, tier level, and customer status
  </Card>

  <Card title="Reward Campaigns" icon="gift">
    Available rewards, challenges, and how to earn them
  </Card>

  <Card title="Leaderboard" icon="trophy">
    Customer ranking, top performers, and competitive standings
  </Card>

  <Card title="Transaction History" icon="clock-rotate-left">
    Points earned and redeemed history with detailed breakdown
  </Card>
</CardGroup>

<Info>
  The widget content, design, and branding can be fully customized from your Gameball dashboard.
</Info>

## Best Practices

<Steps>
  <Step title="Prominent Access">
    Place the profile widget button in an easily accessible location (e.g., main navigation, profile screen, bottom bar).
  </Step>

  <Step title="Badge Indicators">
    Show badge notifications when customers earn new rewards or achievements to increase engagement.
  </Step>

  <Step title="Deep Linking">
    Use the `openDetail` parameter to deep link to specific sections based on user context (e.g., open rewards after purchase).
  </Step>

  <Step title="Consistent Styling">
    Match the close button color with your app's theme using `closeButtonColor` for a cohesive user experience.
  </Step>

  <Step title="Contextual Display">
    Show the profile widget at relevant moments (after earning points, completing challenges, or when users check their account).
  </Step>
</Steps>

<Tip>
  Consider showing the profile widget after a customer earns points to increase engagement and encourage repeat purchases. Users are more likely to interact with the widget when they've just been rewarded.
</Tip>

<Warning>
  Ensure you've initialized the customer with `initializeCustomer()` before calling `showProfile()` to guarantee the widget displays accurate customer data.
</Warning>

## Next Steps

* [Go-Live Checklist](/installation-guides/v3/flutter/go-live-checklist)
* [Migration Notes](/installation-guides/v3/flutter/migration-notes)
