Flutter集成 CMB SDK 插件 cmbsdk_flutter 的使用

Flutter集成 CMB SDK 插件 cmbsdk_flutter 的使用

文档与集成信息

本页提供了有关如何在 Flutter 应用程序中集成 CMB SDK 插件 cmbsdk_flutter 的详细信息。

API 方法

所有 API 方法的使用和示例可以在 此处 找到。

更新日志

详细的更新日志可以在我们的 开发者网络站点 上找到。


示例代码

以下是完整的 Flutter 应用程序示例代码,演示如何使用 cmbsdk_flutter 插件进行扫描。

import 'dart:io';

import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';

import 'package:cmbsdk_flutter/cmbsdk_flutter.dart';
import 'package:cmbsdk_flutter/cmbsdk_flutter_constants.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() {
  runApp(
    new MaterialApp(
      home: new MyApp(),
    ),
  );
}

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

class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  // 支持多码扫描(一次扫描多个条形码);因此扫描结果返回为数组。
  // 注意:此示例应用程序未演示多码功能。
  List<dynamic> _resultsArray = [];

  bool _isScanning = false;
  String _cmbSDKVersion = 'N/A';
  String _connectionStatusText = 'Disconnected';
  Color _connectionStatusBackground = Colors.redAccent;
  String _scanButtonText = '(NOT CONNECTED)';
  bool _scanButtonEnabled = false;
  late SharedPreferences prefs;
  final String pairedPeripheralUUIDKey = 'pairedPeripheralUUID';
  BuildContext? deviceDialogContext;

  // 如果 USE_PRECONFIGURED_DEVICE 为 YES,则应用程序将使用 device/cameraMode 的值创建读取器。
  // 否则,应用程序会提供一个选择列表供用户选择 MX-1xxx 或内置摄像头。
  final bool _usePreconfiguredDevice = false;
  int _deviceType = cmbDeviceType.MXReader;
  int _cameraMode = cmbCameraMode.NoAimer;

  [@override](/user/override)
  void initState() {
    super.initState();
    WidgetsBinding.instance!.addObserver(this);

    initCmbSDK();
  }

  [@override](/user/override)
  void dispose() {
    WidgetsBinding.instance!.removeObserver(this);
    super.dispose();
  }

  [@override](/user/override)
  void didChangeAppLifecycleState(AppLifecycleState appLifecycleState) {
    switch (appLifecycleState) {
      case AppLifecycleState.resumed:
        // 关闭设备选择对话框
        if (deviceDialogContext != null) {
          Navigator.pop(deviceDialogContext!);
        }

        cmb
            .connect()
            .catchError((error, stackTrace) => print('${error.message}'));
        break;
      case AppLifecycleState.paused:
        cmb
            .disconnect()
            .catchError((error, stackTrace) => print('${error.message}'));
        break;
    }
  }

  Future<void> savePairedPeripheral() async {
    final String? pairedperipheralUUID = await cmb
        .getPairedBluetoothDevice()
        .catchError((error, stackTrace) => print(error.message));

    if (pairedperipheralUUID != null)
      prefs.setString(pairedPeripheralUUIDKey, pairedperipheralUUID);
  }

  Future<void> initCmbSDK() async {
    String cmbSDKVersion = 'N/A';

    prefs = await SharedPreferences.getInstance();

    // 当 MX-1xxx 设备可用时(USB电缆已插入或MX设备已打开),
    // 或当 MX-1xxx 已经可用但不再可用(USB电缆被拔出、由于不活动或电池耗尽而关闭)时调用。
    cmb.setAvailabilityChangedListener((availability) {
      if (availability == cmbAvailability.Available.index) {
        cmb
            .connect()
            .catchError((error, stackTrace) => print('${error.message}'));
      } else {
        showDialog(
          context: context,
          builder: (BuildContext context) {
            return AlertDialog(
              content: Text('设备不可用'),
              actions: [
                TextButton(
                  child: Text('确定'),
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                ),
              ],
            );
          },
        );
      }
    });

    // 当与读取器的连接状态改变时调用。
    // 只有在“连接”状态下读取器才可用。
    cmb.setConnectionStateChangedListener((state) {
      if (state == cmbConnectionState.Connected.index) {
        if (Platform.isIOS && _deviceType == cmbDeviceType.Bluetooth) {
          savePairedPeripheral();
        }

        _configureReaderDevice();

        _updateUIByConnectionState(cmbConnectionState.Connected);
      } else {
        _updateUIByConnectionState(cmbConnectionState.Disconnected);
      }

      if (!mounted) return;

      setState(() {
        _isScanning = false;
        _resultsArray = [];
      });
    });

    // 在扫描完成后调用(检测到条形码、通过屏幕按钮或硬件触发按钮取消扫描、扫描超时等)。
    cmb.setReadResultReceivedListener((resultJSON) {
      final Map<String, dynamic> resultMap = jsonDecode(resultJSON);
      final List<dynamic> resultsArray = resultMap['results'];

      if (!mounted) return;

      setState(() {
        _resultsArray = resultsArray;
      });
    });

    // 返回 TRUE 表示扫描过程已开始,返回 FALSE 表示已停止。
    cmb.setScanningStateChangedListener((scanningState) {
      if (!mounted) return;

      setState(() {
        _isScanning = scanningState;
        _scanButtonText = _isScanning ? '停止扫描' : '开始扫描';
      });
    });

    // 获取 cmbSDK 版本号
    cmbSDKVersion = await cmb.sdkVersion;

    // 初始化并连接到 MX/手机摄像头
    if (_usePreconfiguredDevice) {
      _createReaderDevice();
    } else {
      _selectDeviceFromPicker();
    }

    if (!mounted) return;

    setState(() {
      _cmbSDKVersion = cmbSDKVersion;
    });
  }

  // 根据当前连接状态更新应用程序的 UI(扫描按钮、连接状态标签)。
  void _updateUIByConnectionState(cmbConnectionState state) {
    String connectionStatusText = _connectionStatusText;
    Color connectionStatusBackground = _connectionStatusBackground;
    String scanButtonText = _scanButtonText;
    bool scanButtonEnabled = _scanButtonEnabled;

    if (state == cmbConnectionState.Connected) {
      connectionStatusText = '已连接';
      connectionStatusBackground = Colors.lightGreen;

      scanButtonText = '开始扫描';
      scanButtonEnabled = true;
    } else {
      connectionStatusText = '断开连接';
      connectionStatusBackground = Colors.redAccent;

      scanButtonText = '(未连接)';
      scanButtonEnabled = false;
    }

    if (!mounted) return;

    setState(() {
      _connectionStatusText = connectionStatusText;
      _connectionStatusBackground = connectionStatusBackground;

      _scanButtonText = scanButtonText;
      _scanButtonEnabled = scanButtonEnabled;
    });
  }

  // 这是一个选择读取器连接类型的选项列表。
  Future<void> _selectDeviceFromPicker() async {
    int deviceType = _deviceType;
    int cameraMode = _cameraMode;

    List<Widget> deviceOptions = [
      SimpleDialogOption(
        onPressed: () {
          Navigator.pop(context, cmbDeviceType.Bluetooth);
        },
        child: const Text('配对蓝牙扫描仪'),
      ),
      SimpleDialogOption(
        onPressed: () {
          Navigator.pop(context, cmbDeviceType.MXReader);
        },
        child: const Text('MX 扫描仪 (MX-1xxx)'),
      ),
      SimpleDialogOption(
        onPressed: () {
          Navigator.pop(context, cmbDeviceType.Camera);
        },
        child: const Text('手机摄像头'),
      ),
      SimpleDialogOption(
        onPressed: () {
          Navigator.pop(context, null);
        },
        child: const Text('取消'),
      ),
    ];

    if (Platform.isIOS) {
      deviceOptions.insert(
          0,
          SimpleDialogOption(
            onPressed: () {
              prefs.remove(pairedPeripheralUUIDKey);
              Navigator.pop(context, cmbDeviceType.Bluetooth);
            },
            child: const Text('新蓝牙扫描仪'),
          ));
    }

    bool dismissed = false;

    switch (await showDialog<int>(
        context: context,
        barrierDismissible: false,
        builder: (BuildContext context) {
          deviceDialogContext = context;
          return SimpleDialog(
            title: const Text('选择设备'),
            children: deviceOptions,
          );
        })) {
      case cmbDeviceType.Bluetooth:
        deviceType = cmbDeviceType.Bluetooth;
        break;
      case cmbDeviceType.MXReader:
        deviceType = cmbDeviceType.MXReader;
        break;
      case cmbDeviceType.Camera:
        deviceType = cmbDeviceType.Camera;
        cameraMode = cmbCameraMode.NoAimer;
        // ...
        break;
      default:
        // 对话框被取消
        dismissed = true;
        break;
    }

    deviceDialogContext = null;

    if (!mounted) return;

    setState(() {
      _deviceType = deviceType;
      _cameraMode = cameraMode;
    });

    if (dismissed == false) {
      _createReaderDevice();
    }
  }

  // 使用从 "selectDeviceFromPicker" 选择的选项创建读取器。
  void _createReaderDevice() {
    if (_deviceType == cmbDeviceType.Camera) {
      cmb.setCameraMode(_cameraMode);
      cmb.setPreviewOptions(cmbPrevewiOption.Defaults);

      cmb.registerSDK("SDK_KEY");
    } else if (_deviceType == cmbDeviceType.Bluetooth) {
      if (Platform.isIOS) {
        final String? pairedPeripheralUUID =
            prefs.getString(pairedPeripheralUUIDKey);

        if (pairedPeripheralUUID != null) {
          cmb.setPairedBluetoothDevice(pairedPeripheralUUID);
        } else {
          cmb.setPairedBluetoothDevice(""); // 清除它
        }
      } else
        cmb.setPairedBluetoothDevice("BT_Device_MAC_Address");
    }

    cmb.loadScanner(_deviceType).then((value) {
      cmb.connect().then((value) {
        _updateUIByConnectionState(cmbConnectionState.Connected);
      }).catchError((error, stackTrace) {
        _updateUIByConnectionState(cmbConnectionState.Disconnected);
      });
    }).catchError((error, stackTrace) => print('${error.message}'));
  }

  // 这是一个配置设备的示例。在此示例应用中,我们每次连接状态更改为连接时都配置设备(见 ConnectionStateChanged 事件),
  // 这样可以保证其按我们想要的方式设置。这不仅确保了我们在首次连接时配置了设备,还覆盖了 MX 扫描仪休眠(重新连接)的情况——除非明确保存更改到非易失性内存,
  // 否则它们可能会因 MX 休眠或重启而丢失。
  //
  // 这些只是示例设置;在您的应用程序中,您可能希望考虑哪些设置更改最适合您的应用。需要注意的是,
  // 不同支持的设备有不同的默认设置:
  //
  //    * MX-1xxx 移动终端默认启用以下符号体系:
  //        - Data Matrix
  //        - UPC/EAN
  //        - Code 39
  //        - Code 93
  //        - Code 128
  //        - Interleaved 2 of 5
  //        - Codabar
  //    * 相机扫描仪默认未启用任何符号体系
  //
  // 为了获得最佳扫描性能,建议仅启用应用程序实际需要扫描的条形码符号体系。如果使用 MX-1xxx,则可能意味着禁用某些默认值(或启用默认情况下关闭的符号体系)。
  //
  // 请注意,此示例应用程序适用于所有三种类型的设备,因此在我们的示例中,我们显示显式启用符号体系以及显式禁用符号体系(即使这些符号体系可能已经开启或关闭)。
  //
  // 我们还展示了如何发送可能特定于设备类型的配置命令——主要是为了演示目的。
  void _configureReaderDevice() {
    //----------------------------------------------
    // 显式启用我们需要的符号体系
    //----------------------------------------------
    cmb
        .setSymbologyEnabled(cmbSymbology.DataMatrix, true)
        .then((value) => print('DataMatrix 启用'))
        .catchError((error, stackTrace) =>
            print('DataMatrix 未启用。${error.message}'));

    cmb
        .setSymbologyEnabled(cmbSymbology.C128, true)
        .catchError((error, stackTrace) => print('${error.message}'));
    cmb
        .setSymbologyEnabled(cmbSymbology.UpcEan, true)
        .catchError((error, stackTrace) => print('${error.message}'));

    //-------------------------------------------------------
    // 显式禁用我们已知不需要的符号体系
    //-------------------------------------------------------
    cmb
        .setSymbologyEnabled(cmbSymbology.CodaBar, false)
        .then((value) => print('CodaBar 禁用'))
        .catchError((error, stackTrace) =>
            print('CodaBar 未禁用。${error.message}'));

    cmb
        .setSymbologyEnabled(cmbSymbology.C93, false)
        .catchError((error, stackTrace) => print('${error.message}'));

    //---------------------------------------------------------------------------
    // 下面是一些示例,展示如何发送 DMCC 命令并获取响应
    //---------------------------------------------------------------------------
    cmb
        .sendCommand('GET DEVICE.TYPE')
        .then((value) => print('$value'))
        .catchError((error, stackTrace) => print('${error.message}'));

    cmb
        .sendCommand('GET DEVICE.FIRMWARE-VER')
        .then((value) => print('$value'))
        .catchError((error, stackTrace) => print('${error.message}'));

    //---------------------------------------------------------------------------
    // 我们将显式关闭图像结果(尽管这是默认设置)。原因是,对于 MX-1xxx 扫描仪,
    // 如果应用程序不需要扫描图像,则启用图像结果可能会影响扫描性能。
    //---------------------------------------------------------------------------
    cmb
        .enableImage(false)
        .catchError((error, stackTrace) => print('${error.message}'));
    cmb
        .enableImageGraphics(false)
        .catchError((error, stackTrace) => print('${error.message}'));

    //---------------------------------------------------------------------------
    // 设备特定配置示例
    //---------------------------------------------------------------------------
    if (_deviceType == cmbDeviceType.Camera) {
      //---------------------------------------------------------------------------
      // 手机/平板电脑
      //---------------------------------------------------------------------------

      // 将 SDK 的解码努力设置为 3 级
      cmb
          .sendCommand("SET DECODER.EFFORT 3")
          .catchError((error, stackTrace) => print('${error.message}'));
    } else if (_deviceType == cmbDeviceType.MXReader) {
      //---------------------------------------------------------------------------
      // MX-1xxx
      //---------------------------------------------------------------------------

      //---------------------------------------------------------------------------
      // 将我们的配置保存到非易失性内存
      // 如果 MX 休眠或重启,我们的设置将保留。
      //---------------------------------------------------------------------------
      cmb
          .sendCommand("CONFIG.SAVE")
          .catchError((error, stackTrace) => print('${error.message}'));
    } else if (_deviceType == cmbDeviceType.Bluetooth) {
      //---------------------------------------------------------------------------
      // DM-8700
      //---------------------------------------------------------------------------

      //---------------------------------------------------------------------------
      // 将我们的配置保存到非易失性内存
      // 如果读取器休眠或重启,我们的设置将保留。
      //---------------------------------------------------------------------------
      cmb
          .sendCommand("CONFIG.SAVE")
          .catchError((error, stackTrace) => print('${error.message}'));
    }
  }

  void _toggleScanner() {
    if (_isScanning) {
      cmb
          .stopScanning()
          .catchError((error, stackTrace) => print('${error.message}'));
    } else {
      cmb
          .startScanning()
          .catchError((error, stackTrace) => print('${error.message}'));
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return new Scaffold(
      backgroundColor: Color(0xff333333),
      appBar: AppBar(
        title: const Text('CMBCameraDemo'),
        actions: [
          Padding(
              padding: const EdgeInsets.all(20.0),
              child: GestureDetector(
                onTap: () {
                  _selectDeviceFromPicker();
                },
                child: const Text('设备'),
              )),
        ],
      ),
      body: Center(
        child: Column(
          children: [
            Expanded(
                child: ListView.separated(
                    padding: const EdgeInsets.all(10),
                    itemCount: _resultsArray.length,
                    itemBuilder: (BuildContext context, int index) {
                      return Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text('${_resultsArray[index]['readString']}',
                                style: TextStyle(
                                    color: Colors.white, fontSize: 18)),
                            Text('${_resultsArray[index]['symbologyString']}',
                                style: TextStyle(color: Colors.grey)),
                          ]);
                    },
                    separatorBuilder: (BuildContext context, int index) =>
                        const Divider(thickness: 1))),
            Padding(
                padding: const EdgeInsets.all(10),
                child: Container(
                    width: double.infinity,
                    height: 50,
                    child: ElevatedButton(
                      onPressed: _scanButtonEnabled
                          ? () {
                              _toggleScanner();
                            }
                          : null,
                      child: Text(_scanButtonText),
                      style: ElevatedButton.styleFrom(
                          backgroundColor: Color(0xfffadb04),
                          foregroundColor: Colors.black,
                          disabledBackgroundColor: Color(0xfffadb04)),
                    ))),
            Padding(
                padding: const EdgeInsets.all(5),
                child: Row(
                  mainAxisSize: MainAxisSize.max,
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text(_cmbSDKVersion,
                        style: TextStyle(
                          color: Colors.white,
                        )),
                    Container(
                      color: _connectionStatusBackground,
                      child: Padding(
                          padding: EdgeInsets.all(2),
                          child: Text(_connectionStatusText,
                              style: TextStyle(
                                color: Colors.white,
                              ))),
                    )
                  ],
                ))
          ],
        ),
      ),
    );
  }
}

