Flutter dynamic type argument - flutter

I'm trying to pass a dynamic type argument to the Provider.of<T>(context)
My Code
class CustomInputField extends StatelessWidget {
final Type stateClass;
CustomInputField({
Key key,
this.stateClass,
}) : super(key: key);
#override
Widget build(BuildContext context) {
var state = Provider.of<stateClass>(context);
return TextFormField(
key: state.key,
...
);
}
}
But this gives me following error:
The name 'stateClass' isn't a type so it can't be used as a type argument.
Try correcting the name to an existing type, or defining a type named 'stateClass'.
Anyone knows how to correctly do this?
It could be that this is not a good practice, or maybe it's even impossible. But thing is, I would like to make a single CustomInputField widget for all the inputfields in my app. I'm using the ChangeNotifierProvider class from the Provider package and would like to pass different states to this CustomInputField widget

You can't pass a type to generic dynamically in Dart. The only way to do it is to have switch/if-else block and return objects with different type in generic.

Related

How do I preserve generic type information when accessing a Stateful Flutter Widget property from the Widget State? [duplicate]

This question already has an answer here:
Why am I getting TypeError at runtime with my generic StatefulWidget class?
(1 answer)
Closed last month.
I've got a simplified example below. In practice, I'm passing a list of data objects to a StatefulWidget. I want the Widget to copy the provided list into its state object which will then be filtered through future interactions. The filters use a type parameter so they know what fields they can work with in a callback, for example Filter<MyData>.
So, I'm trying to create a Widget that is aware of the MyData type so it can build the FilterChip Widgets using MyData fields. I tried to achieve this by adding a type parameter to both the StatefulWidget and its State class.
import 'package:flutter/material.dart';
/// Generic type information loss example
class MyWidget<T> extends StatefulWidget {
final List<T> things;
const MyWidget({Key? key, required this.things}) : super(key: key);
#override
_MyWidgetState createState() => _MyWidgetState<T>();
}
class _MyWidgetState<T> extends State<MyWidget> {
#override
Widget build(BuildContext context) {
List<T> things = widget.things; // Compiler error (IDE shows widget.things has a type of List<dynamic>)
return Container();
}
}
This code results in:
Error: A value of type 'List<dynamic>' can't be assigned to a variable of type 'List<T>'.
So what I don't understand is, why does things in the StatefulWidget class have a type of List<T>, but when referenced through the widget property of the class extendingState<MyWidget>, widget.things has a type of List<dynamic>.
And, as a result, any code in the state class that needs to be aware of the type now breaks. At runtime, the filter callbacks result in errors like:
type '(MyData) => bool' is not a subtype of type '(dynamic) => bool'
I think it is only class MyWidget extends StatefulWidget, without <T>.
Then you don‘t need to copy your list to preserve the list. Just use widget.things, this is considered best practice. The data remains during the rebuilds.

Flutter error encounter 'The argument type 'int' can't be assigned to the parameter type 'CardItem'.'

