Flutter自定义输入插件new_pinput的使用

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

Flutter 自定义输入插件 new_pinput 使用指南

简介

Flutter Pinput 是一个易于使用且可高度定制的 Pin 码输入字段插件。它提供了诸如动画装饰切换、表单验证、短信自动填充、自定义光标、从剪贴板复制等功能。

主要特性

  • 动画装饰切换
  • 表单验证
  • iOS 和 Android 的 SMS 自动填充
  • 标准和自定义光标
  • 光标动画
  • 从剪贴板复制
  • 支持自定义键盘
  • 标准粘贴选项
  • 遮挡字符
  • 触觉反馈
  • 输入完成后关闭键盘
  • 多个漂亮的示例模板可供选择

安装与配置

确保你使用的 Flutter 版本 >= 3.7.0,否则请使用 Pinput 版本 2.2.21。

pubspec.yaml 文件中添加依赖:

dependencies:
  new_pinput: ^latest_version

然后运行 flutter pub get 来安装该包。

基本用法

以下是一个基本的 Pinput 使用示例:

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

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: Text('Pinput Example')),
      body: FractionallySizedBox(
        widthFactor: 1,
        child: PinputExample(),
      ),
    ),
  ));
}

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

class _PinputExampleState extends State<PinputExample> {
  final pinController = TextEditingController();

  [@override](/user/override)
  void dispose() {
    pinController.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Center(
      child: Pinput(
        onCompleted: (pin) => print(pin),
      ),
    );
  }
}

定制化 Pinput

你可以通过创建 defaultPinTheme 来定制化你的 Pinput:

final defaultPinTheme = PinTheme(
  width: 56,
  height/XMLSchema = 56,
  textStyle: TextStyle(fontSize: 20, color: Color.fromRGBO(30, 60, 87, 1), fontWeight: FontWeight.w600),
  decoration: BoxDecoration(
    border: Border.all(color: Color.fromRGBO(234, 239, 243, 1)),
    borderRadius: BorderRadius.circular(20),
  ),
);

final focusedPinTheme = defaultPinTheme.copyDecorationWith(
  border: Border.all(color: Color.fromRGBO(114, 178, 238, 1)),
  borderRadius: BorderRadius.circular(8),
);

final submittedPinTheme = defaultPinTheme.copyWith(
  decoration: defaultPinTheme.decoration.copyWith(
    color: Color.fromRGBO(234, 239, 243, 1),
  ),
);

return Pinput(
  defaultPinTheme: defaultPinTheme,
  focusedPinTheme: focusedPinTheme,
  submittedPinTheme: submittedPinTheme,
  validator: (s) {
    return s == '2222' ? null : 'Pin is incorrect';
  },
  pinputAutovalidateMode: PinputAutovalidateMode.onSubmit,
  showCursor: true,
  onCompleted: (pin) => print(pin),
);

SMS 自动填充

对于 iOS 设备,只需点击键盘顶部的代码即可工作。

对于 Android 设备,如果你正在使用 firebase_auth,你需要设置 androidSmsAutofillMethodAndroidSmsAutofillMethod.none 并在 verificationCompleted 回调中设置 controller 的值。

示例代码如下:

await FirebaseAuth.instance.verifyPhoneNumber(
  verificationCompleted: (PhoneAuthCredential credential) {
    pinController.setText(credential.smsCode);
  },
  verificationFailed: (FirebaseAuthException e) {},
  codeSent: (String verificationId, int? resendToken) {},
  codeAutoRetrievalTimeout: (String verificationId) {},
);

如果不使用 firebase_auth,可以使用 SmartAuth 包来实现自动填充功能。

示例 Demo

这里提供了一个完整的示例应用,展示如何集成并使用 Pinput:

// example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:new_pinput/new_pinput.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      appBar: AppBar(title: Text('Pinput Example')),
      body: const FractionallySizedBox(widthFactor: 1, child: PinputExample()),
    ),
  ));
}

class PinputExample extends StatefulWidget {
  const PinputExample({Key? key}) : super(key: key);

  [@override](/user/override)
  State<PinputExample> createState() => _PinputExampleState();
}

class _PinputExampleState extends State<PinputExample> {
  final pinController = TextEditingController();
  final focusNode = FocusNode();
  final formKey = GlobalKey<FormState>();

