Skip to content

Instantly share code, notes, and snippets.

@CoderNamedHendrick
Created August 2, 2023 19:37
Show Gist options
  • Save CoderNamedHendrick/c363d94ff76b977107e493b5c545596c to your computer and use it in GitHub Desktop.
Save CoderNamedHendrick/c363d94ff76b977107e493b5c545596c to your computer and use it in GitHub Desktop.
Custom num pad
class CustomAmountKeyboard extends StatefulWidget {
const CustomAmountKeyboard({Key? key}) : super(key: key);
@override
State<CustomAmountKeyboard> createState() => _CustomAmountKeyboardState();
}
class _CustomAmountKeyboardState extends State<CustomAmountKeyboard> {
late final FocusNode focusNode;
late final TextEditingController controller;
final showNumpad = ValueNotifier(false);
@override
void initState() {
super.initState();
focusNode = FocusNode();
controller = TextEditingController();
focusNode.addListener(() {
showNumpad.value = focusNode.hasFocus;
});
}
@override
void dispose() {
focusNode.dispose();
controller.dispose();
showNumpad.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
keyboardAppearance: Brightness.dark,
keyboardType: TextInputType.none,
focusNode: focusNode,
controller: controller,
decoration: const InputDecoration(border: OutlineInputBorder()),
inputFormatters: const [AmountInputFormatter()],
),
ValueListenableBuilder<bool>(
valueListenable: showNumpad,
builder: (_, showing, child) =>
showing ? child! : const SizedBox.shrink(),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: TweenAnimationBuilder(
tween:
Tween(begin: const Offset(0, 1), end: const Offset(0, 0)),
duration: const Duration(milliseconds: 1200),
builder: (context, offset, child) {
return Transform.translate(offset: offset, child: child!);
},
child: _NumPad(onEntry: _numPadOnEntry),
),
),
SizedBox(height: MediaQuery.of(context).viewPadding.bottom),
],
),
),
],
);
}
void _numPadOnEntry(NumPadEntryEvent entryEvent) {
switch (entryEvent) {
case NumPadEntryEvent.one:
_enterValue('1');
case NumPadEntryEvent.two:
_enterValue('2');
case NumPadEntryEvent.three:
_enterValue('3');
case NumPadEntryEvent.four:
_enterValue('4');
case NumPadEntryEvent.five:
_enterValue('5');
case NumPadEntryEvent.six:
_enterValue('6');
case NumPadEntryEvent.seven:
_enterValue('7');
case NumPadEntryEvent.eight:
_enterValue('8');
case NumPadEntryEvent.nine:
_enterValue('9');
case NumPadEntryEvent.zero:
_enterValue('0');
case NumPadEntryEvent.point:
_enterValue('.');
case NumPadEntryEvent.delete:
if (controller.text.isEmpty) return;
final value = controller.value;
final textValue = toCurrencyString(
'${controller.text.substring(0, value.selection.extentOffset - 1)}${controller.text.substring(value.selection.extentOffset)}');
controller.value = TextEditingValue(
text: textValue,
selection: TextSelection.fromPosition(
TextPosition(offset: value.selection.extentOffset - 1)));
}
}
void _enterValue(String value) {
final textEditingValue = controller.value;
final textValue = toCurrencyString(
'${controller.text.substring(0, textEditingValue.selection.extentOffset)}$value${controller.text.substring(textEditingValue.selection.extentOffset)}');
controller.value = TextEditingValue(
text: textValue,
selection: TextSelection.fromPosition(
TextPosition(offset: textEditingValue.selection.extentOffset + 1)),
);
}
}
enum NumPadEntryEvent {
one,
two,
three,
four,
five,
six,
seven,
eight,
nine,
zero,
point,
delete;
}
class _NumPad extends StatefulWidget {
const _NumPad({Key? key, this.onEntry}) : super(key: key);
final ValueChanged<NumPadEntryEvent>? onEntry;
@override
State<_NumPad> createState() => _NumPadState();
}
class _NumPadState extends State<_NumPad> {
static const splashRadius = 65.0;
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Table(
defaultVerticalAlignment: TableCellVerticalAlignment.bottom,
children: [
TableRow(children: [
_buildNumEntry(1, onTap: () => _inputEntry(NumPadEntryEvent.one)),
_buildNumEntry(2, onTap: () => _inputEntry(NumPadEntryEvent.two)),
_buildNumEntry(3, onTap: () => _inputEntry(NumPadEntryEvent.three)),
]),
TableRow(children: [
_buildNumEntry(4, onTap: () => _inputEntry(NumPadEntryEvent.four)),
_buildNumEntry(5, onTap: () => _inputEntry(NumPadEntryEvent.five)),
_buildNumEntry(6, onTap: () => _inputEntry(NumPadEntryEvent.six)),
]),
TableRow(children: [
_buildNumEntry(7, onTap: () => _inputEntry(NumPadEntryEvent.seven)),
_buildNumEntry(8, onTap: () => _inputEntry(NumPadEntryEvent.eight)),
_buildNumEntry(9, onTap: () => _inputEntry(NumPadEntryEvent.nine)),
]),
TableRow(children: [
InkResponse(
onTap: () => _inputEntry(NumPadEntryEvent.point),
radius: splashRadius,
child: Text(
'.',
style: Theme.of(context).textTheme.displaySmall,
textAlign: TextAlign.center,
),
),
_buildNumEntry(0, onTap: () => _inputEntry(NumPadEntryEvent.zero)),
InkResponse(
onTap: () => _inputEntry(NumPadEntryEvent.delete),
radius: splashRadius,
child: Text(
'Delete',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
height: 2.5,
),
textAlign: TextAlign.center,
),
),
]),
],
);
}
Widget _buildNumEntry(int num, {VoidCallback? onTap}) {
return InkResponse(
onTap: onTap,
radius: splashRadius,
child: Text(
num.toString(),
style: Theme.of(context).textTheme.displaySmall,
textAlign: TextAlign.center,
),
);
}
void _inputEntry(NumPadEntryEvent event) {
widget.onEntry?.call(event);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment