Flutter. Как использовать PIN-код
FlutterPulseЭта статья переведена специально для канала FlutterPulse. В этом канале вы найдёте много интересных вещей, связанных с Flutter. Не забывайте подписываться! 🚀

Мне нужна страница с PIN-кодом для одного из моих приложений. Использование очень простое: пользователь просто разблокирует веб-приложение с помощью PIN-кода, который я отправляю ему через WhatsApp.
Мне нужна страница с PIN-кодом для одного из моих приложений. Использование очень простое: пользователь просто разблокирует веб-приложение с помощью PIN-кода, который я отправляю ему через WhatsApp.
Если вы являетесь членом, пожалуйста, продолжайте,иначе, читайте полную историю здесь.

На pub.dev есть несколько пакетов именно для этого с явным лидером—pinput.

Мне нравятся эти кнопки в виде кофе.
Пакет легко использовать. Вот базовый рабочий процесс:

В случае неправильного PIN-кода отображается сообщение об ошибке, в случае правильного PIN-кода пользователь перенаправляется на страницу Welcome.
Вот наш контроллер:
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:getx_miscellanous/app/routes/app_pages.dart';
class PinCodeController extends GetxController {
String secretPin = '1234';
bool isWrong = false;
final pinController = TextEditingController();
final focusNode = FocusNode();
void onCompleted(String pin) {
pinController.value = TextEditingValue.empty;
update();
if (pin == secretPin) {
pinController.value = TextEditingValue.empty;
Get.toNamed(Routes.WELCOME);
return;
}
isWrong = true;
update();
}
void onChange(val) {
isWrong = false;
update();
}
void clearPin() {
pinController.value = TextEditingValue.empty;
update();
}
@override
void onClose() {
pinController.dispose();
focusNode.dispose();
super.onClose();
}
}
Я просто жестко задал секретный код для простоты.
А вот и View:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pinput/pinput.dart';
import 'pin_code_controller.dart';
class PinCodeView extends GetView<PinCodeController> {
const PinCodeView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Enter PIN'),
centerTitle: true,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
spacing: 10,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Enter 4-digit PIN code',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 22),
GetBuilder<PinCodeController>(builder: (controller) {
return Column(
spacing: 5,
children: [
Pinput(
length: 4,
onCompleted: (pin) => controller.onCompleted(pin),
onChanged: (value) => controller.onChange(value),
obscureText: true,
showCursor: true,
controller: controller.pinController,
focusNode: controller.focusNode,
),
Text(controller.isWrong ? 'wrong pin' : '',
style: TextStyle(
color: Colors.red,
))
],
);
}),
const SizedBox(height: 32),
TextButton(
onPressed: controller.clearPin,
child: const Text('Clear'),
),
],
),
),
);
}
}
Когда пользователь заканчивает ввод PIN-кода, вызывается метод onCompleted контроллера:
void onCompleted(String pin) {
pinController.value = TextEditingValue.empty;
update();
if (pin == secretPin) {
pinController.value = TextEditingValue.empty;
Get.toNamed(Routes.WELCOME);
return;
}
isWrong = true;
update();
}Сначала мы очищаем поле PIN, затем проверяем, правильный ли PIN, и либо перенаправляем пользователя на страницу Welcome или показываем сообщение wrong pin.
Всё работает, и мы можем остановиться здесь, но пакет pinput предлагает несколько шаблонов которые делают его более привлекательным визуально.
Добавим один из них к нашему примеру:
import 'package:flutter/material.dart';
import 'package:pinput/pinput.dart';
mixin PinThemeHelper {
final defaultPinTheme = PinTheme(
width: 56,
height: 60,
textStyle: TextStyle(
fontSize: 22,
color: const Color.fromRGBO(30, 60, 87, 1),
),
decoration: BoxDecoration(
color: Color.fromRGBO(222, 231, 240, .57),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.transparent),
),
);
final focusPinTheme = PinTheme(
height: 68,
width: 64,
textStyle: TextStyle(
fontSize: 22,
color: const Color.fromRGBO(30, 60, 87, 1),
),
decoration: BoxDecoration(
border: Border.all(color: Colors.blue.shade200),
),
);
final cursor = Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
margin: const EdgeInsets.only(bottom: 9),
width: 22,
height: 3,
color: Colors.blue,
),
],
);
}
Чтобы сохранить объектно-ориентированный подход, я создал миксин PinThemeHelper.
Мы больше не можем сделать наш PinCodeView const, но это небольшая проблема.
class PinCodeView extends GetView<PinCodeController> with PinThemeHelper{
PinCodeView({super.key});
Теперь мы можем просто добавить несколько свойств к нашему виджету PinPut:
Pinput(
length: 4,
defaultPinTheme: defaultPinTheme, //<-
focusedPinTheme: focusPinTheme, //<-
cursor: cursor, //<-
onCompleted: (pin) => controller.onCompleted(pin),
onChanged: (value) => controller.onChange(value),
obscureText: true,
showCursor: true,
controller: controller.pinController,
focusNode: controller.focusNode,
pinAnimationType: PinAnimationType.rotation,
),
Вот результат:

Чтобы настроить виджет PinPut, нам нужно только создать defaultPinTheme и focusedPinTheme (и, при необходимости, cursor), и пакет создаст плавные анимированные переходы.
Это всё о базовом использовании. Пакет можно использовать для сценария OTP (One-time Password), но это тема другой статьи.
Спасибо за чтение!
