Flutter数字密码输入插件widget_toolkit_pin的使用

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

Flutter数字密码输入插件widget_toolkit_pin的使用

Widget Toolkit Pin 提供了一个用于输入PIN码的页面,并且可以与生物识别技术(如指纹或面部识别)一起使用。此包通过使用 local_auth 包来提供平台对话框,以在应用级别启用或禁用生物识别功能。

功能特性

  • 自定义PIN长度:最多支持10位数。
  • 掩码行:带有已输入数字和炫酷动画效果。
  • 内置键盘:带按钮按下状态的自定义图标按键。
  • 错误处理:可选的用户级错误处理。
  • 生物识别认证:集成生物识别技术进行身份验证。

设置步骤

1. 添加依赖

首先,在项目的 pubspec.yaml 文件中添加以下依赖:

$ flutter pub add widget_toolkit_pin widget_toolkit_biometrics

2. 配置主题

接下来,将 PinCodeThemeWidgetToolkitTheme 扩展传递给应用程序的主题配置:

MaterialApp(
  theme: ThemeData.light().copyWith(
    extensions: [
      PinCodeTheme.light(),
      WidgetToolkitTheme.light(),
    ],
  ),
  darkTheme: ThemeData.dark().copyWith(
    extensions: [
      PinCodeTheme.dark(),
      WidgetToolkitTheme.dark(),
    ],
  ),
);

3. 实现数据源和服务

实现 BiometricsLocalDataSource

class ProfileLocalDataSource implements BiometricsLocalDataSource {
  static const _areBiometricsEnabled = 'areBiometricsEnabled';

  Future<SharedPreferences> get _storageInstance => SharedPreferences.getInstance();

  @override
  Future<bool> areBiometricsEnabled() async {
    final storage = await _storageInstance;
    return storage.getBool(_areBiometricsEnabled) ?? false;
  }

  @override
  Future<void> setBiometricsEnabled(bool enable) async {
    final storage = await _storageInstance;
    await storage.setBool(_areBiometricsEnabled, enable);
  }
}

实现 PinCodeService

class AppPinCodeService implements PinCodeService {
  static const _isPinCodeInStorage = 'pinCode';

  FlutterSecureStorage get flutterSecureStorage => const FlutterSecureStorage();

  @override
  Future<String> encryptPinCode(String pinCode) async {
    // 加密逻辑...
    return Future.value(pinCode);
  }

  @override
  Future<int> getPinLength() async => Future.value(4);

  @override
  Future<dynamic> verifyPinCode(String pinCode) async {
    var pinFromStorage = await flutterSecureStorage.read(key: _isPinCodeInStorage);
    if (pinFromStorage == null || pinFromStorage != pinCode) {
      throw IncorrectPinCode();
    }
    return pinCode;
  }

  @override
  Future<String?> getPinCode() async => await flutterSecureStorage.read(key: _isPinCodeInStorage);
}

class IncorrectPinCode extends ErrorModel {
  const IncorrectPinCode() : super(errorMessage: 'Incorrect pin code');
}

4. 使用 PinCodeKeyboard 组件

在你的页面中使用 PinCodeKeyboard 组件,并传入必要的参数:

PinCodeKeyboard(
  pinCodeService: AppPinCodeService(),
  biometricsLocalDataSource: ProfileLocalDataSource(),
  translateError: _translateError,
  mapBiometricMessageToString: _exampleMapBiometricMessageToString,
  onAuthenticated: (dynamic result) {
    _onAuthenticated(context);
  },
  onError: (error, translatedError) => _onError(error, translatedError, context),
  autoPromptBiometric: true,
);

示例代码

