Flutter show datepicker with keypress

645

So I resolved my problem. But i don't know how to edit my first message or closing it..

I just extract the method in the onTap to create an other method, and use it in the ontap and in the _handleKeyPress method.

Also I adjust an other problem to the focus when the new methode is call.

In fact it was simple...

I post the code if it can be helpful for someone :)

//#################################################################################################
/// Displays a Material date picker.
class DateInput extends StatefulWidget {
  final Map<String, dynamic> properties;
  final int lines;
  final TCell templateCell;
  final modelFormField.FormField<DateTime> formField;
  final String fieldName;
  final bool isFormReadonly;

  //-----------------------------------------------------------------------------------------------
  DateInput(
      {@required this.properties,
      @required this.lines,
      @required this.templateCell,
      @required this.formField,
      @required this.fieldName,
      @required this.isFormReadonly,
      Key key})
      : super(key: key);

  //-----------------------------------------------------------------------------------------------
  @override
  _AfiDatePickerState createState() => _AfiDatePickerState(formField);
}

//#################################################################################################
class _AfiDatePickerState extends ElementState<DateInput> {
  TextEditingController _dateController;
  String _selectedValue;
  FocusNode _fieldFocusNode;
  FocusAttachment _nodeAttachment;
  bool _focused = false;

  //-----------------------------------------------------------------------------------------------
  _AfiDatePickerState(modelFormField.FormField formField) : super(formField);

  //-----------------------------------------------------------------------------------------------
  @override
  void initState() {
    super.initState();
    _dateController = TextEditingController(
        text: widget.formField.valueAccessor.modelToViewValue(widget.formField.value));
    _selectedValue = _dateController.text;
    initStreamListeners(widget.formField);
    _dateController.addListener(_onUserUpdateValue);
    _fieldFocusNode = FocusNode();
    _fieldFocusNode.addListener(_onFocusChanges);
    _nodeAttachment = _fieldFocusNode.attach(context, onKey: _handleKeyPress);
  }

  //-----------------------------------------------------------------------------------------------
  void _onUserUpdateValue() {
    String newDate = _dateController.text;

    // This is a necessary check. If we don't do this, the rules will be executed everytime the
    // date picker is opened.
    if (newDate != _selectedValue) {
      setState(() {
        _selectedValue = newDate;
      });
      widget.formField.updateValue(widget.formField.valueAccessor.viewToModelValue(newDate));
      formField.markAsDirty();
      formField.markAsTouched();
    }
  }

  //-----------------------------------------------------------------------------------------------
  @override
  void onValueChanges(value) {
    if (mounted) {
      setState(() {
        _selectedValue = widget.formField.valueAccessor.modelToViewValue(value);
        _dateController.text = _selectedValue;
      });
    }
  }

  //-----------------------------------------------------------------------------------------------
  Future<Null> _onFocusChanges() async {
    if (_fieldFocusNode.hasFocus != _focused) {
      setState(() {
        _focused = _fieldFocusNode.hasFocus;
      });
    }
  }
  //-----------------------------------------------------------------------------------------------
  void activeDatePicker()  async {
      DateTime initialDate = widget.formField.value ?? DateTime.now();
      final DateTime pickedDate = await showDatePicker(
          context: context,
          initialDate: initialDate,
          firstDate: DateTime(2000),
          lastDate: DateTime(2101),
          cancelText: translate(I18n.CORE_ERASE),
          confirmText: translate(I18n.CORE_OK),
          builder: (BuildContext context, Widget child) {
            return MediaQuery(
              data: MediaQuery.of(context).copyWith(alwaysUse24HourFormat: true, accessibleNavigation: true),
              child: child,
            );
        }
      );
      if (_focused) {
        _fieldFocusNode.unfocus();
      } else {
        _fieldFocusNode.requestFocus();
      }
      if (pickedDate == null) {
        setState(() {
          _dateController.text = '';
        });
      } else {
        setState(() {
          _dateController.text = widget.formField.valueAccessor.modelToViewValue(pickedDate);
        });
      }
    }