  [@override](/user/override)
  void dispose() {
    pinController.dispose();
    focusNode.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    const focusedBorderColor = Color.fromRGBO(23, 171, 144, 1);
    const fillColor = Color.fromRGBO(243, 246, 249, 0);
    const borderColor = Color.fromRGBO(23, 171, 144, 0.4);

    final defaultPinTheme = PinTheme(
      width: 56,
      height: 56,
      textStyle: const TextStyle(fontSize: 22, color: Color.fromRGBO(30, 60, 87, 1)),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(19),
        border: Border.all(color: borderColor),
      ),
    );

    return Form(
      key: formKey,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Directionality(
            textDirection: TextDirection.ltr,
            child: Pinput(
              controller: pinController,
              focusNode: focusNode,
              androidSmsAutofillMethod: AndroidSmsAutofillMethod.smsUserConsentApi,
              listenForMultipleSmsOnAndroid: true,
              defaultPinTheme: defaultPinTheme,
              validator: (value) {
                return value == '2222' ? null : 'Pin is incorrect';
              },
              hapticFeedbackType: HapticFeedbackType.lightImpact,
              onCompleted: (pin) {
                debugPrint('onCompleted: $pin');
              },
              onChanged: (value) {
                debugPrint('onChanged: $value');
              },
              cursor: Column(
                mainAxisAlignment: MainAxisAlignment.end,
                children: [
                  Container(
                    margin: const EdgeInsets.only(bottom: 9),
                    width: 22,
                    height: 1,
                    color: focusedBorderColor,
                  ),
                ],
              ),
              focusedPinTheme: defaultPinTheme.copyWith(
                decoration: defaultPinTheme.decoration!.copyWith(
                  borderRadius: BorderRadius.circular(8),
                  border: Border.all(color: focusedBorderColor),
                ),
              ),
              submittedPinTheme: defaultPinTheme.copyWith(
                decoration: defaultPinTheme.decoration!.copyWith(
                  color: fillColor,
                  borderRadius: BorderRadius.circular(19),
                  border: Border.all(color: focusedBorderColor),
                ),
              ),
              errorPinTheme: defaultPinTheme.copyBorderWith(
                border: Border.all(color: Colors.redAccent),
              ),
            ),
          ),
          TextButton(
            onPressed: () {
              focusNode.unfocus();
              formKey.currentState!.validate();
            },
            child: const Text('Validate'),
          ),
        ],
      ),
    );
  }
}

更多关于Flutter自定义输入插件new_pinput的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter自定义输入插件new_pinput的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter中使用自定义输入插件 new_pinput 的示例代码。new_pinput 是一个假设的插件名称,具体细节(如方法名和参数)可能需要根据实际插件的文档进行调整。不过,以下代码提供了一个基本的框架,展示了如何在Flutter中集成和使用一个自定义输入插件。

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

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

然后运行 flutter pub get 来安装插件。

接下来,在你的 Dart 文件中(例如 main.dart),你可以这样使用 new_pinput 插件:

import 'package:flutter/material.dart';
import 'package:new_pinput/new_pinput.dart';  // 假设插件的主文件是 new_pinput.dart

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

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

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

class _MyHomePageState extends State<MyHomePage> {
  String _inputText = '';

  // 假设 new_pinput 插件提供了一个 onChanged 回调来更新输入文本
  void _handleInputChange(String newValue) {
    setState(() {
      _inputText = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Custom Input Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text('Input:', style: TextStyle(fontSize: 20)),
            SizedBox(height: 10),
            // 使用 new_pinput 插件提供的自定义输入组件
            NewPInput(
              hintText: 'Enter text here...',
              onChanged: _handleInputChange,  // 当输入变化时调用此回调
            ),
            SizedBox(height: 20),
            Text('You entered: $_inputText', style: TextStyle(fontSize: 20)),
          ],
        ),
      ),
    );
  }
}

在这个示例中:

  1. NewPInput 是假设的自定义输入组件,它可能提供了 hintText 属性来显示占位符文本,以及 onChanged 回调来监听输入的变化。
  2. _handleInputChange 方法在输入变化时被调用,并更新 _inputText 状态,从而触发 UI 的重新渲染。
  3. Text('You entered: $_inputText') 显示用户输入的文本。

请注意,由于 new_pinput 是一个假设的插件名称,实际使用时你需要参考该插件的官方文档来了解具体的属性和方法。如果插件提供了更多高级功能(如验证、格式化等),你也可以根据需要进一步扩展上述示例代码。

回到顶部