以下是一个完整的示例代码,展示了如何在Flutter项目中集成和使用 widget_toolkit_pin 插件:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:widget_toolkit/theme_data.dart';
import 'package:widget_toolkit_pin/widget_toolkit_pin.dart';
import 'package:widget_toolkit_biometrics/widget_toolkit_biometrics.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Widget Toolkit Pin Demo',
      theme: ThemeData.light().copyWith(
        extensions: [
          PinCodeTheme.light(),
          WidgetToolkitTheme.light(),
        ],
      ),
      darkTheme: ThemeData.dark().copyWith(
        extensions: [
          PinCodeTheme.dark(),
          WidgetToolkitTheme.dark(),
        ],
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) => MultiProvider(
        providers: [
          Provider<PinCodeService>(
            create: (context) => AppPinCodeService(),
          ),
          Provider<BiometricsLocalDataSource>(
            create: (context) => ProfileLocalDataSource(),
          )
        ],
        child: Builder(
          builder: (context) => Scaffold(
            body: SizedBox(
              height: MediaQuery.of(context).size.height,
              child: Column(
                children: [
                  Expanded(
                    child: PinCodeKeyboard(
                      pinCodeService: context.read<PinCodeService>(),
                      biometricsLocalDataSource: context.read<BiometricsLocalDataSource>(),
                      translateError: _translateError,
                      localizedReason: 'Activate the biometrics of your device',
                      onAuthenticated: (dynamic result) {
                        _onAuthenticated(context);
                      },
                      onError: (error, translatedError) => _onError(error, translatedError, context),
                      autoPromptBiometric: true,
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      );

  void _onAuthenticated(BuildContext context) {
    showBlurredBottomSheet(
      context: context,
      configuration: const ModalConfiguration(safeAreaBottom: false),
      builder: (context) => const MessagePanelWidget(
        message: 'You authenticated successfully',
        messageState: MessagePanelState.positive,
      ),
    );
  }

  void _onError(Object error, String strValue, BuildContext context) {
    showBlurredBottomSheet(
      context: context,
      configuration: const ModalConfiguration(safeAreaBottom: false),
      builder: (context) => MessagePanelWidget(
        message: _translateError(error),
        messageState: MessagePanelState.important,
      ),
    );
  }

  String _translateError(Object error) =>
      error is ErrorModel ? error.toString() : 'An error has occurred';

  String _exampleMapBiometricMessageToString(BiometricsMessage message) {
    switch (message) {
      case BiometricsMessage.notSetup:
        return 'To use biometrics, you need to turn it on in your device settings!';
      case BiometricsMessage.notSupported:
        return 'Biometric features aren’t supported on this device!';
      case BiometricsMessage.enabled:
        return 'Your biometrics are enabled!';
      case BiometricsMessage.disabled:
        return 'Your biometrics are disabled!';
    }
  }
}

class AppPinCodeService implements PinCodeService {
  final String _pinCode = '1111';

  @override
  Future<String> encryptPinCode(String pinCode) async {
    return Future.value(pinCode);
  }

  @override
  Future<int> getPinLength() async => Future.value(4);

  @override
  Future<dynamic> verifyPinCode(String pinCode) async {
    await Future.delayed(const Duration(seconds: 1));
    if (pinCode != '1111') {
      throw WrongPinCodeException(pinCode);
    }
    return pinCode;
  }

  @override
  Future<String?> getPinCode() async {
    return Future.value(_pinCode);
  }

  @override
  Future<bool> savePinCodeInSecureStorage(String pinCode) async {
    return true;
  }
}

class ProfileLocalDataSource implements BiometricsLocalDataSource {
  bool _areBiometricsEnabled = true;

  @override
  Future<bool> areBiometricsEnabled() async => _areBiometricsEnabled;

  @override
  Future<void> setBiometricsEnabled(bool enable) async => _areBiometricsEnabled = enable;
}

class WrongPinCodeException implements ErrorModel {
  final String pinCode;

  WrongPinCodeException(this.pinCode);

  @override
  String toString() => 'Invalid pin code: $pinCode';
}

通过以上步骤,你可以在Flutter项目中成功集成并使用 widget_toolkit_pin 插件来实现安全的PIN码输入和生物识别认证功能。


更多关于Flutter数字密码输入插件widget_toolkit_pin的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter数字密码输入插件widget_toolkit_pin的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter应用中使用widget_toolkit_pin插件来实现数字密码输入的示例代码。

首先,确保你已经在pubspec.yaml文件中添加了widget_toolkit_pin依赖:

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

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

接下来,你可以在你的Flutter应用中创建一个使用widget_toolkit_pin插件的页面。以下是一个简单的示例:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Pin Input Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: PinInputScreen(),
    );
  }
}

class PinInputScreen extends StatefulWidget {
  @override
  _PinInputScreenState createState() => _PinInputScreenState();
}

class _PinInputScreenState extends State<PinInputScreen> {
  final _pinController = TextEditingController();
  final _focusNode = FocusNode();
  String _pin = '';

  @override
  void dispose() {
    _pinController.dispose();
    _focusNode.dispose();
    super.dispose();
  }

  void _onSubmit(String pin) {
    // 处理提交的密码,例如验证或提交到服务器
    print('Submitted PIN: $pin');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Pin Input Demo'),
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: PinCodeTextField(
            controller: _pinController,
            focusNode: _focusNode,
            length: 6, // 密码长度
            obscureText: false, // 是否隐藏密码
            animationType: AnimationType.slide,
            pinTheme: PinTheme(
              shape: PinCodeFieldShape.box,
              borderRadius: BorderRadius.circular(10),
              fieldSize: 50,
              activeColor: Colors.blue,
              inactiveColor: Colors.grey,
              selectedColor: Colors.blueAccent,
              inactiveBorderColor: Colors.grey[300]!,
              activeBorderColor: Colors.blue,
              selectedBorderColor: Colors.blueAccent,
            ),
            animationDuration: Duration(milliseconds: 300),
            onCompleted: (pin) {
              _onSubmit(pin);
            },
            onChanged: (pin) {
              setState(() {
                _pin = pin;
              });
            },
            beforeTextPaste: (text) {
              // 在粘贴之前处理文本
              return text.replaceAll(RegExp(r'[^0-9]'), ''); // 只允许数字
            },
          ),
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,其中包含一个使用widget_toolkit_pin插件的数字密码输入框。我们设置了密码长度为6位,并且允许用户输入数字。当用户完成输入时,_onSubmit方法会被调用,并打印出输入的密码。

注意:

  • PinCodeTextField是一个自定义的密码输入框,它提供了许多可配置的选项,如密码长度、动画类型、主题等。
  • PinTheme允许你自定义输入框的外观,包括形状、边框半径、字段大小、颜色等。
  • onCompleted回调在用户完成输入时被调用,你可以在这里处理提交的密码。
  • beforeTextPaste回调允许你在粘贴文本之前对其进行处理,例如只允许粘贴数字。

请确保你替换了widget_toolkit_pin: ^最新版本号为实际的最新版本号,并根据需要调整代码中的参数和样式。

回到顶部