Skip to content

Instantly share code, notes, and snippets.

@roipeker
Created February 10, 2021 12:09
Show Gist options
  • Save roipeker/4ec27f2f3e2010c41210a41391784bc0 to your computer and use it in GitHub Desktop.
Save roipeker/4ec27f2f3e2010c41210a41391784bc0 to your computer and use it in GitHub Desktop.
PIN code sample with hidden TextField.
/// roipeker 2021.
import 'package:cryptericon/utils/common.dart';
class EditPhoneConfirmationView extends GetView<MyTextInputController> {
static const id = '/contact-edit/phone/confirmation';
@override
Widget build(BuildContext context) {
Get.put(MyTextInputController());
return Scaffold(
appBar: CommonAppBar(
title: 'Phone',
actionIcon: null,
showBack: true,
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Center(
child: Container(
constraints: BoxConstraints(maxWidth: 500),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.start,
children: [
kGap18,
Text(
"Phone Verification",
style: TextStyle(
fontFamily: 'ProximaNova',
color: const Color(0xfff0f0f0),
fontSize: 20,
fontWeight: FontWeight.w700,
fontStyle: FontStyle.normal,
// letterSpacing: 0.15000000953674317,
letterSpacing: 7.5 / 20,
),
),
Gap(12),
Text(
"Enter the code that came in the message",
style: TextStyle(
fontFamily: 'ProximaNova',
color: const Color(0xfff0f0f0),
fontSize: 14,
fontWeight: FontWeight.w400,
),
),
_DigitBuilder(),
// kGap18,
// kGap32,
Obx(
() => SolidButton.solid(
onTap: controller.isFull ? () {} : null,
label: 'SEND',
),
),
],
),
),
),
),
);
}
}
class _DigitBuilder extends GetView<MyTextInputController> {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: controller.onTap,
child: Container(
color: Colors.transparent,
padding: EdgeInsets.symmetric(vertical: 33),
child: Stack(
alignment: Alignment.center,
children: [
FittedBox(
child: Obx(
() => Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
...List.generate(
controller.numChars,
(index) => _DigitInput(
value: controller.getSlotValue(index),
state: controller.getSlowState(index),
hasGap: index < controller.numChars,
),
),
],
),
),
),
Offstage(
offstage: true,
child: TextField(
maxLines: 1,
maxLength: 6,
maxLengthEnforcement: MaxLengthEnforcement.enforced,
expands: false,
autocorrect: false,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
FilteringTextInputFormatter.singleLineFormatter,
],
focusNode: controller.hiddenFocusNode,
controller: controller.hiddenController,
),
),
],
),
),
);
}
}
enum DigitInputState { empty, focused, entered }
class _DigitInput extends StatelessWidget {
final String value;
final bool hasGap;
final DigitInputState state;
const _DigitInput({
Key key,
this.hasGap = false,
this.state = DigitInputState.empty,
this.value = '1',
}) : super(key: key);
BoxDecoration _getDecoration() {
if (state == DigitInputState.focused) {
return BoxDecoration(
color: Color(0xff282835),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppColors.macaroniAndCheese, width: 3),
);
} else if (state == DigitInputState.entered) {
return BoxDecoration(
color: Color(0xff282835).withOpacity(.8),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Color(0xff73CA86).withOpacity(1), width: .8),
// border: Border.all(color: Color(0xff73CA86).withOpacity(1), width: 1),
);
}
/// empty
return BoxDecoration(
color: Color(0xff282835).withOpacity(.2),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Color(0xff393A4B), width: 1),
);
}
@override
Widget build(BuildContext context) {
BoxDecoration deco = _getDecoration();
return AnimatedContainer(
duration: .3.seconds,
width: 50,
height: 60,
margin: hasGap ? EdgeInsets.only(right: 16) : null,
decoration: deco,
alignment: Alignment.center,
child: Text(
value,
style: const TextStyle(
fontFamily: 'ProximaNova',
color: const Color(0xfff0f0f0),
fontSize: 24,
fontWeight: FontWeight.w600,
),
),
);
}
}
class MyTextInputController extends GetxController {
TextEditingController hiddenController = TextEditingController();
FocusNode hiddenFocusNode = FocusNode();
final _input = ''.obs;
final numChars = 6;
@override
void onReady() {
super.onReady();
hiddenController.addListener(() {
var txt = hiddenController.text;
final maxLen = Math.min(numChars, txt.length);
_input.value = txt.substring(0, maxLen);
});
}
bool get isFull {
var len = _input().length;
return len >= 6;
}
String getSlotValue(int index) {
var str = _input();
if (str.length <= index) {
return '';
}
return str[index];
}
DigitInputState getSlowState(int index) {
var str = _input();
if (index == str.length) {
return DigitInputState.focused;
} else if (index < str.length) {
return DigitInputState.entered;
}
return DigitInputState.empty;
}
@override
void onClose() {
super.onClose();
hiddenFocusNode?.dispose();
hiddenController?.dispose();
_input?.close();
}
void onTap() {
hiddenFocusNode.requestFocus();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment