Flutter智能卡交互插件tangem_sdk的使用

Flutter智能卡交互插件tangem_sdk的使用

Tangem卡是一款支持NFC的自托管硬件钱包,其主要功能是安全地创建和存储私钥并签署数据。Tangem SDK用于在第三方应用程序中支持Tangem卡。

支持的平台包括:

  • iOS
  • Android
  • JVM
  • Flutter
  • React Native
  • Cordova
  • Capacitor

文档

如需详尽的文档,请访问Tangem开发者门户

要开始开发过程,可以从Tangem开发者入门指南页面开始。

许可证

Tangem SDK 在MIT许可证下可用。有关更多信息,请参阅LICENSE文件


示例代码

以下是一个完整的示例,展示了如何使用flutter_tangem_sdk插件来与Tangem卡进行交互。

示例代码

import 'dart:convert';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tangem_sdk/tangem_sdk.dart';
import 'package:tangem_sdk_example/app_widgets.dart';
import 'package:tangem_sdk_example/source.dart';

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Tangem SDK 插件示例')),
        body: CommandListWidget(),
      ),
    );
  }
}

class CommandListWidget extends StatefulWidget {
  [@override](/user/override)
  _CommandListWidgetState createState() => _CommandListWidgetState();
}

class _CommandListWidgetState extends State<CommandListWidget> {
  final _jsonEncoder = JsonEncoder.withIndent('  ');

  static const int ID_UNDEFINED = -1;
  static const int ID_SCAN = 1;
  static const int ID_CREATE_WALLET = 2;
  static const int ID_PURGE_WALLET = 3;

  late TangemSdk _sdk;
  int _methodId = 10;

  String? _cardId;
  String? _walletPublicKey;
  String? _scanImage;
  String _response = "";

  final _controller = TextEditingController();

