Flutter 使用按键显示日期选择器
Flutter show datepicker with keypress
这是我的第一个问题,如果我不清楚我所写的内容,我深表歉意(我是法国人,不会用我的英语..)
所以我是 Flutter 和开发世界的新手,我从事一个显示表单的项目。这里我们有一个输入 who onTap return 一个 DateTime 到 select 一个日期。没问题。但是输入不接受焦点,所以我们添加 FocusNode。我修改代码以添加对焦点输入的视觉反馈。但是如果不点击输入就无法打开日期选择器。我们想在 space 时打开它或在按下时输入并保留现有的方式。
所以我有一个用于这些键盘键的方法 KeyEventResult。但是我不知道如何在我们按下日期选择器时 return 以及如何 return 到页面。当我们按下这些键以显示日期选择器时,我尝试设置一个布尔值,但构建函数 returned null 并在按下选项卡后显示日期选择器,但我不能离开它。我不知道我真的需要如何使用它..
我给你看我的初始代码和我最后修改的第二个代码。我希望你的帮助,我认为这并不难,但我想念能力....感谢你的帮助!
首次实现焦点的初始代码
/// 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,
);
}
}
我的修改'
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,
);
}
}
}
所以我解决了我的问题。但是我不知道如何编辑我的第一条消息或关闭它..
我只是提取 onTap 中的方法来创建另一个方法,并在 ontap 和 _handleKeyPress 方法中使用它。
另外我在调用新方法的时候调整了另外一个问题
其实很简单...
我post代码如果它可以对某人有帮助:)
//#################################################################################################
/// 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,
);
}
}
这是我的第一个问题,如果我不清楚我所写的内容,我深表歉意(我是法国人,不会用我的英语..)
所以我是 Flutter 和开发世界的新手,我从事一个显示表单的项目。这里我们有一个输入 who onTap return 一个 DateTime 到 select 一个日期。没问题。但是输入不接受焦点,所以我们添加 FocusNode。我修改代码以添加对焦点输入的视觉反馈。但是如果不点击输入就无法打开日期选择器。我们想在 space 时打开它或在按下时输入并保留现有的方式。
所以我有一个用于这些键盘键的方法 KeyEventResult。但是我不知道如何在我们按下日期选择器时 return 以及如何 return 到页面。当我们按下这些键以显示日期选择器时,我尝试设置一个布尔值,但构建函数 returned null 并在按下选项卡后显示日期选择器,但我不能离开它。我不知道我真的需要如何使用它..
我给你看我的初始代码和我最后修改的第二个代码。我希望你的帮助,我认为这并不难,但我想念能力....感谢你的帮助!
首次实现焦点的初始代码
/// 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,
);
}
}
我的修改'
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,
);
}
}
}
所以我解决了我的问题。但是我不知道如何编辑我的第一条消息或关闭它..
我只是提取 onTap 中的方法来创建另一个方法,并在 ontap 和 _handleKeyPress 方法中使用它。
另外我在调用新方法的时候调整了另外一个问题
其实很简单...
我post代码如果它可以对某人有帮助:)
//#################################################################################################
/// 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,
);
}
}