I am sure there is a way to return a dynamic or generic instance of a class from a riverpod FutureProvider?
I can fetch different tracker data from an API. It can be for Physical Activity or Weight data for instance. So far, I tried this but does not work, not even compile:
final trackerDataProvider = FutureProvider.family<AbstractTrackerData, TrackerType>((ref, trackerType) async {
final repository = ref.read(trackerRepositoryProvider);
AbstractTrackerData trackerData =
await repository.getTrackerData(trackerType);
return trackerData;
});
and in my Widget
class PhysicalActivityLandingTrackerScreen extends HookWidget {
#override
Widget build(BuildContext context) {
final trackerData = useProvider(trackerDataProvider(TrackerType.physicalactivity));
...
}
}
and my tracker data classes
class SmokeFree extends AbstractTrackerData {
...
}
class PhysicalActivity extends AbstractTrackerData {
...
}
class Weight extends AbstractTrackerData {
...
}
and the enum
enum TrackerType {
physicalactivity,
exercise,
weeklyweight,
bloodpressure,
saltyfoodsubstitution,
goodfatsubstitution,
medicationuse,
smokefree
}
Yes, because Generic methods have a type parameter (the diamond operator enclosing the type) before the return type of the method declaration. ... Generic methods can have different type parameters separated by commas in the method signature. Method body for a generic method is just like a normal method.
Hence we can say that it is returned by provider.
Related
Having abstract class with methods that should be defined in multiple concrete class. For simplicity sake, here is an example:
abstract class MyAbstractClass extends StatelessWidget{
const MyAbstractClass ({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Text(abstractMethod().toString());
}
int _abstractMethod();
}
class MyConcreteClass1 extends MyAbstractClass {
#override
int _abstractMethod() {
return 42;
}
}
class MyConcreteClass2 extends MyAbstractClass {
#override
int _abstractMethod() {
return 66;
}
}
now when I try to call MyConcreteClass1 or MyConcreteClass2 it gives me the error below:
'MyConcreteClass1' has no instance method '_abstractMethod'
How to call MyConcreteClass1 or MyConcreteClass2 within my screen?
Note that it will work if I called and instance of it like below:
MyConcreteClass1 c = MyConcreteClass1();
int value = c._abstractMethod();
but it should have been called by build method in abstract class, right?
Well, here is an advice for whoever faces this issue:
DON'T use a leading underscore for identifiers that aren’t private.
as this link explains in dart leading underscore in an identifier to mark members and top-level declarations as private. for that it cannot be overridden within inherited classes.
I wanna know how can I create an instance of an Object from the passed generic Type T passed from the super class constructor
this example of what I want to achieve but it's wrong :
class Widget<T> extends StatelessWidget {
Widget({super.key});
final T instanceOfT = T(); // throws 'T' isn't a function.
#override
Widget build(BuildContext context) {
return Text(instanceOfT.exampleStringProperty);
}
}
class ExampleClass {
final String exampleStringProperty;
const ExampleClass({this.exampleStringProperty = "exampleValue"});
}
I'm expecting that I should pass the Type from the generic call when calling the Widget constructor like this:
Widget<ExampleClass>(),
Then an instance of the ExampleClass should be created and works fine.
Any ideas on this ?
When using Flutter and Riverpod, how do I update its values from my business logic?
I understand that I can get and set values from the UI side.
class XxxNotifier extends StateNotifier<String> {
XxxNotifier() : super("");
}
final xxxProvider = StateNotifierProvider<XxxNotifier, int>((ref) {
return XxxNotifier();
});
class MyApp extends HookConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
// getValue
final String value = ref.watch(xxxProvider);
// setValue
context.read(xxxProvider).state = "val";
return Container();
}
}
This method requires a context or ref.
How do I get or set these states from the business logic side?
Passing a context or ref from the UI side to the business logic side might do that, but I saw no point in separating the UI and business logic. Perhaps another method exists.
Perhaps I am mistaken about something. You can point it out to me.
You can pass ref in your XxxNotifier class:
class XxxNotifier extends StateNotifier<String> {
XxxNotifier(this._ref) : super("");
final Ref _ref;
void setNewState() {
state = 'to setting';
// use `_ref.read` to read state other provider
}
}
final xxxProvider = StateNotifierProvider<XxxNotifier, int>((ref) {
return XxxNotifier(ref);
});
// or using tear-off
final xxxProvider = StateNotifierProvider<XxxNotifier, int>(XxxNotifier.new);
You can create methods in your XxxNotifier class to modify the state of your provider.
For example, your notifier class can look like this.
class TodosNotifier extends StateNotifier <List<Todo>> {
TodosNotifier(): super([]);
void addTodo(Todo todo) {
state = [...state, todo];
}
}
You can then read the provider in a callback.
ref.read(xxxProvider.notifier).addTodo(todo);
This is my Notifier:
class Counter extends Notifier<int> {
final int initial;
Counter(this.initial);
#override
int build() => initial;
}
I need to pass initial value to it, but I'm unable to do that using the family modifier anymore.
// Error
final counterProvider = NotifierProvider.family<Counter, int, int>((initial) {
// How to get the initial value to pass here?
return Counter(initial);
});
The syntax for using family/autoDispose using Notifier/AsyncNotifier is different. You're supposed to change the inherited type
So instead of:
final provider = NotifierProvider(MyNotifier.new);
class MyNotifier extends Notifier<Value> {
With family you should do:
final provider = NotifierProvider.family(MyNotifier.new);
class MyNotifier extends FamilyNotifier<Value, Param> {
And the same reasoning applies with autoDispose.
The objective is to use the properties of a parent class to call a method and extend the content of that class.
The following code is designed to call a method based on the property of it's parent class. It returns a type error.
It is called like this:
MyToolbar(data: [
{
'MySecondClass': ['red','green','purple']
}
])
And this is the class:
class MyToolbar extends StatelessWidget {
MyToolbar({required this.data})
final List data;
ToolbarContent(type, data) {
if (type == 'MySecondClass') {
return MySecondClass(toggles: data);
}
#override
Widget build(BuildContext context) {
return Stack(children:[
for (List childData in data)
ToolbarContent('mySecondClass', childData),
])}
Firstly this returns the following type error.
_TypeError (type '_InternalLinkedHashMap<String, List<String>>' is not a subtype of type 'List<dynamic>')
Secondly, the list needs to find the key of the property data in order to set the correct function name for the function 'ToolbarContent'.
There's a few issues here. First, as mentioned by temp_, you need to set the List type for data, in this case would be List<Map<String,List<String>>
Second would be that for (List childData in data) needs to be actually for (Map<String,List<String>> childData in data)
The third is an assumption, but I think that there's a typo in your for loop where mySecondClass should be MySecondClass (or the other way)
The correct code would be as follows:
class MyToolbar extends StatelessWidget {
final List<Map<String, List<String>>> data;
MyToolbar({required this.data});
#override
Widget build(BuildContext context) {
var children = <Widget>[];
data.forEach((childData) {
childData.forEach((key, stringList) {
//I'm assuming Toolbar content takes the key of the map i.e. MySecondClass
//as the first param and the List for the key as the second param
children.add(ToolbarContent(key, stringList));
});
});
return Stack(
children: children,
);
}
}
Note: I'm also assuming that the ToolbarContent is another class, but do let me know if otherwise.
By default Dart sets any List to List<dynamic>. This is what the error is saying. You need to cast your List, try this instead final List<Map<String, List<String>> data;