  [@override](/user/override)
  void initState() {
    super.initState();

    _sdk = TangemSdk();
    _controller.addListener(() {
      setState(() {});
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Column(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          SizedBox(height: 25),
          RowActions(
            [
              ActionButton("扫描卡片", _handleScanCard),
              ActionButton("签名哈希", _handleSign),
            ],
          ),
          ActionType("设置扫描图像"),
          RowActions(
            [
              ActionButton("设置", _handleSetScanImage),
              ActionButton("移除", _handleRemoveScanImage),
            ],
          ),
          ActionType("钱包"),
          RowActions(
            [
              ActionButton("创建", _handleCreateWallet),
              ActionButton("清除", _handlePurgeWallet),
            ],
          ),
          ActionType("PIN码"),
          RowActions(
            [
              ActionButton("设置访问码", _handleSetAccessCode),
              ActionButton("设置密码", _handleSetPasscode),
            ],
          ),
          SizedBox(height: 5),
          Divider(),
          ActionType("JSON-RPC"),
          Container(
            padding: EdgeInsets.symmetric(horizontal: 16),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                SizedBox(height: 5),
                TextField(
                  decoration: InputDecoration(
                    contentPadding: EdgeInsets.fromLTRB(0, 0, 0, 5),
                    labelText: "粘贴配置",
                    isDense: true,
                  ),
                  minLines: 1,
                  maxLines: 25,
                  controller: _controller,
                ),
                SizedBox(height: 15),
                Row(
                  children: [
                    OutlinedButton(
                        onPressed: () async {
                          final data = await Clipboard.getData(Clipboard.kTextPlain);
                          final textData = data?.text ?? "";
                          if (textData.isEmpty) return;

                          _controller.value = TextEditingValue(text: textData);
                        },
                        child: Icon(Icons.paste)),
                    SizedBox(width: 8),
                    Expanded(
                      child: OutlinedButton(
                        child: Text("启动"),
                        onPressed: _controller.text.isEmpty ? null : () => _handleJsonRpc(_controller.text),
                      ),
                    )
                  ],
                ),
              ],
            ),
          ),
          Divider(),
          ActionType("结果"),
          Text(_response),
        ],
      ),
    );
  }

  void _handleScanCard() {
    _execJsonRPCRequest(_makeJsonRpc(SdkMethod.scan));
  }

  void _handleSign() {
    if (_cardId == null || _walletPublicKey == null) {
      _notify("扫描卡片或创建钱包");
      return;
    }

    final request = _makeJsonRpc(SdkMethod.sign_hash, {
      "walletPublicKey": _walletPublicKey,
      "hash": "f1642bb080e1f320924dde7238c1c5f8f1642bb080e1f320924dde7238c1c5f8ff",
    });
    _execJsonRPCRequest(request, _cardId);
  }

  void _handleSetScanImage() {
    _sdk.setScanImage(ScanTagImage(base64Image, 0)).then((value) {
      _parseResponse(value);
      _printResponse(value);
    }).onError((error, stackTrace) {
      _printResponse(error);
    });
  }

  void _handleRemoveScanImage() {
    _sdk.setScanImage(null).then((value) {
      _parseResponse(value);
      _printResponse(value);
    }).onError((error, stackTrace) {
      _printResponse(error);
    });
  }

  void _handleCreateWallet() {
    if (_cardId == null) {
      _notify("扫描卡片");
      return;
    }

    final request = _makeJsonRpc(SdkMethod.create_wallet, {
      "curve": "Secp256k1",
    });
    _execJsonRPCRequest(request, _cardId);
  }

  void _handlePurgeWallet() {
    if (_cardId == null || _walletPublicKey == null) {
      _notify("扫描卡片或创建钱包");
      return;
    }

    final request = _makeJsonRpc(SdkMethod.purge_wallet, {
      "walletPublicKey": _walletPublicKey,
    });
    _execJsonRPCRequest(request, _cardId);
  }

  void _handleSetAccessCode() {
    if (_cardId == null) {
      _notify("扫描卡片");
      return;
    }

    final request = _makeJsonRpc(SdkMethod.set_accesscode, {
      "accessCode": "ABCDEFGH",
    });
    _execJsonRPCRequest(request, _cardId);
  }

  void _handleSetPasscode() {
    if (_cardId == null) {
      _notify("扫描卡片");
      return;
    }

    final request = _makeJsonRpc(SdkMethod.set_passcode, {
      "passcode": "ABCDEFGH",
    });
    _execJsonRPCRequest(request, _cardId);
  }

  void _handleJsonRpc(String text) {
    try {
      final jsonMap = jsonDecode(text.trim());
      final request = JSONRPCRequest.fromJson(jsonMap);
      _execJsonRPCRequest(request, _cardId);
    } catch (ex) {
      _notify(ex.toString());
    }
  }

  void _execJsonRPCRequest(JSONRPCRequest request, [String? cardId, Message? message, String? accessCode]) {
    final completeRequest = {
      "JSONRPCRequest": jsonEncode(request),
      "cardId": cardId,
      "initialMessage": message?.toJson(),
      "accessCode": accessCode,
    };

    _sdk.runJSONRPCRequest(completeRequest).then((value) {
      _parseResponse(value);
      _printResponse(value);
    }).onError((error, stackTrace) {
      _printResponse(error);
    });
  }

  void _printResponse(Object? decodedResponse) {
    if (decodedResponse == null) return;

    setState(() {
      _response = _reEncode(decodedResponse);
    });
  }

  void _parseResponse(String response) {
    JSONRPCResponse jsonRpcResponse;
    try {
      jsonRpcResponse = JSONRPCResponse.fromJson(jsonDecode(response));
    } catch (ex) {
      print(ex.toString());
      return;
    }

    if (jsonRpcResponse.result != null) {
      switch (jsonRpcResponse.id) {
        case ID_SCAN:
          {
            _cardId = jsonRpcResponse.result["cardId"];
            final wallets = jsonRpcResponse.result["wallets"];
            if (wallets is List && wallets.isNotEmpty) {
              _walletPublicKey = wallets[0]["publicKey"];
            }
            break;
          }
        case ID_CREATE_WALLET:
          {
            _walletPublicKey = jsonRpcResponse.result["wallet"]["publicKey"];
            break;
          }
        case ID_PURGE_WALLET:
          {
            _walletPublicKey = null;
            break;
          }
      }
    }
  }

  JSONRPCRequest _makeJsonRpc(SdkMethod method, [Map<String, dynamic> params = const {}]) {
    return JSONRPCRequest(describeEnum(method), params, _getMethodId(method));
  }

  int _getMethodId(SdkMethod method) {
    switch (method) {
      case SdkMethod.scan:
        return ID_SCAN;
      case SdkMethod.create_wallet:
        return ID_CREATE_WALLET;
      case SdkMethod.purge_wallet:
        return ID_PURGE_WALLET;
      default:
        return _methodId++;
    }
  }

  void _notify(String message) {
    setState(() {
      _response = message;
    });
  }

  String _reEncode(Object value) {
    if (value is String) {
      return _jsonEncoder.convert(jsonDecode(value));
    } else {
      return _jsonEncoder.convert(value);
    }
  }
}
1 回复

