Flutter Solana钱包管理插件solana_wallet_provider的使用

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

Flutter Solana钱包管理插件 solana_wallet_provider 的使用

概述

solana_wallet_provider 是一个用于在Flutter应用中集成Solana钱包管理功能的插件。它提供了对 solana_web3solana_wallet_adapter 的访问,并创建了围绕 Mobile Wallet Adapter Specification 方法调用的UI包装器。

示例图示

Sign and Send Transaction

示例应用

查看完整示例应用

API

以下是该插件提供的主要API方法:

  • connect - 授权应用程序与可用的钱包或呈现下载选项列表。
  • disconnect - 显示授权账户和断开连接按钮以取消应用程序的授权。
  • signTransactions - 使用授权账户签署交易。
  • signAndSendTransactions - 使用授权账户签署交易,然后将它们广播到网络。
  • signMessages - 使用授权账户签署消息。

便利小部件

  • SolanaWalletButton - 一个切换应用程序与钱包授权状态的按钮小部件。

Dapp身份验证

钱包端点将使用您的 AppIdentity 信息来决定是否信任您的dapp。

AppIdentity(
    uri: Uri.parse('https://<YOUR_DOMAIN>'),
    icon: Uri.parse('favicon.png'),
    name: '<APP_NAME>'
)

Android设置

  1. 获取您的应用程序ID。

    // FILE: /android/app/build.gradle
    defaultConfig {
        applicationId "<APPLICATION_ID>"
    }
    
  2. 生成应用程序的sha256指纹。

    $ cd android
    $ ./gradlew app:signingReport
    
    // Output:
    //  ...
    //  SHA-256: <SHA256_FINGERPRINT>
    //  ...
    
  3. 托管包含以下内容的数字资产链接文件:

    // GET: https:://<YOUR_DOMAIN>/.well-known/assetlinks.json
    [{
        "relation": ["delegate_permission/common.handle_all_urls"],
        "target": { 
            "namespace": "android_app", 
            "package_name": "<APPLICATION_ID>",
            "sha256_cert_fingerprints": ["<SHA256_FINGERPRINT>"]
        }
    }]
    

主题定制

添加 SolanaWalletThemeExtension 作为 ThemeData 的扩展以自定义提供者的外观。

ThemeData(
    extensions: const [
        SolanaWalletThemeExtension(
            cardTheme: SolanaWalletCardTheme(
                color: Colors.indigo,
            ),
        ),
    ],
);

示例:连接

下面是一个如何使用该插件授权应用程序与Solana钱包的示例:

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

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});
  
  [@override](/user/override)
  Widget build(final BuildContext context) {
    return SolanaWalletProvider.create(                           
      identity: const AppIdentity(),
      child: MaterialApp(
        home: Scaffold(
          body: FutureBuilder(
            future: SolanaWalletProvider.initialize(),            
            builder: ((context, snapshot) {
              final provider = SolanaWalletProvider.of(context);
              return TextButton(
                onPressed: () => provider.connect(context),
                child: const Center(
                  child: Text('Example App'),
                ),
              );
            }),
          ),
        ),
      ),
    );
  }
}

完整示例Demo

以下是一个更完整的示例,展示了如何使用 solana_wallet_provider 插件执行多种操作,如连接、断开连接、签署交易、发送交易等:

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

void main() {
  runApp(const MaterialApp(home: ExampleApp()));
}

class ExampleApp extends StatefulWidget {
  const ExampleApp({super.key});

  [@override](/user/override)
  State<ExampleApp> createState() => _ExampleAppState();
}

class _ExampleAppState extends State<ExampleApp> {
  String? _status;

