Watching a future returning function leads to type case error
Closed this issue · 5 comments
Hi there,
as the title describes an error is thrown if you try to watch a future based on a function that is returning a future.
Error:
type '() => ItemTemplateViewModel?' is not a subtype of type 'ItemTemplateViewModel?' in type cast
Stacktrace
I/flutter ( 7540): #0 _WatchItState.registerFutureHandler (package:watch_it/src/watch_it_state.dart:398:42)
I/flutter ( 7540): #1 watchFuture (package:watch_it/src/watch_it.dart:268:31)
...
Line throwing the error:
initialValueProvider?.call as R?;
watchFuture call:
final itemTemplate = watchFuture((LibraryService p0) => p0.getItemTemplateById(templateId ?? -1), initialValue: null);
getItemTemplateById signature:
Future<ItemTemplateViewModel?> getItemTemplateById(int templateId)
I suspect this might be a bug in registerFutureHandler. The following is the code block including the line throwing the error and the code before it
/// select returned a different value than the last time
/// so we have to unregister out handler and subscribe anew
watch.dispose();
initialValue = preserveState && watch.lastValue!.hasData
? watch.lastValue!.data ?? initialValueProvider?.call()
: initialValueProvider?.call as R?;
initialValueProvider?.call as R?;
is trying to cast the function to R instead of calling it and then casting. Given that at all other times (in this file) the .call
is actually being called, this is most likely a bug. Changing it to : initialValueProvider?.call() as R?;
works without throwing an error.
Thanks for reporting, I will look into it
Hi,
I added a new branch and PR with a fix #30
Could you please verify that it's works (you can directly reference the branch from your pubspec)
It seems that your selector function (LibraryService p0) => p0.getItemTemplateById(templateId ?? -1)
returns different futures on different builds which is probably pretty rarely used. watch_it was built to deal with this, but you just found the bug there.
Hi,
thanks for the quick fix. I tested it out and it works.
One thing I noticed is that this only happened when templateId
was null
. getItemTemplateById
ultimately calls the following implementation interacting with the local Drift database
Future<ItemTemplateViewModel?> getItemTemplateWithId(int id) async {
final row = await _joinValues(select(itemTemplates)..where((li) => li.id.equals(id))).getSingleOrNull();
return _rowToViewModel(row);
}
It might be, that it only really happend when the returned value was null. But in general I wouldn't implement it this way as it really will create a new Future on every call that is awaited and then leads to a rebuild which creates a new future.
I prefer to have such functions updating a ´ValueNotifier´ when a new value is available and then watch that one.
I highly recommend checking out my flutter_command package as it is a great addition to watch_it especially to handle async function calls
Fixed in V1.4.2 now on pub