更多关于Flutter智能卡交互插件tangem_sdk的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


tangem_sdk 是一个用于在 Flutter 应用中与 Tangem 智能卡进行交互的插件。Tangem 智能卡是一种硬件钱包,用于安全地存储和管理加密货币。通过 tangem_sdk 插件,开发者可以在 Flutter 应用中实现与 Tangem 智能卡的通信,执行诸如读取卡片信息、签名交易等操作。

以下是如何在 Flutter 项目中使用 tangem_sdk 插件的基本步骤:

1. 添加依赖

首先,在 pubspec.yaml 文件中添加 tangem_sdk 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  tangem_sdk: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来获取依赖。

2. 初始化 SDK

在使用 tangem_sdk 之前,需要先初始化 SDK。通常可以在应用的 main 函数中进行初始化:

import 'package:tangem_sdk/tangem_sdk.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await TangemSdk.initialize();
  runApp(MyApp());
}

3. 扫描卡片

要开始与 Tangem 智能卡进行交互,首先需要扫描卡片。可以使用 TangemSdk.scanCard() 方法来扫描卡片并获取卡片信息:

import 'package:tangem_sdk/tangem_sdk.dart';

Future<void> scanCard() async {
  try {
    final response = await TangemSdk.scanCard();
    print('Card ID: ${response.cardId}');
    print('Public Key: ${response.walletPublicKey}');
    print('Balance: ${response.balance}');
  } catch (e) {
    print('Error scanning card: $e');
  }
}

4. 签名交易

要使用 Tangem 智能卡签名交易,可以使用 TangemSdk.sign() 方法。首先需要准备交易数据,然后调用 sign 方法进行签名:

import 'package:tangem_sdk/tangem_sdk.dart';

Future<void> signTransaction() async {
  final transactionData = 'Your transaction data here';
  try {
    final signature = await TangemSdk.sign(transactionData);
    print('Transaction signature: $signature');
  } catch (e) {
    print('Error signing transaction: $e');
  }
}

5. 处理错误

在使用 tangem_sdk 时,可能会遇到各种错误,例如卡片未连接、操作被取消等。建议在使用 SDK 时捕获并处理这些错误:

try {
  final response = await TangemSdk.scanCard();
  // 处理响应
} on TangemSdkError catch (e) {
  print('Tangem SDK error: ${e.message}');
} catch (e) {
  print('Unexpected error: $e');
}

6. 其他功能

tangem_sdk 还提供了其他功能,例如创建钱包、导入钱包、设置访问码等。你可以根据需求调用相应的方法。

7. 示例代码

以下是一个简单的 Flutter 应用示例,展示了如何使用 tangem_sdk 扫描卡片并显示卡片信息:

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

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await TangemSdk.initialize();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Tangem SDK Example'),
        ),
        body: Center(
          child: TangemCardScanner(),
        ),
      ),
    );
  }
}

class TangemCardScanner extends StatefulWidget {
  [@override](/user/override)
  _TangemCardScannerState createState() => _TangemCardScannerState();
}

class _TangemCardScannerState extends State<TangemCardScanner> {
  String _cardInfo = 'Scan a Tangem card to see its information.';

  Future<void> _scanCard() async {
    try {
      final response = await TangemSdk.scanCard();
      setState(() {
        _cardInfo = 'Card ID: ${response.cardId}\nPublic Key: ${response.walletPublicKey}\nBalance: ${response.balance}';
      });
    } catch (e) {
      setState(() {
        _cardInfo = 'Error scanning card: $e';
      });
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(_cardInfo),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: _scanCard,
          child: Text('Scan Card'),
        ),
      ],
    );
  }
}
回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!