  //-----------------------------------------------------------------------------------------------
  KeyEventResult _handleKeyPress(FocusNode node, RawKeyEvent event) {
    if (event is RawKeyDownEvent) {
      if (event.logicalKey == LogicalKeyboardKey.space || event.logicalKey == LogicalKeyboardKey.enter) {
        activeDatePicker();
        return KeyEventResult.handled;
      }
    }
      return KeyEventResult.ignored;
  }
  //-----------------------------------------------------------------------------------------------
  @override
  void dispose() {
    _dateController.removeListener(_onUserUpdateValue);
    _dateController.dispose();
    _fieldFocusNode.dispose();
    super.dispose();
  }

  //-----------------------------------------------------------------------------------------------
  @override
  void refreshValidationMessages() {
    validationMessages = '';
    Set<String> validationKeys = {};
    RegExp dateRegex = RegExp(r'^(\d{2}/\d{2}/\d{4})?$');

    if (!dateRegex.hasMatch(_dateController.text)) {
      validationKeys.add(ValidationMessage.pattern);
    }

    validationKeys.forEach((key) {
      if (modelFormField.FormField.validatorMessagesI18N.containsKey(key)) {
        validationMessages += translate(modelFormField.FormField.validatorMessagesI18N[key]);
        validationMessages += ' ';
      }
    });
  }

  //-----------------------------------------------------------------------------------------------
  @override
  Widget build(BuildContext context) {
    refreshValidationMessages();
    final formProvider = Provider.of<FormProvider>(context);
    final alerts = formProvider.getFieldAlerts(
      widget.fieldName,
    );
    bool isReadonly = isFieldReadonly(
        formProvider, widget.fieldName, widget.templateCell.control, widget.isFormReadonly);
    InputDecoration inputDecoration =
        buildInputDecoration(alerts, isReadonly, isValid: validationMessages.isEmpty);

    addAlertsToValidationMessage(alerts, formProvider.form.template);

    _nodeAttachment.reparent();

    
  //-----------------------------------------------------------------------------------------------
     
    return wrapAlertTooltip(
      Container(
        height: 30,
        child: MouseRegion(
          cursor: SystemMouseCursors.click,
          child: GestureDetector(
            child: InputDecorator(
              decoration: inputDecoration,
              isFocused: _focused,
              child: Text(
                _dateController.text,
                overflow: TextOverflow.visible,
                maxLines: 1,
                style: TextStyle(fontSize: 16.0),
              ),
            ),
            onTap: () {
              if (!isReadonly) {
               activeDatePicker();
              }
            } ,
          ),
        ),
      ),
      alerts,
      validationMessages,
    );
  }
}

Share:
645
Gino Chasles
Author by

Gino Chasles

Updated on December 31, 2022

