Flutter钱包连接插件wallet_connect的使用

发布于 1周前 作者 htzhanglong 来自 Flutter

Flutter钱包连接插件wallet_connect的使用

Wallet Connect Logo

Wallet Connect

使用

import 'package:wallet_connect/wallet_connect.dart';
  1. 创建一个Wallet Connect客户端实例并定义回调函数。
final wcClient = WCClient(
  onConnect: () {
    // 响应连接回调
  },
  onDisconnect: (code, reason) {
    // 响应断开连接回调
  },
  onFailure: (error) {
    // 响应连接失败回调
  },
  onSessionRequest: (id, peerMeta) {
    // 响应会话请求回调
  },
  onEthSign: (id, message) {
    // 响应个人签名或eth_sign或eth_signTypedData请求回调
  },
  onEthSendTransaction: (id, tx) {
    // 响应eth_sendTransaction请求回调
  },
  onEthSignTransaction: (id, tx) {
    // 响应eth_signTransaction请求回调
  },
);
  1. 从wc: URI创建WCSession对象。
final session = WCSession.from(wcUri);
  1. 创建WCPeerMeta对象,包含应用程序的元数据。
final peerMeta = WCPeerMeta(
    name: 'Example Wallet',
    url: 'https://example.wallet',
    description: 'Example Wallet',
    icons: [],
);
  1. 连接到新的会话。
wcClient.connectNewSession(session: session, peerMeta: peerMeta);
  1. 或者连接到已保存的会话(步骤8)。
wcClient.connectFromSessionStore(sessionStore);
  1. 批准会话连接请求。
wcClient.approveSession(
    accounts: [], // 账户地址
    chainId: 1, // 链ID
);
  1. 或者拒绝会话连接请求。
wcClient.rejectSession();
  1. 从sessionStore获取活动会话以供以后使用。
final sessionStore = wcClient.sessionStore;
  1. 通过签署交易并发送已签名的十六进制数据来批准签名交易请求。
wcClient.approveRequest<String>(
    id: id,
    result: signedDataAsHex,
);
  1. 通过发送生成的交易哈希来批准发送交易请求。
wcClient.approveRequest<String>(
    id: id,
    result: transactionHash,
);
  1. 通过发送生成的签名数据十六进制来批准签名请求。
wcClient.approveRequest<String>(
    id: id,
    result: signedDataAsHex,
);
  1. 或者通过指定请求ID来拒绝上述任何请求。
wcClient.rejectRequest(id: id);
  1. 在本地断开会话连接。
wcClient.disconnect();
  1. 永久关闭已连接的会话。
wcClient.killSession();

完整示例Demo

以下是完整的示例代码:

import 'dart:convert';

import 'package:eth_sig_util/eth_sig_util.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:wallet_connect/models/jsonrpc/json_rpc_request.dart';
import 'package:wallet_connect/wallet_connect.dart';
import 'package:wallet_connect_example/utils/constants.dart';
import 'package:wallet_connect_example/utils/eth_conversions.dart';
import 'package:wallet_connect_example/widgets/input_field.dart';
import 'package:wallet_connect_example/widgets/qr_scan_view.dart';
import 'package:wallet_connect_example/widgets/session_request_view.dart';
import 'package:wallet_connect_example/widgets/update_session_view.dart';
import 'package:web3dart/crypto.dart';
import 'package:web3dart/web3dart.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Wallet Connect',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Wallet Connect'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  [@override](/user/override)
  _MyHomePageState createState() => _MyHomePageState();
}

const rpcUri =
    'https://rpc-mainnet.maticvigil.com/v1/140d92ff81094f0f3d7babde06603390d7e581be';

enum MenuItems {
  PREVIOUS_SESSION,
  UPDATE_SESSION,
  KILL_SESSION,
  SCAN_QR,
  PASTE_CODE,
  CLEAR_CACHE,
  GOTO_URL,
}

class _MyHomePageState extends State<MyHomePage> {
  late WCClient _wcClient;
  late SharedPreferences _prefs;
  late InAppWebViewController _webViewController;
  late String walletAddress, privateKey;
  bool connected = false;
  WCSessionStore? _sessionStore;
  final _web3client = Web3Client(rpcUri, http.Client());

  [@override](/user/override)
  void initState() {
    _initialize();
    final rpcReq = JsonRpcRequest.fromJson({
      "id": 1666695490479571,
      "jsonrpc": "2.0",
      "method": "hello_World",
      "params": [
        {"chainId": "0xa4b1"}
      ],
    });
    debugPrint('rpcReq $rpcReq');
    super.initState();
  }

