Flutter setting variable of CupertinoPicker after showModalBottomSheet - flutter

I'm referencing this answer:https://stackoverflow.com/a/57592970/8616895
Trying to get selectedValue when the bottommodal is closed with WhenComplete.
The function in WhenComplete() is called, but when I print the selectedValue, it only responds as the default, 0. I have checked and SelectedValue does change as I rotate, but the correct value is not retrieved when I close the modal, I assume it has something to do with async or scope. Please share any suggestions.
My class is as follows:
class PopupButton extends StatefulWidget {
final String initialValue;
final List<dynamic> options;
final Function callback;
int initialIndex;
PopupButton(
{#required this.initialValue,
#required this.options,
this.callback,
this.initialIndex = 0});
#override
_PopupButtonState createState() => _PopupButtonState();
}
class _PopupButtonState extends State<PopupButton> {
int selectedValue;
String text = "";
void showPicker(
BuildContext context,
) {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
height: 200,
child: CupertinoPicker(
itemExtent: 32,
onSelectedItemChanged: (value) {
if (mounted) {
setState(() {
selectedValue = value;
});
}
},
children: (widget.options.map((option) => Text(
option,
style: TextStyles.headerLight,
))).toList()),
);
}).whenComplete(() {
// THIS ALWAYS PRINTS 0
print("Selected: $selectedValue");
if (widget.callback != null) {
widget.callback(selectedValue);
}
TextEditingController();
return;
});
}
#override
Widget build(BuildContext context) {
selectedValue = widget.initialIndex;
text = widget.initialValue;
return GestureDetector(
child: Text(text),
onTap: () {
showPicker(context);
},
);
}
}

your mistake is initialization selectedValue in build method.
Every time you call
setState(() {
selectedValue = value;
});
the selectedValue initiated by widget.initialIndex.
Please move selectedValue = widget.initialIndex; to initState method.
#override
void initState() {
super.initState();
selectedValue = widget.initialIndex;
text = widget.initialValue;
}
#override
Widget build(BuildContext context) {
return GestureDetector(
child: Text(text),
onTap: () {
showPicker(context);
},
);
}

Related

how to Enable button if "All textfield" is not empty flutter