更多关于Flutter集成 CMB SDK 插件 cmbsdk_flutter 的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter集成 CMB SDK 插件 cmbsdk_flutter 的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter项目中集成并使用cmbsdk_flutter插件(假设这是一个用于集成中国民生银行(CMB)SDK的Flutter插件),你可以按照以下步骤进行操作。以下是一个基本的代码案例,展示如何在Flutter应用中集成并使用该插件。

1. 添加依赖

首先,在你的pubspec.yaml文件中添加cmbsdk_flutter插件的依赖。

dependencies:
  flutter:
    sdk: flutter
  cmbsdk_flutter: ^x.y.z  # 请替换为实际的版本号

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

2. 导入插件

在你的Dart文件中导入该插件。

import 'package:cmbsdk_flutter/cmbsdk_flutter.dart';

3. 初始化SDK

根据CMB SDK的要求,你可能需要在应用启动时初始化SDK。以下是一个简单的示例,展示如何在Flutter应用的main.dart文件中进行初始化。

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

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

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

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    // 初始化CMB SDK
    _initializeCMBSDK();
  }

  Future<void> _initializeCMBSDK() async {
    try {
      // 假设初始化方法需要一些参数,这里用示例参数
      bool result = await CMBSDK.initialize(
        appId: "your_app_id",
        appKey: "your_app_key",
        // 其他必要的初始化参数
      );
      if (result) {
        print("CMB SDK initialized successfully.");
      } else {
        print("Failed to initialize CMB SDK.");
      }
    } catch (e) {
      print("Error initializing CMB SDK: $e");
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('CMB SDK Flutter Example'),
        ),
        body: Center(
          child: Text('Check console for CMB SDK initialization status.'),
        ),
      ),
    );
  }
}

