Created
August 2, 2023 19:37
-
-
Save CoderNamedHendrick/c363d94ff76b977107e493b5c545596c to your computer and use it in GitHub Desktop.
Custom num pad
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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