my app has two textformfield. I want to enable button if all textfield are not empty.
In the internet, there are a way to enable button if "one" textfield are not empty.
I try to make isFilledTitle true if title of textfield is not empty. and isFilledContent true if content of textfield is not empty. and then if they are all true, isButtonActive is true. but it doesn't work.
late TextEditingController _titleEditingController;
late TextEditingController _contentEditingController;
bool isButtonActive = true;
bool isFilledContent = false;
bool isFilledTitle = false;
#override
void initState() {
super.initState();
_titleEditingController = TextEditingController();
_contentEditingController = TextEditingController();
_titleEditingController.addListener(() {
final isFilledTitle = _titleEditingController.text.isNotEmpty;
setState(() {
this.isFilledTitle = isFilledTitle;
});
});
_contentEditingController.addListener(() {
final isFilledContent = _contentEditingController.text.isNotEmpty;
setState(() {
this.isFilledContent = isFilledContent;
});
});
if(isFilledContent && isFilledTitle){
setState(() {
isButtonActive = true;
});
} else {
setState(() {
isButtonActive = false;
});
}
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child: Container(
child: SafeArea(
child: Scaffold(
appBar: AppBarWriteContainer(
onButtonPressed: isButtonActive
? (widget.postNo != null)
? () => revisePost()
: () => newPost()
: null,
),
Yeah your answer is correct but the user needs validation on two text field so i will modify the answer like
class _TempDialogState extends State<TempDialog> {
final TextEditingController _inputController = TextEditingController();
final TextEditingController _inputController2 = TextEditingController();
bool enable = false;
#override
void initState() {
super.initState();
}
#override
void dispose() {
_inputController.dispose();
_inputController2.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Center(
child: Column(children: [
TextField(
controller: _inputController,
onChanged: (data) {
if (_inputController.text.isEmpty ||
_inputController2.text.isEmpty) {
enable = false;
} else {
enable = true;
}
setState(() {});
},
),
TextField(
controller: _inputController2,
onChanged: (data) {
if (_inputController.text.isEmpty ||
_inputController2.text.isEmpty) {
enable = false;
} else {
enable = true;
}
setState(() {});
},
),
ElevatedButton(
onPressed: enable ? () {} : null,
child: Text('${enable}'),
)
])),
),
);
}
}
You don't need any additional libraries to do that. Flutter has it out-of-the-box and you can make sure you're not going to rebuild the whole tree on each keystroke.
TextEditingController extends ValueNotifier<TextEditingValue> which means you can utilize ValueListenableBuilder from material package to listen to text changes.
class MyWidget extends StatelessWidget {
final TextEditingController _inputController = TextEditingController();
#override
void dispose() {
_inputController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Column(children: [
TextField(controller: _inputController),
ValueListenableBuilder<TextEditingValue>(
valueListenable: _inputController,
builder: (context, value, child) {
return ElevatedButton(
onPressed: value.text.isNotEmpty ? () {} : null,
child: Text('I am disabled when text is empty'),
);
},
),
]);
}
}
You only set the isButtonActive in the initState. Try updating it in the controller listeners. Maybe something like:
#override
void initState() {
super.initState();
_titleEditingController = TextEditingController();
_contentEditingController = TextEditingController();
_titleEditingController.addListener(() {
final isFilledTitle = _titleEditingController.text.isNotEmpty;
setState(() {
this.isFilledTitle = isFilledTitle;
updateIsButtonActive();
});
});
_contentEditingController.addListener(() {
final isFilledContent = _contentEditingController.text.isNotEmpty;
setState(() {
this.isFilledContent = isFilledContent;
updateIsButtonActive();
});
});
}
void updateIsButtonActive() {
if(isFilledContent && isFilledTitle){
isButtonActive = true;
} else {
isButtonActive = false;
}
}

Try checking that the value isn't 'null' before using it as a condition

How can solve this problem of null safety
class _SelectEmpTaskState extends State<SelectEmpTask> {
bool? _value = false;
#override
Widget build(BuildContext context) {
return Container(
child: InkWell(
onTap: () {
setState(() {
print("object");
_value = !_value // Not working
});
},
),
);
}
}
You are using flutter version with sound null safety or you have enabled null safety
The first problem is no semicolon in the code below
_value = !_value // Not working
Second change use:
late bool _value = false
instead of bool? _value = false;
complete source code below:
class _SelectEmpTaskState extends State {
late bool _value = false;
#override
Widget build(BuildContext context) {
return Container(
child: InkWell(
onTap: () {
setState(() {
print("object");
_value = !_value; // Not working
});
},
),
);
}
}

Flutter show datepicker with keypress

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,
);
}
}
}
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,
);
}
}

How to call SetState from another class

