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

# Authenticate with External Wallets

> Let users sign in to your Flutter app with wallets they already own, such as MetaMask or Phantom.

External wallets — also called externally owned accounts (EOAs) — are wallets users bring to your app, such as MetaMask, Phantom, or Rainbow. They are different from embedded wallets, which Dynamic creates and manages for the user.

When an external wallet is enabled, users can sign up or log in by connecting an existing wallet through the Dynamic auth UI. The SDK handles the wallet app handoff and returns the connected wallet.

## Prerequisites

* Dynamic Flutter SDK initialized (see [Quickstart](/docs/flutter/quickstart))
* `DynamicSDK.instance.dynamicWidget` in your widget tree
* A configured `redirectUrl` and `appOrigin` in `ClientProps`
* Chains enabled in your Dynamic dashboard
* Wallet login enabled under **External Wallets** in the dashboard

## Dashboard configuration

1. Go to **Chains & Networks** in the [Dynamic dashboard](https://app.dynamic.xyz/dashboard/chains-and-networks) and enable the chains you want to support (e.g. EVM, Solana).
2. Go to **Log in & User Profile** in the [Dynamic dashboard](https://app.dynamic.xyz/dashboard/log-in-user-profile) and toggle on **Wallet Log in** under **External Wallets**.
3. Go to **Security → Whitelist Mobile Deeplink** and add the same URL you use for `redirectUrl` (e.g. `myapp://`). This is required for the wallet app to return to your app after approval.

<Warning>
  `appOrigin` is required for external wallet connections. Use the HTTPS origin that represents your app (for example, `https://your-app.com`).
</Warning>

## SDK configuration

Make sure `ClientProps` includes `appOrigin` and `redirectUrl` when you initialize the SDK:

```dart theme={"system"}
import 'package:dynamic_sdk/dynamic_sdk.dart';

DynamicSDK.init(
  props: ClientProps(
    environmentId: 'your-environment-id',
    appName: 'My App',
    appLogoUrl: 'https://your-app.com/logo.png',
    appOrigin: 'https://your-app.com',
    redirectUrl: 'myapp://',
  ),
);
```

## Deep link setup

External wallet connections open the wallet app using deep links. The wallet app then redirects back to your app using the `redirectUrl` you configured.

<Tabs>
  <Tab title="iOS">
    Add your URL scheme to `ios/Runner/Info.plist`:

    ```xml theme={"system"}
    <key>CFBundleURLTypes</key>
    <array>
      <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLName</key>
        <string>com.yourcompany.yourapp</string>
        <key>CFBundleURLSchemes</key>
        <array>
          <string>myapp</string>
        </array>
      </dict>
    </array>
    ```
  </Tab>

  <Tab title="Android">
    Add an intent filter in `android/app/src/main/AndroidManifest.xml` so the wallet app can return to your app:

    ```xml theme={"system"}
    <activity
      android:name=".MainActivity"
      android:launchMode="singleTask">
      <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="myapp" />
      </intent-filter>
    </activity>
    ```
  </Tab>
</Tabs>

## Show the auth UI

The simplest way to let users connect an external wallet is to show the Dynamic auth UI. The SDK presents available wallet options, opens the selected wallet app, and handles the redirect automatically.

```dart theme={"system"}
DynamicSDK.instance.ui.showAuth();
```

## Listen for connected wallets

After a wallet is connected, it appears in the user's wallet list. Use `userWalletsChanges` to react to new or updated wallets:

```dart theme={"system"}
StreamBuilder<List<BaseWallet>>(
  stream: DynamicSDK.instance.wallets.userWalletsChanges,
  initialData: DynamicSDK.instance.wallets.userWallets,
  builder: (context, snapshot) {
    final wallets = snapshot.data ?? [];

    if (wallets.isEmpty) {
      return const Text('No wallets connected');
    }

    return Column(
      children: wallets.map((wallet) {
        return ListTile(
          title: Text(wallet.walletName ?? 'Wallet'),
          subtitle: Text(wallet.address),
          trailing: Text(wallet.chain.toUpperCase()),
        );
      }).toList(),
    );
  },
)
```

## Filter connected wallets

You can separate EVM and Solana wallets by chain:

```dart theme={"system"}
final evmWallets = DynamicSDK.instance.wallets.userWallets
    .where((w) => w.chain.toUpperCase() == 'EVM')
    .toList();

final solanaWallets = DynamicSDK.instance.wallets.userWallets
    .where((w) => w.chain.toUpperCase() == 'SOL')
    .toList();
```

## Set the primary wallet

If a user connects multiple wallets, you can set one as the primary wallet:

```dart theme={"system"}
Future<void> setPrimaryWallet(BaseWallet wallet) async {
  final walletId = wallet.id;
  if (walletId == null) return;

  try {
    await DynamicSDK.instance.wallets.setPrimary(walletId: walletId);
  } catch (e) {
    print('Failed to set primary wallet: $e');
  }
}
```

## Troubleshooting

* **Wallet app does not open**: Verify `redirectUrl` matches the URL scheme in `Info.plist` and `AndroidManifest.xml`, and that the deep link is whitelisted in the dashboard.
* **Wallet does not appear after connection**: Ensure `DynamicSDK.instance.dynamicWidget` is in the widget tree and you are listening to `userWalletsChanges`.
* **Connection fails after redirect**: Confirm `appOrigin` is set and matches the origin registered in the dashboard.

## Next steps

* [Wallet Creation](/docs/flutter/wallet-creation) for embedded wallets
* [Wallet Management Reference](/docs/flutter/sdk-reference/wallet-management)
* [Token Balances](/docs/flutter/wallets/general/token-balances)