  _initialize() async {
    _wcClient = WCClient(
      onSessionRequest: _onSessionRequest,
      onFailure: _onSessionError,
      onDisconnect: _onSessionClosed,
      onEthSign: _onSign,
      onEthSignTransaction: _onSignTransaction,
      onEthSendTransaction: _onSendTransaction,
      onCustomRequest: (_, __) {},
      onConnect: _onConnect,
      onWalletSwitchNetwork: _onSwitchNetwork,
    );
    walletAddress = WALLET_ADDRESS;
    privateKey = PRIVATE_KEY;
    _prefs = await SharedPreferences.getInstance();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
        actions: [
          PopupMenuButton<MenuItems>(
            onSelected: (item) {
              switch (item) {
                case MenuItems.PREVIOUS_SESSION:
                  _connectToPreviousSession();
                  break;
                case MenuItems.UPDATE_SESSION:
                  if (_wcClient.isConnected) {
                    showGeneralDialog(
                      context: context,
                      barrierDismissible: true,
                      barrierLabel: 'Update Session',
                      pageBuilder: (context, _, __) => UpdateSessionView(
                        client: _wcClient,
                        address: walletAddress,
                      ),
                    ).then((value) {
                      if (value != null && (value as List).isNotEmpty) {
                        _wcClient.updateSession(
                          chainId: value[0] as int,
                          accounts: [value[1] as String],
                        );
                      }
                    });
                  } else {
                    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                      content: Text('Not connected.'),
                    ));
                  }
                  break;
                case MenuItems.KILL_SESSION:
                  _wcClient.killSession();
                  break;
                case MenuItems.SCAN_QR:
                  Navigator.push(
                    context,
                    MaterialPageRoute(builder: (_) => QRScanView()),
                  ).then((value) {
                    if (value != null) {
                      _qrScanHandler(value);
                    }
                  });
                  break;
                case MenuItems.PASTE_CODE:
                  showGeneralDialog(
                    context: context,
                    barrierDismissible: true,
                    barrierLabel: 'Paste Code',
                    pageBuilder: (context, _, __) => InputDialog(
                      title: 'Paste code to connect',
                      label: 'Enter Code',
                    ),
                  ).then((value) {
                    if (value != null && (value as String).isNotEmpty) {
                      _qrScanHandler(value);
                    }
                  });
                  break;
                case MenuItems.CLEAR_CACHE:
                  _webViewController.clearCache();
                  break;
                case MenuItems.GOTO_URL:
                  showGeneralDialog(
                    context: context,
                    barrierDismissible: true,
                    barrierLabel: 'Goto URL',
                    pageBuilder: (context, _, __) => InputDialog(
                      title: 'Enter URL to open',
                      label: 'Enter URL',
                    ),
                  ).then((value) {
                    if (value != null && (value as String).isNotEmpty) {
                      _webViewController.loadUrl(
                        urlRequest: URLRequest(url: Uri.parse(value)),
                      );
                    }
                  });
                  break;
              }
            },
            itemBuilder: (_) {
              return [
                PopupMenuItem(
                  value: MenuItems.PREVIOUS_SESSION,
                  child: Text('Connect Previous Session'),
                ),
                PopupMenuItem(
                  value: MenuItems.UPDATE_SESSION,
                  child: Text('Update Session'),
                ),
                PopupMenuItem(
                  value: MenuItems.KILL_SESSION,
                  child: Text('Kill Session'),
                ),
                PopupMenuItem(
                  value: MenuItems.SCAN_QR,
                  child: Text('Connect via QR'),
                ),
                PopupMenuItem(
                  value: MenuItems.PASTE_CODE,
                  child: Text('Connect via Code'),
                ),
                PopupMenuItem(
                  value: MenuItems.CLEAR_CACHE,
                  child: Text('Clear Cache'),
                ),
                PopupMenuItem(
                  value: MenuItems.GOTO_URL,
                  child: Text('Goto URL'),
                ),
              ];
            },
          ),
        ],
      ),
      body: InAppWebView(
        initialUrlRequest:
            URLRequest(url: Uri.parse('https://example.walletconnect.org')),
        initialOptions: InAppWebViewGroupOptions(
          crossPlatform: InAppWebViewOptions(
            useShouldOverrideUrlLoading: true,
          ),
        ),
        onWebViewCreated: (controller) {
          _webViewController = controller;
        },
        shouldOverrideUrlLoading: (controller, navAction) async {
          final url = navAction.request.url.toString();
          debugPrint('URL $url');
          if (url.contains('wc?uri=')) {
            final wcUri = Uri.parse(
                Uri.decodeFull(Uri.parse(url).queryParameters['uri']!));
            _qrScanHandler(wcUri.toString());
            return NavigationActionPolicy.CANCEL;
          } else if (url.startsWith('wc:')) {
            _qrScanHandler(url);
            return NavigationActionPolicy.CANCEL;
          } else {
            return NavigationActionPolicy.ALLOW;
          }
        },
      ),
    );
  }

  _qrScanHandler(String value) {
    if (value.contains('bridge') && value.contains('key')) {
      final session = WCSession.from(value);
      debugPrint('session $session');
      final peerMeta = WCPeerMeta(
        name: "Example Wallet",
        url: "https://example.wallet",
        description: "Example Wallet",
        icons: [
          "https://gblobscdn.gitbook.com/spaces%2F-LJJeCjcLrr53DcT1Ml7%2Favatar.png"
        ],
      );
      _wcClient.connectNewSession(session: session, peerMeta: peerMeta);
    }
  }

  _connectToPreviousSession() {
    final _sessionSaved = _prefs.getString('session');
    debugPrint('_sessionSaved $_sessionSaved');
    _sessionStore = _sessionSaved != null
        ? WCSessionStore.fromJson(jsonDecode(_sessionSaved))
        : null;
    if (_sessionStore != null) {
      debugPrint('_sessionStore $_sessionStore');
      _wcClient.connectFromSessionStore(_sessionStore!);
    } else {
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
        content: Text('No previous session found.'),
      ));
    }
  }

  _onConnect() {
    setState(() {
      connected = true;
    });
  }

  _onSwitchNetwork(int id, int chainId) async {
    await _wcClient.updateSession(chainId: chainId);
    _wcClient.approveRequest<Null>(id: id, result: null);
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
      content: Text('Changed network to $chainId.'),
    ));
  }

  _onSessionRequest(int id, WCPeerMeta peerMeta) {
    showDialog(
      context: context,
      builder: (_) => SessionRequestView(
        peerMeta: peerMeta,
        onApprove: (chainId) async {
          _wcClient.approveSession(
            accounts: [walletAddress],
            chainId: chainId,
          );
          _sessionStore = _wcClient.sessionStore;
          await _prefs.setString(
              'session', jsonEncode(_wcClient.sessionStore.toJson()));
          Navigator.pop(context);
        },
        onReject: () {
          _wcClient.rejectSession();
          Navigator.pop(context);
        },
      ),
    );
  }

  _onSessionError(dynamic message) {
    setState(() {
      connected = false;
    });
    showDialog(
      context: context,
      builder: (_) {
        return SimpleDialog(
          title: Text("Error"),
          contentPadding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 16.0),
          children: [
            Padding(
              padding: const EdgeInsets.only(bottom: 8.0),
              child: Text('Some Error Occured. $message'),
            ),
            Row(
              children: [
                TextButton(
                  style: TextButton.styleFrom(
                    foregroundColor: Colors.white,
                    backgroundColor: Theme.of(context).colorScheme.secondary,
                  ),
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  child: Text('CLOSE'),
                ),
              ],
            ),
          ],
        );
      },
    );
  }

  _onSessionClosed(int? code, String? reason) {
    _prefs.remove('session');
    setState(() {
      connected = false;
    });
    showDialog(
      context: context,
      builder: (_) {
        return SimpleDialog(
          title: Text("Session Ended"),
          contentPadding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 16.0),
          children: [
            Padding(
              padding: const EdgeInsets.only(bottom: 8.0),
              child: Text('Some Error Occured. ERROR CODE: $code'),
            ),
            if (reason != null)
              Padding(
                padding: const EdgeInsets.only(bottom: 8.0),
                child: Text('Failure Reason: $reason'),
              ),
            Row(
              children: [
                TextButton(
                  style: TextButton.styleFrom(
                    foregroundColor: Colors.white,
                    backgroundColor: Theme.of(context).colorScheme.secondary,
                  ),
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  child: Text('CLOSE'),
                ),
              ],
            ),
          ],
        );
      },
    );
  }

  _onSignTransaction(
    int id,
    WCEthereumTransaction ethereumTransaction,
  ) {
    _onTransaction(
      id: id,
      ethereumTransaction: ethereumTransaction,
      title: 'Sign Transaction',
      onConfirm: () async {
        final creds = EthPrivateKey.fromHex(privateKey);
        final tx = await _web3client.signTransaction(
          creds,
          _wcEthTxToWeb3Tx(ethereumTransaction),
          chainId: _wcClient.chainId!,
        );
        _wcClient.approveRequest<String>(
          id: id,
          result: bytesToHex(tx),
        );
        Navigator.pop(context);
      },
      onReject: () {
        _wcClient.rejectRequest(id: id);
        Navigator.pop(context);
      },
    );
  }

  _onSendTransaction(
    int id,
    WCEthereumTransaction ethereumTransaction,
  ) {
    _onTransaction(
      id: id,
      ethereumTransaction: ethereumTransaction,
      title: 'Send Transaction',
      onConfirm: () async {
        final creds = EthPrivateKey.fromHex(privateKey);
        final txhash = await _web3client.sendTransaction(
          creds,
          _wcEthTxToWeb3Tx(ethereumTransaction),
          chainId: _wcClient.chainId!,
        );
        debugPrint('txhash $txhash');
        _wcClient.approveRequest<String>(
          id: id,
          result: txhash,
        );
        Navigator.pop(context);
      },
      onReject: () {
        _wcClient.rejectRequest(id: id);
        Navigator.pop(context);
      },
    );
  }

  _onTransaction({
    required int id,
    required WCEthereumTransaction ethereumTransaction,
    required String title,
    required VoidCallback onConfirm,
    required VoidCallback onReject,
  }) async {
    BigInt gasPrice = BigInt.parse(ethereumTransaction.gasPrice ?? '0');
    if (gasPrice == BigInt.zero) {
      gasPrice = await _web3client.estimateGas();
    }
    showDialog(
      context: context,
      builder: (_) {
        return SimpleDialog(
          title: Column(
            children: [
              if (_wcClient.remotePeerMeta!.icons.isNotEmpty)
                Container(
                  height: 100.0,
                  width: 100.0,
                  padding: const EdgeInsets.only(bottom: 8.0),
                  child: Image.network(_wcClient.remotePeerMeta!.icons.first),
                ),
              Text(
                _wcClient.remotePeerMeta!.name,
                style: TextStyle(
                  fontWeight: FontWeight.normal,
                  fontSize: 20.0,
                ),
              ),
            ],
          ),
          contentPadding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 16.0),
          children: [
            Container(
              alignment: Alignment.center,
              padding: const EdgeInsets.only(bottom: 8.0),
              child: Text(
                title,
                style: TextStyle(
                  fontWeight: FontWeight.bold,
                  fontSize: 18.0,
                ),
              ),
            ),
            Padding(
              padding: const EdgeInsets.only(bottom: 8.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Receipient',
                    style: TextStyle(
                      fontWeight: FontWeight.bold,
                      fontSize: 16.0,
                    ),
                  ),
                  const SizedBox(height: 8.0),
                  Text(
                    '${ethereumTransaction.to}',
                    style: TextStyle(fontSize: 16.0),
                  ),
                ],
              ),
            ),
            Padding(
              padding: const EdgeInsets.only(bottom: 8.0),
              child: Row(
                children: [
                  Expanded(
                    flex: 2,
                    child: Text(
                      'Transaction Fee',
                      style: TextStyle(
                        fontWeight: FontWeight.bold,
                        fontSize: 16.0,
                      ),
                    ),
                  ),
                  Expanded(
                    child: Text(
                      '${EthConversions.weiToEthUnTrimmed(gasPrice * BigInt.parse(ethereumTransaction.gas ?? '0'), 18)} MATIC',
                      style: TextStyle(fontSize: 16.0),
                    ),
                  ),
                ],
              ),
            ),
            Padding(
              padding: const EdgeInsets.only(bottom: 8.0),
              child: Row(
                children: [
                  Expanded(
                    flex: 2,
                    child: Text(
                      'Transaction Amount',
                      style: TextStyle(
                        fontWeight: FontWeight.bold,
                        fontSize: 16.0,
                      ),
                    ),
                  ),
                  Expanded(
                    child: Text(
                      '${EthConversions.weiToEthUnTrimmed(BigInt.parse(ethereumTransaction.value ?? '0'), 18)} MATIC',
                      style: TextStyle(fontSize: 16.0),
                    ),
                  ),
                ],
              ),
            ),
            Theme(
              data:
                  Theme.of(context).copyWith(dividerColor: Colors.transparent),
              child: Padding(
                padding: const EdgeInsets.only(bottom: 8.0),
                child: ExpansionTile(
                  tilePadding: EdgeInsets.zero,
                  title: Text(
                    'Data',
                    style: TextStyle(
                      fontWeight: FontWeight.bold,
                      fontSize: 16.0,
                    ),
                  ),
                  children: [
                    Text(
                      '${ethereumTransaction.data}',
                      style: TextStyle(fontSize: 16.0),
                    ),
                  ],
                ),
              ),
            ),
            Row(
              children: [
                Expanded(
                  child: TextButton(
                    style: TextButton.styleFrom(
                      foregroundColor: Colors.white,
                      backgroundColor: Theme.of(context).colorScheme.secondary,
                    ),
                    onPressed: onConfirm,
                    child: Text('CONFIRM'),
                  ),
                ),
                const SizedBox(width: 16.0),
                Expanded(
                  child: TextButton(
                    style: TextButton.styleFrom(
                      foregroundColor: Colors.white,
                      backgroundColor: Theme.of(context).colorScheme.secondary,
                    ),
                    onPressed: onReject,
                    child: Text('REJECT'),
                  ),
                ),
              ],
            ),
          ],
        );
      },
    );
  }

  _onSign(
    int id,
    WCEthereumSignMessage ethereumSignMessage,
  ) {
    showDialog(
      context: context,
      builder: (_) {
        return SimpleDialog(
          title: Column(
            children: [
              if (_wcClient.remotePeerMeta!.icons.isNotEmpty)
                Container(
                  height: 100.0,
                  width: 100.0,
                  padding: const EdgeInsets.only(bottom: 8.0),
                  child: Image.network(_wcClient.remotePeerMeta!.icons.first),
                ),
              Text(
                _wcClient.remotePeerMeta!.name,
                style: TextStyle(
                  fontWeight: FontWeight.normal,
                  fontSize: 20.0,
                ),
              ),
            ],
          ),
          contentPadding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 16.0),
          children: [
            Container(
              alignment: Alignment.center,
              padding: const EdgeInsets.only(bottom: 8.0),
              child: Text(
                'Sign Message',
                style: TextStyle(
                  fontWeight: FontWeight.bold,
                  fontSize: 18.0,
                ),
              ),
            ),
            Theme(
              data:
                  Theme.of(context).copyWith(dividerColor: Colors.transparent),
              child: Padding(
                padding: const EdgeInsets.only(bottom: 8.0),
                child: ExpansionTile(
                  tilePadding: EdgeInsets.zero,
                  title: Text(
                    'Message',
                    style: TextStyle(
                      fontWeight: FontWeight.bold,
                      fontSize: 16.0,
                    ),
                  ),
                  children: [
                    Text(
                      ethereumSignMessage.data!,
                      style: TextStyle(fontSize: 16.0),
                    ),
                  ],
                ),
              ),
            ),
            Row(
              children: [
                Expanded(
                  child: TextButton(
                    style: TextButton.styleFrom(
                      foregroundColor: Colors.white,
                      backgroundColor: Theme.of(context).colorScheme.secondary,
                    ),
                    onPressed: () async {
                      String signedDataHex;
                      if (ethereumSignMessage.type ==
                          WCSignType.TYPED_MESSAGE) {
                        signedDataHex = EthSigUtil.signTypedData(
                          privateKey: privateKey,
                          jsonData: ethereumSignMessage.data!,
                          version: TypedDataVersion.V4,
                        );
                      } else {
                        final creds = EthPrivateKey.fromHex(privateKey);
                        final encodedMessage =
                            hexToBytes(ethereumSignMessage.data!);
                        final signedData =
                            await creds.signPersonalMessage(encodedMessage);
                        signedDataHex = bytesToHex(signedData, include0x: true);
                      }
                      debugPrint('SIGNED $signedDataHex');
                      _wcClient.approveRequest<String>(
                        id: id,
                        result: signedDataHex,
                      );
                      Navigator.pop(context);
                    },
                    child: Text('SIGN'),
                  ),
                ),
                const SizedBox(width: 16.0),
                Expanded(
                  child: TextButton(
                    style: TextButton.styleFrom(
                      foregroundColor: Colors.white,
                      backgroundColor: Theme.of(context).colorScheme.secondary,
                    ),
                    onPressed: () {
                      _wcClient.rejectRequest(id: id);
                      Navigator.pop(context);
                    },
                    child: Text('REJECT'),
                  ),
                ),
              ],
            ),
          ],
        );
      },
    );
  }

  Transaction _wcEthTxToWeb3Tx(WCEthereumTransaction ethereumTransaction) {
    return Transaction(
      from: EthereumAddress.fromHex(ethereumTransaction.from),
      to: EthereumAddress.fromHex(ethereumTransaction.to!),
      maxGas: ethereumTransaction.gasLimit != null
          ? int.tryParse(ethereumTransaction.gasLimit!)
          : null,
      gasPrice: ethereumTransaction.gasPrice != null
          ? EtherAmount.inWei(BigInt.parse(ethereumTransaction.gasPrice!))
          : null,
      value: EtherAmount.inWei(BigInt.parse(ethereumTransaction.value ?? '0')),
      data: hexToBytes(ethereumTransaction.data!),
      nonce: ethereumTransaction.nonce != null
          ? int.tryParse(ethereumTransaction.nonce!)
          : null,
    );
  }
}