Hopefully someone can help me on this. I have this scenario where my textfield has been set an error text since user click continue without choosing value. Supposedly, when i tap on the textfield, a dialog will show with a list of data that i should choose. When done choosing, the value is set to the textfield and the error text is reset to null. But currently the error text is not reset to null and it's still shown along the value that i chose. I'm thinking maybe it's because i need to rebuild MySearchList class by calling setstate. But i am not sure how to do this. Hope anyone can advise me on what is the best way to do this. Thanks in advance.
import 'package:flutter/material.dart';
// ignore: must_be_immutable
class MySearchList extends StatefulWidget {
MySearchList(
{required this.label,
required this.controller,
required this.dataList,
this.errorText});
final String label;
final TextEditingController controller;
final List<String> dataList;
String? errorText;
#override
_MySearchListState createState() => _MySearchListState();
}
class _MySearchListState extends State<MySearchList> {
#override
Widget build(BuildContext context) {
return Material(
child: TextField(
decoration: InputDecoration(
labelText: widget.label,
errorText: widget.errorText,
),
readOnly: true,
controller: widget.controller,
onTap: () {
showDialog(
context: context,
builder: (BuildContext context) {
return MySearchListDialog(
label: widget.label,
controller: widget.controller,
dataList: widget.dataList,
);
});
}),
);
}
}
// ignore: must_be_immutable
class MySearchListDialog extends StatefulWidget {
MySearchListDialog(
{required this.label,
required this.controller,
required this.dataList,
this.errorText});
final String label;
final TextEditingController controller;
final List<String> dataList;
String? errorText;
#override
_MySearchListDialogState createState() => _MySearchListDialogState();
}
class _MySearchListDialogState extends State<MySearchListDialog> {
TextEditingController _textController = TextEditingController();
List<String> _mainDataList = [];
List<String> _newDataList = [];
#override
void initState() {
super.initState();
_mainDataList = widget.dataList;
_newDataList = List.from(_mainDataList);
}
#override
void dispose() {
_textController.dispose();
super.dispose();
}
onItemChanged(String value) {
setState(() {
_newDataList = _mainDataList
.where((string) => string.toLowerCase().contains(value.toLowerCase()))
.toList();
});
}
#override
Widget build(BuildContext context) {
return Dialog(
child: Container(
constraints: BoxConstraints(
minHeight: 300.0,
maxHeight: 400.0,
),
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.fromLTRB(12.0, 10.0, 12.0, 0.0),
child: MyTextField(
hintLabel: "Search",
controller: _textController,
onChanged: onItemChanged,
errorText: widget.errorText,
),
),
Expanded(
child: ListView(
padding: EdgeInsets.all(12.0),
children: _newDataList.map((data) {
return ListTile(
title: Text(data),
onTap: () {
//this setstate will not rebuild class MySearchList
setState(() {
widget.errorText = null; //reset error on textfield
widget.controller.text = data.toString(); //set value to textfield
Navigator.pop(context); //close dialog
});
});
}).toList(),
),
)
],
),
));
}
}
Show your dialog like this:
_MySearchListState
onTap: () async {
var data = await showDialog(
context: context,
builder: (BuildContext context) {
return MySearchListDialog(
label: widget.label,
controller: widget.controller,
dataList: widget.dataList,
);
});
if(null != data){
setState(() {
});
}
})
And close your dialog like this:
_MySearchListDialogState
onTap: () {
//this setstate will not rebuild class MySearchList
setState(() {
widget.errorText = null; //reset error on textfield
widget.controller.text = data.toString(); //set value to textfield
});
Navigator.of(context).pop(data); // <-- Pass data and close dialog
});

Why can't I read data from the shared preferences import?

I have a ListView.builder that builds a certain amount of widgets depending on user input. Each widget has their own specific name and has a DropDownMenu. I save this value with the corresponding name of the widget. It saves it correctly. However, when I try and read the data and create a new list from it, this error appears: [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The method '[]' was called on null.
'course' is a list. I am using the shared preferences import. When you tap the flat button, it should build the new list, but it is not. Could you explain to me why this is not working please?
This is code in the main app.
void main() {
runApp(Hemis());
}
class Hemis extends StatefulWidget {
#override
_HemisState createState() => _HemisState();
}
class _HemisState extends State<Hemis> {
_read() async {
final prefs = await SharedPreferences.getInstance();
for(int i = 0; i < course.length; i++) {
listMarks[i].name = course[i].name;
listMarks[i].mark = prefs.getInt(course[i].name) ?? 0;
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
ListView.builder(
itemCount: course.length,
itemBuilder: (context, index) {
return ModuleListItem(
name: '${course[index].name}',
credits: course[index].credits,
);
},
),
FlatButton(
onPressed: () {
_read();
for(int i = 0; i < course.length; i++) {
print('${listMarks[i].name}: ${listMarks[i].mark}');
}
},
),
],
),
)
)
);
}
}
The widget that is being built.
final percentage = List<String>.generate(100, (i) => "$i");
class ModuleListItem extends StatefulWidget {
const ModuleListItem ({ Key key, this.name, this.credits }): super(key: key);
final String name;
final int credits;
#override
_ModuleListItemState createState() => _ModuleListItemState();
}
class _ModuleListItemState extends State<ModuleListItem> {
String dropdownValue;
bool isSwitched = false;
_save() async {
final prefs = await SharedPreferences.getInstance();
final key = '${widget.name}';
final value = int.parse(dropdownValue);
prefs.setInt(key, value);
print('saved $value');
}
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
DropdownButton<String>(
value: dropdownValue,
icon: Icon(Icons.keyboard_arrow_down),
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
items: percentage.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
Switch(
value: isSwitched,
onChanged: (value) {
setState(() {
isSwitched = value;
if(isSwitched == true) {
_save();
}
print(isSwitched);
});
},
),
],
),
);
}
}