Calling a Future<Null> function from InkWell's onTap - flutter

I've created an InkWell with an onTap function call as such
new InkWell(onTap: _handleSelectedDate,
...
)
Handle _handleSelectedDate is defined as a Future as the following
Future<Null> _handleSelectedDate(BuildContext context, AppModel model) async {
var endYear = new DateTime.now().year + 10;
var startYear = new DateTime.now().year - 100;
DateTime picked = await showDatePicker(
context: context,
initialDate: model.trackDate,
firstDate: new DateTime(startYear, 1),
lastDate: new DateTime(endYear));
if (picked != null) {
if (picked != model.trackDate) {
model.setTrackDate(picked);
}
}
}
If I call _handleSelectedDate with no parameters the call is valid, but I need to be able to pass a BuildContext and my Pages Model data through to it. As soon as I do this I get the following build error.
error: The argument type '(BuildContext, AppModel) → Future<Null>'
can't be assigned to the parameter type '() → void'.
I'm not sure how to resolve this. I just need a way to pass data through to the call so that I can reference my Model data (I'm experimenting with scoped_model)

You can wrap your call in a closure
new InkWell(
onTap: () => _handleSelectedDate(context, myModel),
)

The correct way of calling a Future is
new InkWell(
onTap: () async => await _handleSelectedDate(context, myModel),
)

Related

How to Navigator.push() from a DropdownMenuItem's onTap and await the returned value?

Following on from Unhandled Exception: type '_DropdownRouteResult<int>' is not a subtype of type 'int?' when returning value from Navigator.pop() as I still haven't resolved the issue.
I have a DropdownFormField which I am dynamically populating from a db via a Provider. I would like to add a DropdownMenuItem which, when selected, pushes a new route (for inserting a new record into the db).
The route returns the id of the newly-inserted db record when popped, and I would like to set the new value as the value of the DropdownFormField.
Implementing the new item with a TextButton child and pushing in the buttons' onPressed results in expected push/pop behaviour, but is styled inconsistently from the "normal" items, and does not close the dropdown (which makes sense as the button is pressed, but the DropdownMenuItem is not tapped). Tapping outside the dropdown after popping reveals that the dropdown's value is updated correctly.
DropdownMenuItem<int>(child: TextButton(
onPressed: () async {
final int newValue = await Navigator.push(context, AddNewTeaProducerRoute());
setState(() {
_selectedValue = newValue;
});
},
child: Text('Add New Manufacturer')));
Implementing the new item with a Text child and pushing in the DropdownMenuItem's onTap (which seems like the correct approach) results in an immediate attempt to return the value, disrespecting the asynchronous nature of the onTap and resulting in the exception from my previous question. Breakpoint debugging without specifying the type of newValue shows that it is immediately assigned the Future/_DropdownRouteResult<int>, rather than awaiting its returned int.
DropdownMenuItem<int>(
onTap: () async {
final int newValue = await Navigator.push(context, AddNewTeaProducerRoute());
setState(() {
_selectedValue = newValue;
});
},
child: const Text('Add New Manufacturer'));
I have no idea why await is being respected in TextButton.onPressed but not in DropdownMenuItem.onTap
I don't know if it's the right way, since it relies on null as a placeholder value and I can't see how you'd easily scale it beyond a single DropdownMenuItem with special behaviour (as unlikely as it seems that you'd want to) but after reading this for the third time I finally grokked a solution - return null as the value, and perform navigation/assignment in the DropdownButtonFormField's onChanged
final brokenAddNewTeaProducerButton = DropdownMenuItem<int>(
value: null,
child: const Text('Add New Manufacturer'));
return DropdownButtonFormField<int?>(
value: _selectedValue,
items: [brokenAddNewTeaProducerButton] + teaProducerListItems,
onChanged: (value) async {
if (value == null) {
final newTeaProducerId = await Navigator.push(context, AddNewTeaProducerRoute());
setState(() {
_selectedValue = newTeaProducerId;
});
} else {
setState(() {
_selectedValue = value;
});
}
},
hint: Text('Select a manufacturer'),
);
}
**You can try this statfulBuilder**
StatefulBuilder(builder: (context,state){
return DropdownMenuItem<int>(child: TextButton(
onPressed: () async {
var newValue = await Navigator.push(context,
AddNewTeaProducerRoute());
state(() {
_selectedValue = newValue;
});
},
child: Text('Add New Manufacturer')));
}),