更多关于Flutter钱包连接插件wallet_connect的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter钱包连接插件wallet_connect的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter项目中集成WalletConnect插件,可以让你的应用轻松与各种钱包进行交互。以下是一个基本的代码示例,展示如何在Flutter项目中使用wallet_connect插件来连接和管理钱包。

1. 添加依赖

首先,你需要在pubspec.yaml文件中添加wallet_connect插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  wallet_connect: ^最新版本号 # 请替换为实际可用的最新版本号

然后运行flutter pub get来安装依赖。

2. 配置Android和iOS

由于WalletConnect需要处理URL Scheme和深度链接,你需要在Android和iOS项目中进行一些配置。

Android配置

android/app/src/main/AndroidManifest.xml中添加以下代码:

<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="你的app的scheme" />
</intent-filter>

替换你的app的scheme为你的应用特定的URL Scheme。

iOS配置

ios/Runner/Info.plist中添加以下代码:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>你的app的scheme</string>
        </array>
    </dict>
</array>

同样替换你的app的scheme

3. 使用WalletConnect插件

在你的Flutter项目中,你可以按照以下方式使用wallet_connect插件:

import 'package:flutter/material.dart';
import 'package:wallet_connect/wallet_connect.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late WalletConnect? wc;

  @override
  void initState() {
    super.initState();

    // 初始化WalletConnect
    wc = WalletConnect(
      bridgeUrl: 'https://bridge.walletconnect.org', // 替换为你的Bridge URL
      clientMeta: ClientMeta(
        name: 'My Flutter App',
        description: 'A Flutter app using WalletConnect',
        url: 'https://yourapp.com', // 替换为你的应用URL
        icons: [
          'https://yourapp.com/icon.png', // 替换为你的应用图标URL
        ],
      ),
    );

    // 监听连接状态变化
    wc?.on('connect', (session) {
      print('Connected to session: $session');
    });

    wc?.on('disconnect', (session) {
      print('Disconnected from session: $session');
    });

    // 处理深度链接回调
    wc?.handleDeepLink().then((session) {
      if (session != null) {
        print('Handled deep link for session: $session');
      }
    }).catchError((error) {
      print('Error handling deep link: $error');
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('WalletConnect Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () async {
                  try {
                    // 创建会话并显示QRCode
                    var session = await wc?.createSession({
                      'chains': ['eip155:1:'], // 替换为你支持的链
                      'methods': ['eth_sendTransaction'], // 替换为你支持的方法
                    });

                    // 显示QRCode的UI代码(这里省略,你可以使用任何QRCode显示库)
                  } catch (error) {
                    print('Error creating session: $error');
                  }
                },
                child: Text('Create Session'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

注意事项

  1. 安全性:确保你的Bridge URL是安全的,并且你的应用正确处理了敏感信息。
  2. 错误处理:在实际应用中,你需要添加更多的错误处理逻辑来确保应用的稳定性。
  3. UI优化:上面的代码示例省略了QRCode的显示部分,你可以使用任何QRCode显示库来展示QRCode。

这个示例展示了如何在Flutter项目中集成和使用wallet_connect插件来管理钱包连接。根据你的具体需求,你可能需要调整配置和代码。

回到顶部