I'm trying to make my datepicker reusable, as I need three datepickers, that change different values that are stored in the same class, lets call it _object.date1 , date 2, date 3
To reuse my datepicker I tried the following and passed the variable to the datepicker that shall be changed. But then the field value isn't changed or stored and nothing happens, also no error. If I don't pass the value to _showAndroidDatePicker() and use the line in setState that I commented out below, it works properly. The Datepicker ist linked to the onTap of a TextFormField in a Form.
Can anyone explain to me what I'm missing here? It would be really great to make this reusable.
Many thanks!
void _showAndroidDatePicker(value) {
showDatePicker(
context: context,
builder: (BuildContext context, Widget child) {
return Theme(
data: ThemeData.light().copyWith(
primaryColor: Theme.of(context).primaryColor,
accentColor: Theme.of(context).primaryColor,
colorScheme:
ColorScheme.light(primary: Theme.of(context).primaryColor),
buttonTheme: ButtonThemeData(textTheme: ButtonTextTheme.primary),
),
child: child,
);
},
initialDate: DateTime.now(),
locale: Locale('de'),
firstDate: DateTime(1900),
helpText: 'Bitte wähle ein Datum aus',
lastDate: DateTime.now(),
).then<DateTime>(
(DateTime newDate) {
if (newDate != null) {
setState(() {
value = newDate.toIso8601String();
//Below code works if value isn't passed to datepicker, but I want it variable to avoid boilerplate
// _object.date1 =newDate.toIso8601String();
});
}
return;
},
);
}
Many thanks for your help!
You could pass it the controller and the focus node for the text field. Something like this:
datePickerListener(node, controller) {
if (node.hasFocus) {
node.unfocus();
showDatePicker(your setup).then((date){
var formatter = DateFormat('dd/MM/yyyy');
var formatted = formatter.format(date).toString();
controller.text = formatted;
});
}
}
Then:
FocusNode yourNode = FocusNode();
#override
void initState(){
yourNode.addListener(){
datePickerListener(yourNode, yourController);
};
}
TextFormField(
focusNode: yourNode,
controller: yourController,
)
Something like that. Just adjust to your needs.
Related
I have a form text field for which on tap I'm showing the flutter's datepicker. Currently I'm showing the hint text with hard coded value.
But now I have decided to format the date based on the selected locale and hence I'm passing the local information to ShowDatePicker. How can I show the date format based on the selected locale as hint text in the text field by removing hard coded value?
Ex:
#override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: TextField(
label: label,
hintText: 'MM-dd-yyyy', //Get the format based on selected local to show as hint
controller: selectedDate,
onTap: () async {
final DateTime? pickedDate = await _getSelectedDate(context);
if (pickedDate != null) {
selectedDate?.text = DateFormat('MM-dd-yyyy')
.format(pickedDate)
.toString();
}
},
),
),
],
);
}
Future<DateTime?> _getSelectedDate(BuildContext context) {
return showDatePicker(
context: context,
locale: context.currentLocale, // Selected locale being passed to showDatePicker
initialDate: DateTime.now(),
firstDate: firstDate,
lastDate: lastDate,
}
First of all get the package for it:
intl: ^0.17.0
import
import 'package:intl/intl.dart';
Define a variable like this:
final DateFormat formatter = DateFormat('MM-dd-yyyy');
hintText:"${formatter.format(DateTime.now())}"
This will show date as hint like this:
02-22-2022(mm-dd-yyyy)
I'm working on application where I need to persist the selected date range with shared preferences. For date picker I'm using an external package, for state handling I work with provider.
At the beginning in provider model I'm reading the value from shared preferences :
class DateRangeFilterModel extends ChangeNotifier {
PickerDateRange? _dateRange;
DateRangeFilterModel() {
loadDateRange();
}
PickerDateRange? get dateRange => _dateRange;
Future<void> loadDateRange() async {
_dateRange = await SharedPreferencesManager.getFilterDateRange();
notifyListeners();
}
Future<PickerDateRange?> getDateRange() async {
return await SharedPreferencesManager.getFilterDateRange();
}
Future<void> setDateRange(PickerDateRange? dateRange) async {
_dateRange = dateRange;
await SharedPreferencesManager.saveFilterDateRange(
dateRange?.startDate, dateRange?.endDate);
notifyListeners();
}
}
With Consumer widget I try to set up the initial value, for an example I also show the result on Text widget. On Text widget saved date range appears but on SfDateRangePicker not.
Consumer<DateRangeFilterModel>(
builder: (context, model, child) => Column(
children: [
Text(model.dateRange.toString()),
SfDateRangePicker(
initialSelectedRange: model.dateRange,
showTodayButton: false,
enablePastDates: false,
selectionMode: DateRangePickerSelectionMode.range,
minDate: DateTime.now(),
onSelectionChanged: (DateRangePickerSelectionChangedArgs args) {
if (args.value is PickerDateRange) {
final DateTime? startDate = args.value.startDate;
final DateTime? endDate = args.value.endDate;
if (startDate != null && endDate != null) {
context
.read<DateRangeFilterModel>()
.setDateRange(PickerDateRange(startDate, endDate));
}
}
},
),
],
),
),
My ChangeNotifierProvider
ChangeNotifierProvider(
create: (context) => DateRangeFilterModel(),
child: const DateRangePicker()
),
I think there is some concurrency problems. Where I made mistake?
Thanks for any advice.
My solution is was to set initial range through controller.
final DateRangePickerController _controller = DateRangePickerController();
.....
SfDateRangePicker(
initialSelectedRange: _controller.selectedRange = model.dateRange,
controller: _controller,
...
)
I am trying to use the DatePicker in flutter and I want to set the initialDate to DateTime.now() and the lastDate I want to set to DateTime.now() + 20yrs so it increments the year dynamically.
Current I have my lastDate set to the year (2100), but this would cause a problem when the year 2101 reaches. So how can I modify my function to increase the lastDate dynamically?
Here is my function:
DateTime _date = DateTime.now();
Future < Null > _checkInDate(BuildContext contex) async {
DateTime ? _datePicker = await showDatePicker(
context: context,
builder: (BuildContext context, Widget ? child) {
return Theme(
data: ThemeData(
primaryColor: Color(0xFFEF5350),
colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.red)
.copyWith(secondary: Color(0xFFFFF176)),
),
child: child ? ? Text(""),
);
},
initialDate: _date,
firstDate: DateTime.now(),
lastDate: DateTime(2100), //how can i increase the lastDate dynamically?
);
if (_datePicker != null && _datePicker != _date) {
_checkInController.text = DateFormat('dd-MM-yyyy').format(_datePicker);
}
}
Yes you can, use DateTime.now().year to get the current year and then add as many years as you want. Like this:
final date = DateTime(DateTime.now().year + 20);
I need to get a time from a timepicker and show it on a text widget.
Here you have the widget that should show the time:
pickedTime = TimeOfDay.now();
ListTile(
title: Text(
" ${pickedTime.hour}:${pickedTime.minute}"),
trailing: Icon(Icons.timer,size: 45,),
onTap: _pickTime,
),
And here you have the function _pickTime:
_pickTime() async{
TimeOfDay time = await showTimePicker(
context: context,
initialTime: pickedTime);
setState(() {
pickedTime = time;
});
}
I have detected an issue when the picked time hour or minute is smaller than 10, the output is as shown in the picture for 04:05:
I would like to show the picked time always in format HH:mm.
The following is an extension on the int class that you can use to enforce two characters for each part of the time:
extension TwoChar on int {
String toTwoChars() {
return this.toString().padLeft(2, '0');
}
}
Then modify your code to use the extension:
title: Text("${pickedTime.hour.toTwoChars()}:${pickedTime.minute.toTwoChars()}"),
I added a speech recognition to a text field, it works but I cannot manage to add the text to the textfield, is there a way to do that.
the textfield looks like this:
Widget _buildDescriptionTextField(productBloc) {
return StreamBuilder<Object>(
stream: productBloc.messageStream,
builder: (context, snapshot) {
return TextField(
maxLines: 3,
controller: _controllerMessage,
onChanged: productBloc.messageSink,
decoration: InputDecoration(
labelText: allTranslations.text(StringConstant.description),
errorText: snapshot.error,
suffixIcon: IconButton(icon: Icon(Icons.mic), onPressed: () {
if (_isAvailable && !_isListening)
_speechRecognition
.listen(locale: "en_US")
.then((result) => print('$result'));
},
),
),
);
}
);
}
I have a steam-builder to manage the added text manually, and an controller if this page is used for editing, then as suffixsIcon the iconButton to start the speech recognition. when I add the result text outside a text Widget it works but I need it inside the texField.
Just doing that should work no ?
setState(() => _controllerMessage.text = result)
You need to use TextEditingController properties. I assume you declared one as _controllerMessage.
To set new value to your TextField and keep the cursor in the end - use something similar to the example from the Docs.
e.g.
_speechRecognition
.listen(locale: "en_US")
.then(_onResult);
// ...
void _onResult(String result) {
setState(() {
_controllerMessage.value = _controllerMessage.value.copyWith(
text: result,
selection: TextSelection(baseOffset: result.length, extentOffset: result.length),
composing: TextRange.empty,
);
});
}
Let me know if this helped.
So What I did is just used the _speechRecognition.setRecognitionResultHandler from the documentation, to set a new value to the controller of the textField, like so:
_speechRecognition.setRecognitionResultHandler(
(String speech) => setState(() {
_controllerMessage = new TextEditingController(text: resultText = speech);
})
);
the textField stays like it was before, see question.