Flutter本地验证码生成插件local_captcha的使用

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

Flutter本地验证码生成插件local_captcha的使用

Flutter Local Captcha

Flutter Local Captcha 是一个用于快速开发原型或演示应用的假验证码组件。该组件旨在本地运行,设置简单,并具备类似真实验证码的基本功能。

请注意:这不是一个真正的防机器人解决方案,不建议在生产环境中使用。

功能特性

  • 轻量级,纯Dart编写。
  • 易于使用,高度可定制。

已知问题

  • [已修复] 在启用了Impeller的iOS设备上滚动性能较差。作为临时解决方案,您可以通过在flutter run命令中添加--no-enable-impeller标志来禁用Impeller。

实时预览

您可以访问网站Demo查看实时效果。

安装

在您的pubspec.yaml文件中添加依赖:

dependencies:
  local_captcha: ^1.0.4

导入

在您的Dart文件顶部导入插件:

import 'package:local_captcha/local_captcha.dart';

使用方法

控制器

初始化控制器并进行验证码验证和刷新操作:

// 初始化控制器
final _localCaptchaController = LocalCaptchaController();

// 验证验证码
_localCaptchaController.validate(value);

// 刷新验证码
_localCaptchaController.refresh();

// 记得不再需要时销毁控制器
@override
void dispose() {
  _localCaptchaController.dispose();
  super.dispose();
}

组件

配置并显示验证码组件:

LocalCaptcha(
  key: ValueKey('to tell widget should update'),
  controller: _localCaptchaController,
  height: 150,
  width: 300,
  backgroundColor: Colors.grey[100]!,
  chars: 'abdefghnryABDEFGHNQRY3468',
  length: 5,
  fontSize: 80.0,
  textColors: [
    Colors.black54,
    Colors.grey,
    Colors.blueGrey,
    Colors.redAccent,
    Colors.teal,
    Colors.amber,
    Colors.brown,
  ],
  noiseColors: [
    Colors.black54,
    Colors.grey,
    Colors.blueGrey,
    Colors.redAccent,
    Colors.teal,
    Colors.amber,
    Colors.brown,
  ],
  caseSensitive: false,
  codeExpireAfter: Duration(minutes: 10),
);

示例代码

以下是一个完整的示例程序,展示了如何在实际项目中使用local_captcha插件:

import 'dart:async';

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Local Captcha Example',
      theme: ThemeData(
        useMaterial3: false,
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _captchaFormKey = GlobalKey<FormState>();
  final _configFormKey = GlobalKey<FormState>();
  final _localCaptchaController = LocalCaptchaController();
  final _configFormData = ConfigFormData();
  final _refreshButtonEnableVN = ValueNotifier(true);

  var _inputCode = '';
  Timer? _refreshTimer = null;

  @override
  void dispose() {
    _localCaptchaController.dispose();
    _refreshTimer?.cancel();
    _refreshTimer = null;
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Local Captcha Example'),
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Center(
          child: SizedBox(
            width: 300.0,
            child: Form(
              key: _captchaFormKey,
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  LocalCaptcha(
                    key: ValueKey(_configFormData.toString()),
                    controller: _localCaptchaController,
                    height: 150,
                    width: 300,
                    backgroundColor: Colors.grey[100]!,
                    chars: _configFormData.chars,
                    length: _configFormData.length,
                    fontSize: _configFormData.fontSize > 0 ? _configFormData.fontSize : null,
                    caseSensitive: _configFormData.caseSensitive,
                    codeExpireAfter: _configFormData.codeExpireAfter,
                    onCaptchaGenerated: (captcha) {
                      debugPrint('Generated captcha: $captcha');
                    },
                  ),
                  const SizedBox(height: 16.0),
                  TextFormField(
                    decoration: const InputDecoration(
                      labelText: 'Enter code',
                      hintText: 'Enter code',
                      isDense: true,
                      border: OutlineInputBorder(),
                    ),
                    validator: (value) {
                      if (value != null && value.isNotEmpty) {
                        if (value.length != _configFormData.length) {
                          return '* Code must be length of ${_configFormData.length}.';
                        }

                        final validation = _localCaptchaController.validate(value);

                        switch (validation) {
                          case LocalCaptchaValidation.invalidCode:
                            return '* Invalid code.';
                          case LocalCaptchaValidation.codeExpired:
                            return '* Code expired.';
                          case LocalCaptchaValidation.valid:
                          default:
                            return null;
                        }
                      }

                      return '* Required field.';
                    },
                    onSaved: (value) => _inputCode = value ?? '',
                  ),
                  const SizedBox(height: 16.0),
                  SizedBox(
                    height: 40.0,
                    width: double.infinity,
                    child: ElevatedButton(
                      onPressed: () {
                        if (_captchaFormKey.currentState?.validate() ?? false) {
                          _captchaFormKey.currentState!.save();

                          showDialog(
                            context: context,
                            builder: (context) {
                              return AlertDialog(
                                title: Text('Code: "$_inputCode" is valid.'),
                                actions: [
                                  TextButton(
                                    onPressed: () => Navigator.of(context).pop(),
                                    child: const Text('OK'),
                                  ),
                                ],
                              );
                            },
                          );
                        }
                      },
                      child: const Text('Validate Code'),
                    ),
                  ),
                  const SizedBox(height: 16.0),
                  SizedBox(
                    height: 40.0,
                    width: double.infinity,
                    child: ValueListenableBuilder(
                        valueListenable: _refreshButtonEnableVN,
                        builder: (context, enable, child) {
                          final onPressed = enable
                              ? () {
                                  if (_refreshTimer == null) {
                                    // Prevent spam pressing refresh button.
                                    _refreshTimer = Timer(const Duration(seconds: 1), () {
                                      _refreshButtonEnableVN.value = true;

                                      _refreshTimer?.cancel();
                                      _refreshTimer = null;
                                    });

                                    _refreshButtonEnableVN.value = false;
                                    _localCaptchaController.refresh();
                                  }
                                }
                              : null;

                          return ElevatedButton(
                            onPressed: onPressed,
                            style: ElevatedButton.styleFrom(
                              backgroundColor: Colors.blueGrey,
                            ),
                            child: const Text('Refresh'),
                          );
                        }),
                  ),
                  const Padding(
                    padding: EdgeInsets.symmetric(vertical: 16.0),
                    child: Divider(),
                  ),
                  _configForm(context),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }

  Widget _configForm(BuildContext context) {
    return Form(
      key: _configFormKey,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Align(
            alignment: Alignment.centerLeft,
            child: Text(
              'Basic Configs',
              style: Theme.of(context).textTheme.titleLarge!,
            ),
          ),
          const SizedBox(height: 16.0),
          TextFormField(
            initialValue: _configFormData.chars,
            decoration: const InputDecoration(
              labelText: 'Captcha chars',
              hintText: 'Captcha chars',
              isDense: true,
              border: OutlineInputBorder(),
            ),
            validator: (value) {
              if (value != null && value.trim().isNotEmpty) {
                return null;
              }

              return '* Required field.';
            },
            onSaved: (value) => _configFormData.chars = value?.trim() ?? '',
          ),
          const SizedBox(height: 16.0),
          TextFormField(
            initialValue: '${_configFormData.length}',
            decoration: const InputDecoration(
              labelText: 'Captcha code length',
              hintText: 'Captcha code length',
              isDense: true,
              border: OutlineInputBorder(),
            ),
            validator: (value) {
              if (value != null && value.isNotEmpty) {
                final length = int.tryParse(value) ?? 0;

                if (length < 1) {
                  return '* Value must be greater than 0.';
                }

                return null;
              }

              return '* Required field.';
            },
            onSaved: (value) =>
                _configFormData.length = int.tryParse(value ?? '1') ?? 1,
          ),
          const SizedBox(height: 16.0),
          TextFormField(
            initialValue:
                '${_configFormData.fontSize > 0 ? _configFormData.fontSize : ''}',
            decoration: const InputDecoration(
              labelText: 'Font size (optional)',
              hintText: 'Font size (optional)',
              isDense: true,
              border: OutlineInputBorder(),
            ),
            onSaved: (value) =>
                _configFormData.fontSize = double.tryParse(value ?? '0.0') ?? 0.0,
          ),
          const SizedBox(height: 16.0),
          DropdownButtonFormField<bool>(
            value: _configFormData.caseSensitive,
            isDense: true,
            decoration: const InputDecoration(
              labelText: 'Case sensitive',
              hintText: 'Case sensitive',
              isDense: true,
              border: OutlineInputBorder(),
            ),
            items: const [
              DropdownMenuItem(
                value: false,
                child: Text('No'),
              ),
              DropdownMenuItem(
                value: true,
                child: Text('Yes'),
              ),
            ],
            onChanged: (value) => _configFormData.caseSensitive = value ?? false,
          ),
          const SizedBox(height: 16.0),
          TextFormField(
            initialValue: '${_configFormData.codeExpireAfter.inMinutes}',
            decoration: const InputDecoration(
              labelText: 'Code expire after (minutes)',
              hintText: 'Code expire after (minutes)',
              isDense: true,
              border: OutlineInputBorder(),
            ),
            validator: (value) {
              if (value != null && value.isNotEmpty) {
                final length = int.tryParse(value) ?? 0;

                if (length < 1) {
                  return '* Value must be greater than 0.';
                }

                return null;
              }

              return '* Required field.';
            },
            onSaved: (value) => _configFormData.codeExpireAfter =
                Duration(minutes: int.tryParse(value ?? '1') ?? 1),
          ),
          const SizedBox(height: 16.0),
          SizedBox(
            height: 40.0,
            width: double.infinity,
            child: ElevatedButton(
              onPressed: () {
                if (_configFormKey.currentState?.validate() ?? false) {
                  _configFormKey.currentState!.save();

                  setState(() {});
                }
              },
              child: const Text('Apply'),
            ),
          ),
        ],
      ),
    );
  }
}

class ConfigFormData {
  String chars = 'abdefghnryABDEFGHNQRY3468';
  int length = 5;
  double fontSize = 0;
  bool caseSensitive = false;
  Duration codeExpireAfter = const Duration(minutes: 10);

  @override
  String toString() {
    return '$chars$length$caseSensitive${codeExpireAfter.inMinutes}';
  }
}

通过以上步骤和示例代码,您可以轻松地将local_captcha集成到您的Flutter项目中,并根据需要自定义其外观和行为。希望这能帮助您更快地构建出原型或演示应用!


更多关于Flutter本地验证码生成插件local_captcha的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter本地验证码生成插件local_captcha的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中集成并使用local_captcha插件来生成本地验证码的示例代码。local_captcha插件允许你在Flutter应用中生成图形验证码,通常用于人机验证。

1. 添加依赖

首先,你需要在pubspec.yaml文件中添加local_captcha依赖:

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

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

2. 导入插件

在你的Dart文件中导入local_captcha插件:

import 'package:local_captcha/local_captcha.dart';

3. 使用插件生成验证码

以下是一个完整的示例,展示如何在Flutter应用中生成并显示验证码:

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

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

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

class CaptchaScreen extends StatefulWidget {
  @override
  _CaptchaScreenState createState() => _CaptchaScreenState();
}

class _CaptchaScreenState extends State<CaptchaScreen> {
  LocalCaptcha? _captcha;
  String? _captchaText;

  @override
  void initState() {
    super.initState();
    _initCaptcha();
  }

  void _initCaptcha() async {
    _captcha = await LocalCaptcha.builder()
        .width(200)
        .height(100)
        .length(6)
        .charType(CaptchaCharType.number)
        .bgColor(Colors.white)
        .lineColor(Colors.grey)
        .fontColor(Colors.black)
        .fontSize(24)
        .build();

    // Optionally, you can get the captcha text for verification purposes
    _captchaText = await _captcha!.captchaText();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Captcha Example'),
      ),
      body: Center(
        child: _captcha != null
            ? Image.memory(_captcha!.captchaImage!)
            : CircularProgressIndicator(),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          // Refresh captcha
          setState(() {
            _initCaptcha();
          });
        },
        tooltip: 'Refresh Captcha',
        child: Icon(Icons.refresh),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
    );
  }

  @override
  void dispose() {
    _captcha?.dispose();
    super.dispose();
  }
}

解释

  1. 添加依赖:在pubspec.yaml中添加local_captcha依赖。
  2. 导入插件:在Dart文件中导入local_captcha
  3. 初始化验证码:在initState方法中初始化验证码,并设置验证码的宽度、高度、字符长度、字符类型、背景颜色、线条颜色、字体颜色和字体大小。
  4. 显示验证码:使用Image.memory显示生成的验证码图像。
  5. 刷新验证码:通过点击浮动操作按钮刷新验证码。
  6. 释放资源:在dispose方法中释放验证码资源。

这个示例展示了如何在Flutter应用中生成和显示一个图形验证码。你可以根据实际需求调整验证码的参数,如字符类型、长度等。

回到顶部