enter image description here
Encounter an error
(The argument type 'int' can't be assigned to the parameter type 'CardItem'.) at line 'itemBuilder: (context, index) => buildCard(item: index,),'
enter image description here
Firstly you extracted Widget to method which named buildCard. This approach is not recommended by Flutter. To learn more why:
https://dartcodemetrics.dev/docs/rules/flutter/avoid-returning-widgets
https://iiro.dev/splitting-widgets-to-methods-performance-antipattern/
The best way which Flutter recommends is extract Widgets to Stateful or Stateless widget classes. In the upper link.
For your code.
Extract your returned Widget to Stateless or Stateful widget
Give CartItem to the constructor of your created CardWidget to use CardWidget inside it data.
Then your item builder must be like this: itemBuilder: (context, index) => MyCustomCardWidget(item: _items[index]),
Your MyCustomCardWidget must be like this:
class MyCustomCardWidget extends StatelessWidget {
final CartItem item;
const MyCustomCardWidget({Key? key, required this.item}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(child: Text(item.name), ....yourwidget code here);
}
}
You need to pass CardItem object instead of index becuase required is CardItem not index
You need to make
List<CardItem> cartItems=
[
CartItem();
CartItem();
];
Then you need pass in your ListView as cartItems[index]
In your case you have to pass your _items List as _items[index] to ListView.builder
You mean that you wanna use cardItems right?
I think you make a typo, because of name of the list cardItems. then you should pass it in the itembuilder :
like this
buildCard(cardItems[index]);

Using a Future on rows of a DataTable

Sure this is something fundamental, but I'm trying to put the results of a database query into a DataTable widget with the below:
class ADataTable extends StatelessWidget {
ADataTable({Key? key}) : super(key: key);
var resultSet = generateRows();
#override
Widget build(BuildContext context) {
return DataTable(
columns: setHeader(['Date','In Temp','Out Temp']),
rows: resultSet
);
}
}
The signature of generateRows is:
Future<List<DataRow>> generateRows() async
However, as expected I'm getting the error:
The argument type 'Future<List<DataRow>>' can't be assigned to the parameter type 'List<DataRow>'.
I've tried various ways to "cast away" the Future but it seems I just keep propagating a Future no matter how I try, so must be missing something fundamental! Appreciate the help!
You created a StatelessWidget, yet the first real line of code is your widget holding state. That won't work.
Create a StatefulWidget, create a State class for it, then in your state class, create a Future<> variable. You can set this variable in the initState method. Then in the build method, you can use this variable and a FutureBuilder widget to make your widget react to the fact that data may come in after it started to show, since it's async.
See What is a Future and how do I use it? for a more detailed explanation why and a full example.

Return same object type user provided in Dart

So i have this Widget I am writing for flutter, it is a Tinder like swiping cards, I want the consumer to be able to provide a List of any type he wants, and i want to use the same type he provides to return in the builder method he should provide:
class Swipeable extends StatelessWidget {
final List<T> data;
final Widget Function(BuildContext, T) builder;
Swipeable({required this.data, required this.builder});
}
Where T is the type of data the user provides, and it is controlled by the user with no limitations by me.
The consumer should be able to consume the widget like this:
Swipeable(
data: <User>[
User(
name: "Zakaria",
profession: "Geek",
images: [
"https://images.unsplash.com/photo-1533488069324-f9265c15d37f?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=594&q=80",
"https://images.unsplash.com/photo-1583864697784-a0efc8379f70?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80",
],
age: 18,
)
],
builder: (context, user) {
return Text(user.name);
}
)
I hope u understood my Question, I'm not that good in explaining stuff when I'm still a newbie.
You can use a generic class for this.
For more information, consult the Generics section in the language tour.
// The class ( ↓ note the type argument here)
class Swipeable<T> extends StatelessWidget {
final List<T> data;
final Widget Function(BuildContext, T) builder;
Swipeable({required this.data, required this.builder});
}
Type inference should work in your example (you shouldn't even need to specify the list type), but if it doesn't, you can specify the type with Swipeable<Type>.
On another note, you may not even need to pass through your list like this. Consider the ListView and GridView builder constructors, which provide an index instead of an object. You may want to do the same for consistency.

Flutter Stateful Widget Constructor That Does Not Include All Members

I am building a Stateful Widget in Flutter, and as such, there is a requirement for all arguments passed in the constructor to be final (since Stateful widgets are marked with the #immutable annotation).
Thing is, I want to have two different constructors for my widget and to exclude some of the members of the Stateful widget, depending on the constructor used. I have to stress, that I do not want these arguments to be optional, but mandatory.
For example,
class MyWidget extends StatefulWidget {
MyWidget.first({this.firstArgument}};
MyWidget.second({this.secondArgument});
final int firstArgument;
final String secondArgument;
#override
MyWidget createState() => MyWidgetState();
}
When I write this, I get a compiler error, telling me that:
All final variables must be initialized, but 'firstArgument' isn't.
The same goes for the second member variable.
How can I overcome this?
I can't move firstArgument and secondArgument to the state of MyWidget, since I want them to be initialized in the constructor(s) and also because they should not be changed.
I can't mark them as not final since then I will get a compiler warning and also break the Stateful widget paradigm.
Is there a different approach I should use?
Thing is, I want to have two different constructors for my widget and to exclude some of the members of the Stateful widget, depending on the constructor used. I have to stress, that I do not want these arguments to be optional, but mandatory.
If you don't want them to be optional, you need to mark them as required:
MyWidget.first({required this.firstArgument}};
MyWidget.second({required this.secondArgument});
(If you don't have null-safety enabled, you will instead need to use the weaker #required annotation from package:meta.)
My understanding is that you want firstArgument and secondArgument to be required for MyWidget.first and MyWidget.second respectively but that they are not intended to be required together (that is, only one should be set).
You could fix this by explicitly initializing both values in the constructors:
MyWidget.first({required this.firstArgument}} : secondArgument = null;
MyWidget.second({required this.secondArgument}): firstArgument = null;
If you have null-safety enabled, you also would need to make your members nullable:
final int? firstArgument;
final String? secondArgument;
Maybe factory constructors would help?
class MyWidget extends StatefulWidget {
MyWidget._({this.firstArgument, this.secondArgument}};
factory MyWidget.first({#required int first})=>MyWidget._(firstArgument: first, );
factory MyWidget.second({#required String second})=>MyWidget._(secondArgument: second, );
final int firstArgument;
final String secondArgument;
#override
MyWidget createState() => MyWidgetState();
}
This way, you'll only be able to build this widget using these constructors (since the class constructor is private) and when you call MyWidget.first the value for secondArgument for the widget will be null, and the same applies when you use MyWidget.second with firstArgument