From 959500372e17d2e377f7c665d60ce4a39436d5f4 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Thu, 25 Jul 2024 07:35:16 +0200 Subject: [PATCH] fix: close keyboard for break duration when tapping outside inputfield --- .../lib/src/ui/widgets/input_fields.dart | 90 ++++++++++++++----- 1 file changed, 69 insertions(+), 21 deletions(-) diff --git a/packages/flutter_availability/lib/src/ui/widgets/input_fields.dart b/packages/flutter_availability/lib/src/ui/widgets/input_fields.dart index 7ce7206..be3811a 100644 --- a/packages/flutter_availability/lib/src/ui/widgets/input_fields.dart +++ b/packages/flutter_availability/lib/src/ui/widgets/input_fields.dart @@ -1,4 +1,5 @@ import "package:flutter/material.dart"; +import "package:flutter/services.dart"; import "package:flutter_availability/src/util/scope.dart"; /// An input field for time selection @@ -56,7 +57,7 @@ class TimeInputField extends StatelessWidget { } /// An input field for giving a duration in minutes -class DurationInputField extends StatelessWidget { +class DurationInputField extends StatefulWidget { /// const DurationInputField({ required this.initialValue, @@ -70,6 +71,51 @@ class DurationInputField extends StatelessWidget { /// final void Function(Duration?) onDurationChanged; + @override + State createState() => _DurationInputFieldState(); +} + +class _DurationInputFieldState extends State { + OverlayEntry? _overlayEntry; + + @override + void dispose() { + _removeOverlay(); + super.dispose(); + } + + void _onFieldChanged(String value) { + var duration = int.tryParse(value); + if (duration != null) { + widget.onDurationChanged(Duration(minutes: duration)); + } else { + widget.onDurationChanged(null); + } + } + + void _showOverlay(BuildContext context) { + _overlayEntry = _createOverlayEntry(context); + Overlay.of(context).insert(_overlayEntry!); + } + + void _removeOverlay() { + _overlayEntry?.remove(); + _overlayEntry = null; + } + + OverlayEntry _createOverlayEntry(BuildContext context) => OverlayEntry( + builder: (context) => GestureDetector( + onTap: () { + FocusScope.of(context).unfocus(); + _removeOverlay(); + }, + behavior: HitTestBehavior.translucent, + child: Container( + color: Colors.transparent, + ), + ), + ); + @override Widget build(BuildContext context) { var theme = Theme.of(context); @@ -77,28 +123,30 @@ class DurationInputField extends StatelessWidget { var options = availabilityScope.options; var translations = options.translations; - void onFieldChanged(String value) { - var duration = int.tryParse(value); - if (duration != null) { - onDurationChanged(Duration(minutes: duration)); - } else { - onDurationChanged(null); - } - } - - return TextFormField( - decoration: InputDecoration( - labelText: translations.time, - labelStyle: theme.inputDecorationTheme.hintStyle, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), + return Focus( + onFocusChange: (hasFocus) { + if (hasFocus) { + _showOverlay(context); + } else { + _removeOverlay(); + } + }, + child: TextFormField( + decoration: InputDecoration( + labelText: translations.time, + labelStyle: theme.inputDecorationTheme.hintStyle, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + ), + suffixIcon: const Icon(Icons.access_time), ), - suffixIcon: const Icon(Icons.access_time), + keyboardType: TextInputType.number, + style: options.textStyles.inputFieldTextStyle, + onChanged: _onFieldChanged, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], ), - initialValue: initialValue?.inMinutes.toString(), - keyboardType: TextInputType.number, - style: options.textStyles.inputFieldTextStyle, - onChanged: onFieldChanged, ); } }