Dart / Flutter - abstract class not working - flutter

I'm trying to make a generic dropdown widget with the current code:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
abstract class FormDropdownProtocol {
String get title;
}
class FormDropdown<FormDropdownProtocol> extends StatelessWidget {
const FormDropdown({this.value, this.items, this.onChanged});
final FormDropdownProtocol value;
final List<FormDropdownProtocol> items;
final ValueChanged<FormDropdownProtocol> onChanged;
#override
Widget build(BuildContext context) {
return DropdownButton<FormDropdownProtocol>(
value: value,
items: items.map<DropdownMenuItem<FormDropdownProtocol>>(
(FormDropdownProtocol value) {
return DropdownMenuItem<FormDropdownProtocol>(
value: value,
child: Text(value.title),
);
}).toList(),
onChanged: onChanged,
);
}
}
But I'm getting the following error when trying to use the title:
Error: The getter 'title' isn't defined for the class 'Object'.
- 'Object' is from 'dart:core'.
Try correcting the name to the name of an existing getter, or defining a getter or field named > 'title'.
child: Text(value.title)
What am I doing wrong?

class FormDropdown<FormDropdownProtocol> extends StatelessWidget {
This defines a generic class (FormDropdown) with a type parameter named FormDropdownProtocol. That type parameter could be named anything; it might be clearer to understand what's going on if it were:
class FormDropdown<T> extends StatelessWidget {
Dart generics aren't like C++ templates where the class is instantiated with the formal type information before everything is resolved. With Dart, everything is resolved before the generic is instantiated with a type.
In the case of class FormDropdown<T>, nothing is known about T. No constraints are given, so it can only be deduced to be an Object. And indeed, Object has no title member.
If you instead do class FormDropdown<T extends FormDropdownProtocol>, then T is now constrained to be FormDropdownProtocol, which does have a title member.

Related

How to create a local Object instance from the generic type

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 ?

How to use generic with interface in dart, flutter

I am trying to use generic with interface in dart lang in flutter framework.
Basically, I would like to set the type of the generic to an implementation of ITestClass.
in the typedef definition, I am setting the "T" type to extend ITestClass (because you cannot implement with generic in dart). But when I actually use this typedef in the build method, it throws following error
The argument type 'TestClass' can't be assigned to the parameter type 'T'.
From my knowledge, TestClass is an implementation of ITestClass so type error should not be thrown. How can I overcome this problem?
import 'package:flutter/material.dart';
class ITestClass {}
class TestClass implements ITestClass {}
typedef TestTypedef<T extends ITestClass> = Widget Function(T model);
void main() {
runApp(
MaterialApp(
home: TestWidget(
testTypedef: (model) => Container(),
),
),
);
}
class TestWidget<T extends ITestClass> extends StatelessWidget {
final TestTypedef<T> testTypedef;
TestWidget({
required this.testTypedef,
});
Widget build(BuildContext context) {
return Scaffold(
body: testTypedef(
TestClass(), // error here
),
);
}
}

Dart correct way to specify generic argument type on callback function

I'vo got a strange error, for a class similar to this one:
class UpdatableListPage<T> extends ConsumerStatefulWidget {
final StateNotifierProvider<UpdatableNotifier, List<T>> provider;
final Widget Function(T t) callbackWidget;
[...]
#override
_UpdatableListPageState<T> createState() => _UpdatableListPageState<T>();
}
class _UpdatableListPageState<T> extends ConsumerState<UpdatableListPage> {
#override
Widget build(BuildContext context) {
// Here the IDE said modelList is dynamic
var modelList = ref.watch(widget.provider);
[...]
ListView(
key: _refreshKey,
shrinkWrap: true,
scrollDirection: widget.scrollDirection,
children: [
for (final product in modelList as List<T>) widget.callbackWidget.call(product),
],
}
}
And I call the funciton as:
UpdatableListPage<RsMsgMetaData>(
userPostsProvider,
callbackWidget: (t) => PostTeaserCard(t,),
),
Where PostTeaserCard is a statefull Widget that recieve a RsMsgMetaData object as parameter.
The IDE say that everything is Ok but at run time, I got the following error:
type '(RsMsgMetaData) => PostTeaserCard' is not a subtype of type '(dynamic) => Widget'`
Seems like callbackWidget acts as (dynamic) => Widget function, but anyway... Should this function be compatible with the function signature of the anonymous function, right?
I don't know what is going on with this...
You wrote:
class _UpdatableListPageState<T> extends ConsumerState<UpdatableListPage> {
which is equivalent to:
class _UpdatableListPageState<T> extends ConsumerState<UpdatableListPage<dynamic>> {
Consequently, the type of _UpdatableListPageState<T>.widget.callbackWidget is Widget Function(dynamic t). You cannot pass a PostTeaserCard Function(RsMsgMetaData) for a Widget Function(dynamic) because the latter is callable with any argument, but the function you actually passed can be called only with an RsMsgMetaData argument.
To fix this, fix your _UpdatableListPageState class declaration to avoid the implicit use of dynamic and to fully depend on T:
class _UpdatableListPageState<T> extends ConsumerState<UpdatableListPage<T>> {
This probably will fix the type for modelList to List<T> instead of List<dynamic>.
Enabling the strict-raw-types check in your analysis_options.yaml should help catch this kind of error in the future.

error: The return type 'CategoryState' isn't a 'Widget', as required by the closure's context

I tried to put the route to Another Class on the Same Page and wrote this code as mentioned in the flutter Doc, but I get this:
error: The return type CategoryState isn't a Widget, as required
by the closure's context.
Page code:
Navigation code:
According to the documentation: the builder function of the MaterialPageRoute class should return a widget. Both StatelessWidget and StatefulWidget extend the Widget class and thus can be used here.
But in your case the CategoryState class does not extend any of them, and it's the reason you are having that error.
You need to use the Category class instead.
It should then look like:
PAGE :
class Category extends StatefulWidget {
#override
_CategoryState createState() => _CategoryState();
}
class _CategoryState extends State<Category> {
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(child: Text("Music")),
Container(child: Text("Projects")),
Container(child: Text("Essay")),
],
);
}
}
NAVIGATION
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Category())
);
Furthermore, I would advise you to add a suffix to this class name (e.g. CategoryScreen) in order to avoid name conflicts in case you have another class (e.g. a model) with the same name.
class CategoryState is not a widget it's just a Dart class you want to navigate to Category()

Dart: Closure types not matching when adding builder

I'm working on my first Flutter project having spent many years on Objective-C and Swift so I'm very much learning the quirks of Dart vs the languages I'm familiar with. Currently I'm trying to follow the patterns I'm seeing in Dart and Flutter to add a builder to a class like this:
// Type def of builder closure.
typedef RecordWidgetBuilder<T> = Widget Function(#required T);
// Widget to display a list of records
class GroupedByDateWidget<T> extends StatefulWidget {
final List<T> items;
final RecordWidgetBuilder<T> widgetBuilder;
GroupedByDateWidget({#required this.items, #required this.widgetBuilder});
#override
_GroupedByDateWidgetState createState() => _GroupedByDateWidgetState(items: items, widgetBuilder: widgetBuilder);
}
class _GroupedByDateWidgetState<Item> extends State<GroupedByDateWidget> {
final List<Item> items;
final RecordWidgetBuilder<Item> widgetBuilder;
_GroupedByDateWidgetState({#required this.items, #required this.widgetBuilder});
// ... Rest of class
}
I then have this widget class that I want the closure to use:
// Widget that displays a record.
class SummarisedWorkRecordWidget extends StatelessWidget {
final WorkRecord record;
SummarisedWorkRecordWidget(this.record);
// ... rest of class
Then I'm trying to use these classes like this:
class _RecordViewState extends State<RecordView> {
#override
Widget build(BuildContext context) {
return GroupedByDateWidget<WorkRecord>(
items: [WorkRecord(), WorkRecord()],
widgetBuilder: (record) => SummarisedWorkRecordWidget(record),
);
}
}
It compiles, but when I run this I get this error:
The following _TypeError was thrown building RecordView(state: _RecordViewState#ade05):
type '(WorkRecord) => SummarisedWorkRecordWidget' is not a subtype of type '(dynamic) => Widget'
The relevant error-causing widget was:
RecordView file:///Users/derekclarkson/Work/projects/FWO/record_my_hours/lib/HomePage.dart:37:13
When the exception was thrown, this was the stack:
#0 GroupedByDateWidget.createState (package:record_my_hours/widgets/GroupedByDateWidget.dart:17:24)
#1 new StatefulElement (package:flutter/src/widgets/framework.dart:4764:24)
...
At this point I'm assuming that closures cannot match on inherited types, but that seem to clash with what I'm seeing in the APIs. Can anyone shed some light on why this isn't working an how to address it?
Solved it. The problem turned out to be that when I created the _GroupedByDateWidget State instance I didn't also specify the generic qualification to it that I'd added. So it instantiated a state with an dynamic generic and failed.
So:
#override
_GroupedByDateWidgetState createState() => _GroupedByDateWidgetState(items: items, widgetBuilder: widgetBuilder);
Should have been:
#override
_GroupedByDateWidgetState createState() => _GroupedByDateWidgetState<Item>(items: items, widgetBuilder: widgetBuilder);
Small thing.