Comments

  • Gino Chasles
    Gino Chasles over 1 year

    this is my first question, I'm sorry if I'm not clear for what I'm written (I'm French and don't work my english..)

    So I'm newby in Flutter and World of Dev, I work on a project who display forms. Here we have a input who onTap return a DateTime to select a date. No problems. But the input don't accept focus so we add FocusNode. I modify the code to add visually feedback on focus input. But it's not possible to open the date picker without tap the input. We want to open it when space or enter while pressed and preserve the existant way.

    So i had a method KeyEventResult for these keyboard keys. But i don't know how to return the datepicker when we press them and to return to the page. I try to set a bool when we pressed these keys to show the date picker but the build function returned null and after press tab show the datepicker but i can't leave it. I don't know how I really need to use it..

    I show you the initial code I have and the second with my last modifications.. I hope your help, I think it's not hard but I miss competences.... Thanks for your help !

    Initial Code with first implementation of focus

    /// Displays a Material date picker.
    class DateInput extends StatefulWidget {
     final Map<String, dynamic> properties;
     final int lines;
     final TCell templateCell;
     final modelFormField.FormField<DateTime> formField;
     final String fieldName;
     final bool isFormReadonly;
    
     //-----------------------------------------------------------------------------------------------
     DateInput(
         {@required this.properties,
         @required this.lines,
         @required this.templateCell,
         @required this.formField,
         @required this.fieldName,
         @required this.isFormReadonly,
         Key key})
         : super(key: key);
    
     //-----------------------------------------------------------------------------------------------
     @override
     _AfiDatePickerState createState() => _AfiDatePickerState(formField);
    }
    
    //#################################################################################################
    class _AfiDatePickerState extends ElementState<DateInput> {
     TextEditingController _dateController;
     String _selectedValue;
     FocusNode _fieldFocusNode;
     FocusAttachment _nodeAttachment;
    
     //-----------------------------------------------------------------------------------------------
     _AfiDatePickerState(modelFormField.FormField formField) : super(formField);
    
     //-----------------------------------------------------------------------------------------------
     @override
     void initState() {
       super.initState();
       _dateController = TextEditingController(
           text: widget.formField.valueAccessor.modelToViewValue(widget.formField.value));
       _selectedValue = _dateController.text;
       initStreamListeners(widget.formField);
       _dateController.addListener(_onUserUpdateValue);
       _fieldFocusNode = FocusNode();
       _fieldFocusNode.addListener(_onFocusChanges);
       _nodeAttachment = _fieldFocusNode.attach(context);
     }
    
     //-----------------------------------------------------------------------------------------------
     void _onUserUpdateValue() {
       String newDate = _dateController.text;
    
       // This is a necessary check. If we don't do this, the rules will be executed everytime the
       // date picker is opened.
       if (newDate != _selectedValue) {
         setState(() {
           _selectedValue = newDate;
         });
         widget.formField.updateValue(widget.formField.valueAccessor.viewToModelValue(newDate));
         formField.markAsDirty();
         formField.markAsTouched();
       }
     }
    
     //-----------------------------------------------------------------------------------------------
     @override
     void onValueChanges(value) {
       if (mounted) {
         setState(() {
           _selectedValue = widget.formField.valueAccessor.modelToViewValue(value);
           _dateController.text = _selectedValue;
         });
       }
     }
    
     //-----------------------------------------------------------------------------------------------
     void _onFocusChanges() {}
    
     //-----------------------------------------------------------------------------------------------
     @override
     void dispose() {
       _dateController.removeListener(_onUserUpdateValue);
       _dateController.dispose();
       _fieldFocusNode.dispose();
       super.dispose();
     }
    
     //-----------------------------------------------------------------------------------------------
     @override
     void refreshValidationMessages() {
       validationMessages = '';
       Set<String> validationKeys = {};
       RegExp dateRegex = RegExp(r'^(\d{2}/\d{2}/\d{4})?$');
    
       if (!dateRegex.hasMatch(_dateController.text)) {
         validationKeys.add(ValidationMessage.pattern);
       }
    
       validationKeys.forEach((key) {
         if (modelFormField.FormField.validatorMessagesI18N.containsKey(key)) {
           validationMessages += translate(modelFormField.FormField.validatorMessagesI18N[key]);
           validationMessages += ' ';
         }
       });
     }
    
     //-----------------------------------------------------------------------------------------------
     @override
     Widget build(BuildContext context) {
       refreshValidationMessages();
       final formProvider = Provider.of<FormProvider>(context);
       final alerts = formProvider.getFieldAlerts(
         widget.fieldName,
       );
       bool isReadonly = isFieldReadonly(
           formProvider, widget.fieldName, widget.templateCell.control, widget.isFormReadonly);
       InputDecoration inputDecoration =
           buildInputDecoration(alerts, isReadonly, isValid: validationMessages.isEmpty);
    
       addAlertsToValidationMessage(alerts, formProvider.form.template);
    
       _nodeAttachment.reparent();
    
       return wrapAlertTooltip(
         Container(
           height: 30,
           child: MouseRegion(
             cursor: SystemMouseCursors.click,
             child: GestureDetector(
               child: InputDecorator(
                 decoration: inputDecoration,
                 child: Text(
                   _dateController.text,
                   overflow: TextOverflow.visible,
                   maxLines: 1,
                   style: TextStyle(fontSize: 16.0),
                 ),
               ),
               onTap: () async {
                 if (!isReadonly) {
                   DateTime initialDate = widget.formField.value ?? DateTime.now();
                   final DateTime pickedDate = await showDatePicker(
                       context: context,
                       initialDate: initialDate,
                       firstDate: DateTime(2000),
                       lastDate: DateTime(2101),
                       cancelText: translate(I18n.CORE_ERASE),
                       confirmText: translate(I18n.CORE_OK),
                       builder: (BuildContext context, Widget child) {
                         return MediaQuery(
                           data: MediaQuery.of(context).copyWith(alwaysUse24HourFormat: true, accessibleNavigation: true),
                           child: child,
                         );
                     }
                   );
    
                   if (pickedDate == null) {
                     setState(() {
                       _dateController.text = '';
                     });
                   } else {
                     setState(() {
                       _dateController.text = widget.formField.valueAccessor.modelToViewValue(pickedDate);
                     });
                   }
                 }
               },
             ),
           ),
         ),
         alerts,
         validationMessages,
       );
     }
    }
    
    

    My modif'

    import 'package:afi_dto/template/t_cell.dart';
    import 'package:afi_flutter_client/config/i18n/i18n.dart';
    import 'package:afi_flutter_client/models/form/form_field.dart' as modelFormField;
    import 'package:afi_flutter_client/services/providers/form_provider.dart';
    import 'package:afi_flutter_client/ui/forms/input/element_state.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    import 'package:flutter_translate/global.dart';
    import 'package:provider/provider.dart';
    import 'package:reactive_forms/reactive_forms.dart';
    
    //#################################################################################################
    /// Displays a Material date picker.
    class DateInput extends StatefulWidget {
      final Map<String, dynamic> properties;
      final int lines;
      final TCell templateCell;
      final modelFormField.FormField<DateTime> formField;
      final String fieldName;
      final bool isFormReadonly;
    
      //-----------------------------------------------------------------------------------------------
      DateInput(
          {@required this.properties,
          @required this.lines,
          @required this.templateCell,
          @required this.formField,
          @required this.fieldName,
          @required this.isFormReadonly,
          Key key})
          : super(key: key);
    
      //-----------------------------------------------------------------------------------------------
      @override
      _AfiDatePickerState createState() => _AfiDatePickerState(formField);
    }
    
    //#################################################################################################
    class _AfiDatePickerState extends ElementState<DateInput> {
      TextEditingController _dateController;
      String _selectedValue;
      FocusNode _fieldFocusNode;
      FocusAttachment _nodeAttachment;
      bool _focused = false;
      bool _setDatePicker = false;
    
      //-----------------------------------------------------------------------------------------------
      _AfiDatePickerState(modelFormField.FormField formField) : super(formField);
    
      //-----------------------------------------------------------------------------------------------
      @override
      void initState() {
        super.initState();
        _dateController = TextEditingController(
            text: widget.formField.valueAccessor.modelToViewValue(widget.formField.value));
        _selectedValue = _dateController.text;
        initStreamListeners(widget.formField);
        _dateController.addListener(_onUserUpdateValue);
        _fieldFocusNode = FocusNode();
        _fieldFocusNode.addListener(_onFocusChanges);
        _nodeAttachment = _fieldFocusNode.attach(context, onKey: _handleKeyPress);
      }
    
      //-----------------------------------------------------------------------------------------------
      void _onUserUpdateValue() {
        String newDate = _dateController.text;
    
        // This is a necessary check. If we don't do this, the rules will be executed everytime the
        // date picker is opened.
        if (newDate != _selectedValue) {
          setState(() {
            _selectedValue = newDate;
          });
          widget.formField.updateValue(widget.formField.valueAccessor.viewToModelValue(newDate));
          formField.markAsDirty();
          formField.markAsTouched();
        }
      }
    
      //-----------------------------------------------------------------------------------------------
      @override
      void onValueChanges(value) {
        if (mounted) {
          setState(() {
            _selectedValue = widget.formField.valueAccessor.modelToViewValue(value);
            _dateController.text = _selectedValue;
          });
        }
      }
    
      //-----------------------------------------------------------------------------------------------
      Future<Null> _onFocusChanges() async {
        if (_fieldFocusNode != _focused) {
          setState(() {
            _focused = _fieldFocusNode.hasFocus;
          });
        }
      }
      //-----------------------------------------------------------------------------------------------
      KeyEventResult _handleKeyPress(FocusNode node, RawKeyEvent event) {
        if (event is RawKeyDownEvent) {
          if (event.logicalKey == LogicalKeyboardKey.space || event.logicalKey == LogicalKeyboardKey.enter) {
            setState(() {
              _setDatePicker = true;
            });
            print("key pressed: $event.logicalKey");
            return KeyEventResult.handled;
          }
        }
          return KeyEventResult.ignored;
      }
      //-----------------------------------------------------------------------------------------------
      @override
      void dispose() {
        _dateController.removeListener(_onUserUpdateValue);
        _dateController.dispose();
        _fieldFocusNode.dispose();
        super.dispose();
      }
    
      //-----------------------------------------------------------------------------------------------
      @override
      void refreshValidationMessages() {
        validationMessages = '';
        Set<String> validationKeys = {};
        RegExp dateRegex = RegExp(r'^(\d{2}/\d{2}/\d{4})?$');
    
        if (!dateRegex.hasMatch(_dateController.text)) {
          validationKeys.add(ValidationMessage.pattern);
        }
    
        validationKeys.forEach((key) {
          if (modelFormField.FormField.validatorMessagesI18N.containsKey(key)) {
            validationMessages += translate(modelFormField.FormField.validatorMessagesI18N[key]);
            validationMessages += ' ';
          }
        });
      }
    
      //-----------------------------------------------------------------------------------------------
      @override
      Widget build(BuildContext context) {
        refreshValidationMessages();
        final formProvider = Provider.of<FormProvider>(context);
        final alerts = formProvider.getFieldAlerts(
          widget.fieldName,
        );
        bool isReadonly = isFieldReadonly(
            formProvider, widget.fieldName, widget.templateCell.control, widget.isFormReadonly);
        InputDecoration inputDecoration =
            buildInputDecoration(alerts, isReadonly, isValid: validationMessages.isEmpty);
    
        addAlertsToValidationMessage(alerts, formProvider.form.template);
    
        _nodeAttachment.reparent();
    
      //-----------------------------------------------------------------------------------------------
        Future _datePicker() async {
                  if (!isReadonly) {
                    DateTime initialDate = widget.formField.value ?? DateTime.now();
                    final DateTime pickedDate = await showDatePicker(
                        context: context,
                        initialDate: initialDate,
                        firstDate: DateTime(2000),
                        lastDate: DateTime(2101),
                        cancelText: translate(I18n.CORE_ERASE),
                        confirmText: translate(I18n.CORE_OK),
                        builder: (BuildContext context, Widget child) {
                          return MediaQuery(
                            data: MediaQuery.of(context).copyWith(alwaysUse24HourFormat: true, accessibleNavigation: true),
                            child: child,
                          );
                      }
                    );
    
                    if (pickedDate == null) {
                      setState(() {
                        _dateController.text = '';
                        _focused = true;
                      });
                    } else {
                      setState(() {
                        _dateController.text = widget.formField.valueAccessor.modelToViewValue(pickedDate);
                        _focused = true;
                      });
                    }
                  }
                }
      //-----------------------------------------------------------------------------------------------
         if ( _setDatePicker) {
            DateTime initialDate = widget.formField.value ?? DateTime.now();
            showDatePicker(
                        context: context,
                        initialDate: initialDate,
                        firstDate: DateTime(2000),
                        lastDate: DateTime(2101),
                        cancelText: translate(I18n.CORE_ERASE),
                        confirmText: translate(I18n.CORE_OK),
                        builder: (BuildContext context, Widget child) {
                          return MediaQuery(
                            data: MediaQuery.of(context).copyWith(alwaysUse24HourFormat: true, accessibleNavigation: true),
                            child: child,
                          );
                      }
                    );
         } else {
        return wrapAlertTooltip(
          Container(
            height: 30,
            child: MouseRegion(
              cursor: SystemMouseCursors.click,
              child: GestureDetector(
                child: InputDecorator(
                  decoration: inputDecoration,
                  isFocused: _focused,
                  child: Text(
                    _dateController.text,
                    overflow: TextOverflow.visible,
                    maxLines: 1,
                    style: TextStyle(fontSize: 16.0),
                  ),
                ),
                onTap: _datePicker ,
              ),
            ),
          ),
          alerts,
          validationMessages,
        );
      }
    }
    }