Double Refreshing the page on flutter

Having a little hard time doing what I want to achieve.
I am using googlesheets as database, with autosort function to sort the products.
In flutter, after editing 3 products, the flutter app stops "refreshing" so It stops updating inside the app, but in the googlesheets it's always updating.
This is what I currently have to edit the date.
Future<void> _updateDate(
BuildContext context) async {
final DateTime? nD =
await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(1901, 1),
lastDate: DateTime(2100));
if (nD != oD) {
await widget.provider
.pushNewDate(_nDF, index);
setState(() {});
//this is what I added after changing some code
Future.delayed(const Duration(seconds: 1),
(() => (setState(() {}))));
///
}
}
If you wrap the _updateDate in a set state it will propably work . If it doesnt try _updateDate.then(SetState(){});
after a long time I figured it out it was not on my end bue on google sheets that took a litle time to update. I went for another route!

How can I set a Future Builder Function on a different dart file?

I have a Future (async) Date&Time picker function which works fine from within the body of my stateful widget which contains the "Builder" and the function can be called via the onpressed by just this:
onPressed: () {SelectDayAndTimeL();},
Code:
Future _selectDayAndTimeL(BuildContext context) async {
DateTime _selectedDay = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2021),
lastDate: DateTime(2030),
builder: (BuildContext context, Widget child) => child);
TimeOfDay _selectedTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);
if (_selectedDay != null && _selectedTime != null) {
//a little check
}
setState(() {
selectedDateAndTime = DateTime(
_selectedDay.year,
_selectedDay.month,
_selectedDay.day,
_selectedTime.hour,
_selectedTime.minute,
);
// _selectedDate = _selectedDay;
});
// print('...');
}
Now I want to be able to call this function from different dart files/screens which means I have to keep this function on a different dart file which I have tried to do, but because of the setState in the function it needs to be inside a stateful widget. I have tried putting it inside a stateful widget but keeps getting errors.
class Picker extends StatefulWidget {
#override
_PickerState createState() => _PickerState();
}
class _PickerState extends State<Picker> {
#override
Future<Widget> build(BuildContext context) async { //the error in on the build on this line
DateTime _selectedDay = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2021),
lastDate: DateTime(2030),
builder: (BuildContext context, Widget child) => child);
TimeOfDay _selectedTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);
if (_selectedDay != null && _selectedTime != null) {
//a little check
}
setState(() {
selectedDateAndTime = DateTime(
_selectedDay.year,
_selectedDay.month,
_selectedDay.day,
_selectedTime.hour,
_selectedTime.minute,
);
// _selectedDate = _selectedDay;
});
}
}
How do I properly place the Future Function inside a stateful widget and how to call it on an onpressed?
I don't know if the title I gave this question is actually what it's supposed to be, but I don't know how else to put it.
The whole setState inside your method is the problem. Your method should do one thing: get a date and time from the user. And there it's responsibility ends.
Future<DateTime> SelectDayAndTimeL(BuildContext context) async {
DateTime _selectedDay = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2021),
lastDate: DateTime(2030),
builder: (BuildContext context, Widget child) => child);
TimeOfDay _selectedTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);
if (_selectedDay != null && _selectedTime != null) {
//a little check
}
return DateTime(
_selectedDay.year,
_selectedDay.month,
_selectedDay.day,
_selectedTime.hour,
_selectedTime.minute,
);
}
Now your onPressed becomes:
onPressed: () async {
final pickedDatetime = await SelectDayAndTimeL(context);
setState(() { selectedDateAndTime = pickedDatetime });
},
You have sucessfully divided your code into the function that picks a thing and your widget, which updates after the thing is picked.
The function that picks the date and time can now be reused in every other widget.
Create a function which will take datetime as a parameter and setstate of the stateful widget. Write this inside the stateful widget. Pass this function to the other class as an argument. Once the date is picked call this method by passing the datetime selected.
You should pass the setState function itself as a parameter to the method. This way, inside the method you will always be using the correct state setter function. That is especially necessary since you need to keep the variables _selectedDay etc inside the widget, not on the static method. Try this:
Future selectDayAndTimeL(BuildContext context, Function(DateTime) dateTimeSetter) async { //add a function to receive and use the
DateTime _selectedDay = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2021),
lastDate: DateTime(2030),
builder: (BuildContext context, Widget child) => child);
TimeOfDay _selectedTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
);
if (_selectedDay != null && _selectedTime != null) {
//a little check
}
// Create the variable with the picked date
DateTime selectedDateAndTime = DateTime(
_selectedDay.year,
_selectedDay.month,
_selectedDay.day,
_selectedTime.hour,
_selectedTime.minute,
);
//call the function from the parameter, which will be executed on the calling widget
dateTimeSetter(selectedDateAndTime);
....
}
Then, call your function to show the datetime picker passing the context, and a function that receives a DateTime parameter, which will be the parameter picked by the user. When the user picks a date, this function body will be executed, calling setState and setting the pickedTime variable.
class Picker extends StatefulWidget {
DateTime pickedTime;
#override
_PickerState createState() => _PickerState();
}
class _PickerState extends State<Picker> {
#override
Future<Widget> build(BuildContext context) async {
return ...
_selectDayAndTimeL(context, (DateTime time){
setState((){
widget.pickedTime = time;
});
});
...
You can also extract the function from the parameter into a normal named function, and just use its name in the parameter, but I'll leave it as is for now to make it simpler.
Also, don't forget to make your method public and static if necessary for the scope of your code.

Flutter exception: 'Context is not a subtype of BuildContext' error using Navigator

I am working on a flutter app and I am running into the following error: "The argument type 'Context' can't be assigned to the parameter type 'BuildContext'." However, when I try to pass in context to my functions as it says to do on the internet, I am unable to get it to work. Can someone please help me with this? The app is a camera app, and I know the video is successfully being recorded. here is the code:
This is the stop button that is pressed.
IconButton(
icon: const Icon(Icons.stop),
color: Colors.red,
onPressed: () {controller != null &&
controller.value.isInitialized &&
controller.value.isRecordingVideo
? onStopButtonPressed. //this is the function that is being called
: null;},
) //icons.stop
This is where the app isn't working with my Navigator.push call. It works fine when I take it out.
void onStopButtonPressed() {
stopVideoRecording().then((_) {
if (mounted) setState(() {});
print('Video recorded to: $videoPath');
print('Navigator is hit');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PreviewImageScreen(videoPath: videoPath),
), //MaterialpageRoute
); //Navigator
});
}
And here is my stopVideoRecording function
Future<void> stopVideoRecording() async {
if (!controller.value.isRecordingVideo) {
return null;
}
try {
await controller.stopVideoRecording();
} on CameraException catch (e) {
_showCameraException(e);
return null;
}
//await _startVideoPlayer();
}
Thanks!
change it to
Navigator.push(
this.context, //add this so it uses the context of the class
MaterialPageRoute(
builder: (context) => PreviewImageScreen(videoPath: videoPath),
), //MaterialpageRoute
); //Navigator
Maybe you're importing a library or something that has a class named context and is interfering with the name context

Flutter error: Argument type 'Future<void>' can't be assigned to the parameter type '() → void' [duplicate]

This question already has answers here:
What does the empty parentheses after the onPressed property mean in Dart?
(5 answers)
Closed 3 years ago.
I'm trying to call the showDatePicker widget once an icon button is clicked, however when i call the onTap method get the following error:
The argument type 'Future' can't be assigned to the parameter type '() → void'.
Please find my code below:
showDatePicker code
DateTime selectedDate = DateTime.now();
Future<Null> _selectDate(BuildContext context) async {
final DateTime picked = await showDatePicker(
context: context,
initialDate: selectedDate,
firstDate: DateTime(2018, 1),
lastDate: DateTime(2101));
if (picked != null && picked != selectedDate)
setState(() {
selectedDate = picked;
});
}
IconButton code
IconButton(
icon: Icon(Icons.date_range),
onPressed: _selectDate(context)),
I'm using the StatefulWidget, please do let me know how to solve this issue. Thanks :)
You are calling _selectDate directly, instead of passing a function that calls _selectDate.
Change your onPressed to onPressed: (){_selectDate(context);}