  [@override](/user/override)
  Widget build(final BuildContext context) {
    return SolanaWalletProvider.create(
      httpCluster: Cluster.devnet,
      identity: AppIdentity(
        uri: Uri.parse('https://my_dapp.com'),
        icon: Uri.parse('favicon.png'),
        name: 'My Dapp',
      ),
      child: MaterialApp(
        home: Scaffold(
          body: FutureBuilder(
            future: SolanaWalletProvider.initialize(),
            builder: (context, snapshot) {
              if (snapshot.connectionState != ConnectionState.done) {
                return const CircularProgressIndicator();
              }
              final provider = SolanaWalletProvider.of(context);

              return Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  const Text('Wallet Button'),
                  SolanaWalletButton(),

                  const Divider(),

                  const Text('Wallet Methods'),
                  Wrap(
                    spacing: 24.0,
                    children: [
                      _textButton(
                        'Connect', 
                        enabled: !provider.adapter.isAuthorized, 
                        onPressed: () => _connect(context, provider),
                      ),
                      _textButton(
                        'Disconnect', 
                        enabled: provider.adapter.isAuthorized, 
                        onPressed: () => _disconnect(context, provider),
                      ),
                      _textButton(
                        'Sign Transactions (1)', 
                        enabled: provider.adapter.isAuthorized, 
                        onPressed: () => _signTransactions(context, provider, 1),
                      ),
                      _textButton(
                        'Sign And Send Transactions (1)', 
                        enabled: provider.adapter.isAuthorized, 
                        onPressed: () => _signAndSendTransactions(context, provider, 1),
                      ),
                      _textButton(
                        'Sign Messages (1)', 
                        enabled: provider.adapter.isAuthorized, 
                        onPressed: () => _signMessages(context, provider, 1),
                      ),
                    ],
                  ),

                  const Divider(),

                  const Text('Output'),
                  Text(_status ?? '-'),
                ],
              );
            },
          ),
        ),
      ),
    );
  }

  Future<void> _connect(BuildContext context, SolanaWalletProvider provider) async {
    if (!provider.adapter.isAuthorized) {
      await provider.connect(context);
      setState(() {});
    }
  }

  Future<void> _disconnect(BuildContext context, SolanaWalletProvider provider) async {
    if (provider.adapter.isAuthorized) {
      await provider.disconnect(context);
      setState(() {});
    }
  }

  void _signTransactions(BuildContext context, SolanaWalletProvider provider, int count) async {
    try {
      setState(() => _status = "Create Sign Transactions...");
      final List<TransferData> transfers = await _createTransfers(provider.connection, provider.adapter, count: count);

      setState(() => _status = "Sign Transactions...");
      final result = await provider.signTransactions(
        context,
        transactions: transfers.map((transfer) => transfer.transaction).toList(),
      );

      setState(() => _status = "Broadcast Transactions...");
      final signatures = await provider.connection.sendSignedTransactions(result.signedPayloads, eagerError: true);

      setState(() => _status = "Confirm Transactions...");
      await _confirmTransfers(provider.connection, signatures: signatures.map((e) => base58To64Encode(e!)).toList(), transfers: transfers);
    } catch (error, stack) {
      print('Sign Transactions Error: $error');
      print('Sign Transactions Stack: $stack');
      setState(() => _status = error.toString());
    }
  }

  void _signAndSendTransactions(BuildContext context, SolanaWalletProvider provider, int count) async {
    try {
      setState(() => _status = "Create Sign And Send Transactions...");
      final List<TransferData> transfers = await _createTransfers(provider.connection, provider.adapter, count: count);

      setState(() => _status = "Sign And Send Transactions...");
      final result = await provider.signAndSendTransactions(
        context,
        transactions: transfers.map((transfer) => transfer.transaction).toList(),
      );

      setState(() => _status = "Confirm Transactions...");
      await _confirmTransfers(provider.connection, signatures: result.signatures, transfers: transfers);
    } catch (error, stack) {
      print('Sign And Send Transactions Error: $error');
      print('Sign And Send Transactions Stack: $stack');
      setState(() => _status = error.toString());
    }
  }

  void _signMessages(BuildContext context, SolanaWalletProvider provider, int count) async {
    try {
      setState(() => _status = "Create Sign Messages...");
      final messages = List.generate(count, (index) => 'Sign message $index');

      setState(() => _status = "Sign Messages...");
      final result = await provider.signMessages(
        context,
        messages: messages,
        addresses: [provider.adapter.encodeAccount(provider.adapter.connectedAccount!)],
      );

      setState(() => _status = "Signed Messages ${result.signedPayloads.join('\n')}");
    } catch (error, stack) {
      print('Sign Messages Error: $error');
      print('Sign Messages Stack: $stack');
      setState(() => _status = error.toString());
    }
  }

  Future<void> _airdrop(Connection connection, Pubkey wallet) async {
    if (connection.httpCluster != Cluster.mainnet) {
      setState(() => _status = "Requesting airdrop...");
      await connection.requestAndConfirmAirdrop(wallet, solToLamports(2).toInt());
    }
  }

  Future<List<TransferData>> _createTransfers(Connection connection, SolanaWalletAdapter adapter, {required int count}) async {
    final wallet = Pubkey.tryFromBase64(adapter.connectedAccount?.address);
    if (wallet == null) throw 'Wallet not connected';

    final balance = await connection.getBalance(wallet);
    if (balance < lamportsPerSol) await _airdrop(connection, wallet);

    final latestBlockhash = await connection.getLatestBlockhash();
    final txs = [];
    for (int i = 0; i < count; ++i) {
      final receiver = Keypair.generateSync();
      final lamports = solToLamports(0.1);
      final transaction = Transaction.v0(
        payer: wallet,
        recentBlockhash: latestBlockhash.blockhash,
        instructions: [
          SystemProgram.transfer(fromPubkey: wallet, toPubkey: receiver.pubkey, lamports: lamports),
        ],
      );
      txs.add(TransferData(transaction: transaction, receiver: receiver, lamports: lamports));
    }
    return txs;
  }

  Future<void> _confirmTransfers(Connection connection, {required List<String?> signatures, required List<TransferData> transfers}) async {
    await Future.wait([for (final sig in signatures) connection.confirmTransaction(base58To64Decode(sig!))], eagerError: true);

    final receiverBalances = await Future.wait([for (final transfer in transfers) connection.getBalance(transfer.receiver.pubkey)], eagerError: true);

    final results = [];
    for (int i = 0; i < receiverBalances.length; ++i) {
      final transfer = transfers[i];
      final pubkey = transfer.receiver.pubkey;
      final balance = receiverBalances[i].toBigInt();
      if (balance != transfer.lamports) throw Exception('Post transaction balance mismatch.');
      results.add("Transfer: Address $pubkey received $balance SOL");
    }

    setState(() => _status = "Success!\n\n"
        "Signatures: $signatures\n\n"
        "${results.join('\n')}"
        "\n");
  }

  TextButton _textButton(String text, {required bool enabled, required void Function() onPressed}) => TextButton(
    onPressed: enabled ? onPressed : null,
    child: Text(text),
  );
}

