bitcaptcha_flutter 0.6.2
bitcaptcha_flutter: ^0.6.2 copied to clipboard
Flutter widgets for BitCaptcha, the human verification service by Xinghui Shudun - smart invisible check, slide puzzle, text and icon click, inline or dialog.
example/lib/main.dart
import 'package:bitcaptcha_flutter/bitcaptcha_flutter.dart';
import 'package:flutter/material.dart';
// 'demo' is the public playground site. Use your own captcha_id from the console in production.
// A client-side pass only yields the one-time ticket r.validate; your backend must verify it with the secret_key.
const kApiBase = 'https://shudun.bitbeam.cn';
const kCaptchaId = 'demo';
void main() => runApp(const ExampleApp());
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) => MaterialApp(
title: 'BitCaptcha Example',
theme: ThemeData(colorSchemeSeed: const Color(0xFF3B82F6), useMaterial3: true),
home: const ExamplePage(),
);
}
class ExamplePage extends StatefulWidget {
const ExamplePage({super.key});
@override
State<ExamplePage> createState() => _ExamplePageState();
}
class _ExamplePageState extends State<ExamplePage> {
final List<String> _log = [];
void _ok(String what, BitCaptchaResult r) => setState(() => _log.insert(0, '$what passed validate=${r.validate.substring(0, 12)}…'));
void _err(String what, Object e) => setState(() => _log.insert(0, '$what error $e'));
Widget _section(String title, Widget child) => Padding(
padding: const EdgeInsets.only(bottom: 24),
child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
Text(title, style: const TextStyle(fontWeight: FontWeight.w600)),
const SizedBox(height: 8),
child,
]),
);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('BitCaptcha Example')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_section(
'Smart invisible check · one tap; suspicious requests get a puzzle',
BitCaptchaSense(
captchaId: kCaptchaId,
apiBase: kApiBase,
onVerify: (r) => _ok('sense', r),
onError: (e) => _err('sense', e),
),
),
_section(
'Slide puzzle',
BitCaptcha(
captchaId: kCaptchaId,
apiBase: kApiBase,
onVerify: (r) => _ok('slider', r),
onError: (e) => _err('slider', e),
),
),
_section(
'Text click',
BitCaptchaText(
captchaId: kCaptchaId,
apiBase: kApiBase,
onVerify: (r) => _ok('text', r),
onError: (e) => _err('text', e),
),
),
_section(
'Icon click',
BitCaptchaText(
captchaId: kCaptchaId,
apiBase: kApiBase,
icon: true,
onVerify: (r) => _ok('icon', r),
onError: (e) => _err('icon', e),
),
),
_section(
'Dialogs · return null when closed',
Wrap(spacing: 12, runSpacing: 8, children: [
FilledButton(
onPressed: () async {
final r = await showBitCaptcha(context, captchaId: kCaptchaId, apiBase: kApiBase);
if (r == null) return _err('slider dialog', 'closed');
_ok('slider dialog', r);
},
child: const Text('Open slide puzzle'),
),
FilledButton.tonal(
onPressed: () async {
final r = await showBitCaptchaText(context, captchaId: kCaptchaId, apiBase: kApiBase);
if (r == null) return _err('text dialog', 'closed');
_ok('text dialog', r);
},
child: const Text('Open text click'),
),
]),
),
_section(
'Events',
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [for (final line in _log) Text(line, style: const TextStyle(fontFamily: 'Menlo', fontSize: 12))],
),
),
],
),
);
}
}