I have a clear button that should be visible if the date
and time
fields are filled and not visible if they are empty. The logic part works but the state does not change and if both the textfields
are filled the checkbox doesn't appear. I can't use the default suffix icon as both the textfields are wrapped within a gesture detector.
class TodoScreen extends StatefulWidget {
final int? todoIndex;
final int? arrayIndex;
const TodoScreen({Key? key, this.todoIndex, this.arrayIndex})
: super(key: key);
@override
State<TodoScreen> createState() => _TodoScreenState();
class _TodoScreenState extends State<TodoScreen> {
@override
Widget build(BuildContext context) {
String date = '';
String? time = '';
if (widget.todoIndex != null) {
date = arrayController
.arrays[widget.arrayIndex!].todos![widget.todoIndex!].date!;
time = arrayController
.arrays[widget.arrayIndex!].todos![widget.todoIndex!].time;
}
TextEditingController _dateController = TextEditingController(text: date);
TextEditingController _timeController = TextEditingController(text: time);
late String _setTime, _setDate;
late String _hour, _minute, _time;
late String dateTime;
DateTime selectedDate = DateTime.now();
TimeOfDay selectedTime = TimeOfDay(
hour: (TimeOfDay.now().minute > 55)
? TimeOfDay.now().hour 1
: TimeOfDay.now().hour,
minute: (TimeOfDay.now().minute > 55) ? 0 : TimeOfDay.now().minute 5);
Future<DateTime?> _selectDate() => showDatePicker(
builder: (context, child) {
return datePickerTheme(child);
},
initialEntryMode: DatePickerEntryMode.calendarOnly,
context: context,
initialDate: selectedDate,
initialDatePickerMode: DatePickerMode.day,
firstDate: DateTime.now(),
lastDate: DateTime(DateTime.now().year 5));
Future<TimeOfDay?> _selectTime() => showTimePicker(
builder: (context, child) {
return timePickerTheme(child);
},
context: context,
initialTime: selectedTime,
initialEntryMode: TimePickerEntryMode.input);
Future _pickDateTime() async {
DateTime? date = await _selectDate();
if (date == null) return;
if (date != null) {
selectedDate = date;
_dateController.text = DateFormat("MM/dd/yyyy").format(selectedDate);
}
TimeOfDay? time = await _selectTime();
if (time == null) {
_timeController.text = formatDate(
DateTime(
DateTime.now().year,
DateTime.now().day,
DateTime.now().month,
DateTime.now().hour,
DateTime.now().minute 5),
[hh, ':', nn, " ", am]).toString();
}
if (time != null) {
selectedTime = time;
_hour = selectedTime.hour.toString();
_minute = selectedTime.minute.toString();
_time = _hour ' : ' _minute;
_timeController.text = _time;
_timeController.text = formatDate(
DateTime(2019, 08, 1, selectedTime.hour, selectedTime.minute),
[hh, ':', nn, " ", am]).toString();
}
}
bool visible =
(_dateController.text.isEmpty && _timeController.text.isEmpty)
? false
: true;
return Scaffold(
resizeToAvoidBottomInset: false,
body: SafeArea(
child: Container(
width: double.infinity,
child: GestureDetector(
onTap: () async {
await _pickDateTime();
visible = true;
},
child: Container(
margin: const EdgeInsets.only(top: 20.0),
width: double.infinity,
padding: const EdgeInsets.symmetric(
horizontal: 24.0, vertical: 15.0),
decoration: BoxDecoration(
color: tertiaryColor,
borderRadius: BorderRadius.circular(14.0)),
child: Column(
children: [
Row(
children: [
Flexible(
child: TextField(
enabled: false,
controller: _dateController,
onChanged: (String val) {
_setDate = val;
},
decoration: InputDecoration(
hintText: "Date",
hintStyle: hintTextStyle,
border: InputBorder.none),
style: todoScreenStyle,
),
),
visible
? IconButton(
onPressed: () {
_dateController.clear();
_timeController.clear();
visible = false;
},
icon: const Icon(
Icons.close,
color: Colors.white,
))
: Container()
],
),
primaryDivider,
TextField(
onChanged: (String val) {
_setTime = val;
},
enabled: false,
controller: _timeController,
decoration: InputDecoration(
hintText: "Time",
hintStyle: hintTextStyle,
border: InputBorder.none),
style: todoScreenStyle,
)
],
)),
),
),
),
);
}
}}
If the code doesn't fully make sense it's because I removed some of the irrelevant code that do not add any real value to the question
CodePudding user response:
First, move all your TextEditController
outside of build method, it should be initialized in the initState
method, like this :
late TextEditingController _dateController;
late TextEditingController _timeController;
late TextEditingController titleEditingController;
late TextEditingController detailEditingController;
@override
void initState() {
super.initState();
String title = '';
String detail = '';
String date = '';
String? time = '';
if (widget.todoIndex != null) {
title = arrayController.arrays[widget.arrayIndex!].todos![widget.todoIndex!].title ?? '';
detail = arrayController.arrays[widget.arrayIndex!].todos![widget.todoIndex!].details ?? '';
date = arrayController.arrays[widget.arrayIndex!].todos![widget.todoIndex!].date!;
time = arrayController.arrays[widget.arrayIndex!].todos![widget.todoIndex!].time;
}
_dateController = TextEditingController(text: date);
_timeController = TextEditingController(text: time);
titleEditingController = TextEditingController(text: title);
detailEditingController = TextEditingController(text: detail);
}
Then, change your onPressed function:
onPressed: () {
_dateController.clear();
_timeController.clear();
setState(() {});
},
For your question about CheckBox
, you don't need a StreamBuilder
for that, just put your CheckBox
like this:
Checkbox(
shape: const CircleBorder(),
checkColor: Colors.white,
activeColor: primaryColor,
value: done,
side: Theme.of(context)
.checkboxTheme
.side,
onChanged: (value) {
setState(() {
done = value;
});
})
And of cource, try to move your done variable outside build method too:
late bool done;
@override
void initState() {
super.initState();
done =
(widget.todoIndex == null) ? false : arrayController.arrays[widget.arrayIndex!].todos![widget.todoIndex!].done!;
}