class TransferData {
  const TransferData({
    required this.transaction,
    required this.receiver,
    required this.lamports,
  });

  final Transaction transaction;
  final Keypair receiver;
  final BigInt lamports;
}

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

1 回复

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


当然,下面是一个关于如何在Flutter项目中使用solana_wallet_provider插件来管理Solana钱包的示例代码。请注意,实际使用时需要根据solana_wallet_provider插件的最新版本和API文档进行调整。

首先,确保你已经将solana_wallet_provider添加到你的pubspec.yaml文件中:

dependencies:
  flutter:
    sdk: flutter
  solana_wallet_provider: ^最新版本号  # 替换为实际的最新版本号

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

接下来,在你的Flutter项目中,你可以按照以下步骤使用solana_wallet_provider来管理Solana钱包。

1. 初始化Solana钱包提供者

在你的主应用文件(通常是main.dart)中,初始化SolanaWalletProvider

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => SolanaWalletProvider()),
      ],
      child: MaterialApp(
        title: 'Flutter Solana Wallet Management',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: MyHomePage(),
      ),
    );
  }
}

2. 连接到Solana钱包

在你的MyHomePage或其他需要管理钱包的页面中,连接到Solana钱包。

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:solana_wallet_provider/solana_wallet_provider.dart';

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final TextEditingController _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    final walletProvider = Provider.of<SolanaWalletProvider>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Solana Wallet Management'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              controller: _controller,
              decoration: InputDecoration(hintText: 'Enter wallet address'),
            ),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: () async {
                try {
                  await walletProvider.connectWallet(walletAddress: _controller.text);
                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Connected to wallet')));
                } catch (e) {
                  ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Failed to connect: ${e.message}')));
                }
              },
              child: Text('Connect Wallet'),
            ),
            SizedBox(height: 16),
            if (walletProvider.isConnected) {
              Text('Connected Wallet Address: ${walletProvider.walletAddress}')
            } else {
              Text('No wallet connected')
            },
          ],
        ),
      ),
    );
  }
}

3. 断开Solana钱包连接

你可以添加一个按钮来断开钱包连接。

// 在Column中添加一个断开连接的按钮
ElevatedButton(
  onPressed: () {
    final walletProvider = Provider.of<SolanaWalletProvider>(context);
    walletProvider.disconnectWallet();
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Disconnected from wallet')));
  },
  child: Text('Disconnect Wallet'),
),

4. 显示钱包余额(示例)

你可以添加一个方法来获取并显示钱包的余额(注意:这只是一个示例,具体实现取决于solana_wallet_provider是否提供了相关API)。

// 假设有一个获取余额的方法
Future<void> fetchBalance() async {
  final walletProvider = Provider.of<SolanaWalletProvider>(context);
  try {
    // 假设有一个获取余额的API
    final balance = await walletProvider.getBalance();
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Balance: $balance')));
  } catch (e) {
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Failed to fetch balance: ${e.message}')));
  }
}

// 在Column中添加一个获取余额的按钮
ElevatedButton(
  onPressed: fetchBalance,
  child: Text('Fetch Balance'),
),

请注意,上述代码中的getBalance方法是一个假设的方法,因为solana_wallet_provider的实际API可能会有所不同。你需要查阅solana_wallet_provider的文档来找到正确的方法来获取钱包余额。

这个示例展示了如何在Flutter中使用solana_wallet_provider插件来管理Solana钱包的连接和断开连接。根据插件的实际API和功能,你可以进一步扩展和自定义你的应用。

回到顶部