4. 使用SDK功能

一旦SDK初始化成功,你就可以调用SDK提供的其他功能了。例如,假设有一个登录方法,你可以这样调用:

Future<void> _login() async {
  try {
    // 假设登录方法需要用户名和密码
    Map<String, String> credentials = {
      "username": "user@example.com",
      "password": "password123",
    };
    CMBUser user = await CMBSDK.login(credentials: credentials);
    if (user != null) {
      print("Login successful: ${user.toJson()}");
    } else {
      print("Login failed.");
    }
  } catch (e) {
    print("Error during login: $e");
  }
}

你可以在按钮点击事件中调用这个方法:

body: Center(
  child: ElevatedButton(
    onPressed: _login,
    child: Text('Login'),
  ),
),

注意事项

  1. 错误处理:在实际应用中,你应该添加更详细的错误处理逻辑。
  2. 安全性:不要在代码中硬编码敏感信息,如appIdappKey,应该使用更安全的方式存储和读取这些信息,如使用Flutter的keychainkeystore插件。
  3. 文档:查阅cmbsdk_flutter插件的官方文档,了解所有可用的方法和参数。

以上代码提供了一个基本的框架,展示如何在Flutter项目中集成并使用cmbsdk_flutter插件。根据实际需求,你可能需要调整代码以适应特定的业务逻辑和UI设计。

回到顶部