First of all, I´ve already submitted an [issue][1] on Flutter's team repository since to me this is an issue on Flutter/Dart but maybe I´m wrong.
Steps to Reproduce
- Execute
flutter run
on the code sample - There are two type of flows that behaves in a similar way, I've created the project above to represent those scenarios
- You will find 2 buttons that in each case will be redirected to a new page that can trigger each failure
Expected results:
In the "widget example" I'm creating a generic stateful widget TypeErrorWidget<T>
that has a property of type:
final IdMapper<T> idMapper;
where IdMapper<T>
is defined as:
typedef IdMapper<T> = String Function(T item);
later on, this property is accessed inside the state to map an iterable of items and extract the ids with the function provided:
_itemsIds = widget.items.map((e) {
return widget.idMapper(e);
}).toList();
I'm expecting to get the ids as a list of List<String>
Actual results:
However, when runtime reaches that line it's failing complaining about the following: '(ExampleListItem) => String' is not a subtype of type '(dynamic) => String'
, which doesn't have much sense since dynamic
should contain my custom type in any case.
lib/presentation/widgets/type_error_widget.dart
class TypeErrorWidget<T> extends StatefulWidget {
final Iterable<T> items;
final IdMapper<T> idMapper;
const TypeErrorWidget({
Key? key,
required this.items,
required this.idMapper,
}) : super(key: key);
@override
State<TypeErrorWidget> createState() => _TypeErrorWidgetState();
}
class _TypeErrorWidgetState extends State<TypeErrorWidget> {
late List<String> _itemsIds;
@override
void initState() {
_itemsIds = widget.items.map((e) {
return widget.idMapper(e); <- it throws!
}).toList();
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
const Text("I'll crash"),
Text(_itemsIds.toString()),
],
);
}
}
class WidgetExamplePage extends StatefulWidget {
const WidgetExamplePage({super.key});
@override
State<WidgetExamplePage> createState() => _WidgetExamplePageState();
}
class _WidgetExamplePageState extends State<WidgetExamplePage> {
bool _makeItCrash = false;
bool _makeItWork = false;
final List<ExampleListItem> _items = const [
ExampleListItem(
id: "id1",
title: "Title1",
),
ExampleListItem(
id: "id2",
title: "Title2",
),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("Widget error")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (_makeItCrash)
TypeErrorWidget<ExampleListItem>(
items: _items,
idMapper: (item) {
return item.id;
},
),
ElevatedButton(
onPressed: () {
setState(() {
_makeItWork = false;
_makeItCrash = true;
});
},
child: const Text("Make it crash"),
),
if (_makeItWork && !_makeItCrash)
TypeWorkaroundWidget<ExampleListItem>(
items: _items,
idMapper: (item) {
return item.id;
},
),
if (!_makeItCrash)
ElevatedButton(
onPressed: () {
setState(() {
_makeItWork = true;
_makeItCrash = false;
});
},
child: const Text("Make it works"),
),
],
),
),
);
}
}
Logs
The following _TypeError was thrown building KeyedSubtree-[GlobalKey#196d8]:
type '(ExampleListItem) => String' is not a subtype of type '(dynamic) => String'
The relevant error-causing widget was:
Scaffold
Scaffold:file:///Users/victor/Documents/personal/development/flutter/type_issues/lib/presentation/pages/widget_example_page.dart:29:12
When the exception was thrown, this was the stack:
#0 _TypeErrorWidgetState.initState.<anonymous closure> (package:type_issues/presentation/widgets/type_error_widget.dart:26:21)
#1 MappedListIterable.elementAt (dart:_internal/iterable.dart:413:31)
#2 ListIterator.moveNext (dart:_internal/iterable.dart:342:26)
#3 new _GrowableList._ofEfficientLengthIterable (dart:core-patch/growable_array.dart:189:27)
#4 new _GrowableList.of (dart:core-patch/growable_array.dart:150:28)
#5 new List.of (dart:core-patch/array_patch.dart:51:28)
#6 ListIterable.toList (dart:_internal/iterable.dart:213:44)
#7 _TypeErrorWidgetState.initState (package:type_issues/presentation/widgets/type_error_widget.dart:27:8)
#8 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:5015:57)
#9 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4853:5)
#10 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3863:16)
#11 MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6435:36)
#12 Element.updateChild (package:flutter/src/widgets/framework.dart:3592:18)
#13 RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:5964:32)
#14 MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6460:17)
#15 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#16 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6307:14)
#17 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#18 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#19 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#20 StatelessElement.update (package:flutter/src/widgets/framework.dart:4956:5)
#21 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#22 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#23 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#24 StatelessElement.update (package:flutter/src/widgets/framework.dart:4956:5)
#25 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#26 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#27 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#28 ProxyElement.update (package:flutter/src/widgets/framework.dart:5228:5)
#29 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#30 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#31 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#32 ProxyElement.update (package:flutter/src/widgets/framework.dart:5228:5)
#33 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#34 RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:5904:32)
#35 MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6460:17)
#36 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#37 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#38 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#39 ProxyElement.update (package:flutter/src/widgets/framework.dart:5228:5)
#40 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#41 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#42 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5050:11)
#43 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#44 StatefulElement.update (package:flutter/src/widgets/framework.dart:5082:5)
#45 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#46 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#47 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5050:11)
#48 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#49 StatefulElement.update (package:flutter/src/widgets/framework.dart:5082:5)
#50 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#51 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#52 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#53 ProxyElement.update (package:flutter/src/widgets/framework.dart:5228:5)
#54 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#55 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#56 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5050:11)
#57 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#58 StatefulElement.update (package:flutter/src/widgets/framework.dart:5082:5)
#59 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#60 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6307:14)
#61 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#62 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#63 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#64 ProxyElement.update (package:flutter/src/widgets/framework.dart:5228:5)
#65 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#66 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6307:14)
#67 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#68 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#69 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5050:11)
#70 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#71 StatefulElement.update (package:flutter/src/widgets/framework.dart:5082:5)
#72 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#73 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#74 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5050:11)
#75 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#76 StatefulElement.update (package:flutter/src/widgets/framework.dart:5082:5)
#77 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#78 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#79 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#80 ProxyElement.update (package:flutter/src/widgets/framework.dart:5228:5)
#81 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#82 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#83 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#84 ProxyElement.update (package:flutter/src/widgets/framework.dart:5228:5)
#85 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#86 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#87 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#88 ProxyElement.update (package:flutter/src/widgets/framework.dart:5228:5)
#89 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#90 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#91 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5050:11)
#92 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#93 StatefulElement.update (package:flutter/src/widgets/framework.dart:5082:5)
#94 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#95 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#96 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#97 ProxyElement.update (package:flutter/src/widgets/framework.dart:5228:5)
#98 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#99 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#100 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5050:11)
#101 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#102 StatefulElement.update (package:flutter/src/widgets/framework.dart:5082:5)
#103 Element.updateChild (package:flutter/src/widgets/framework.dart:3570:15)
#104 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4904:16)
#105 StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5050:11)
#106 Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#107 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2667:19)
#108 WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:882:21)
#109 RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:378:5)
#110 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1175:15)
#111 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1104:9)
#112 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1015:5)
#113 _invoke (dart:ui/hooks.dart:148:13)
#114 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:318:5)
#115 _drawFrame (dart:ui/hooks.dart:115:31)
To workaround that issue I need to pass the widget property down to the widget state through it's constructor:
lib/presentation/widgets/type_error_workaround.dart
...
@override
State<TypeWorkaroundWidget> createState() => _TypeErrorWidgetState<T>(
idMapper: idMapper,
);
...
class _TypeErrorWidgetState<T> extends State<TypeWorkaroundWidget> {
late List<String> _itemsIds;
final IdMapper<T> idMapper;
_TypeErrorWidgetState({
required this.idMapper,
});
@override
void initState() {
_itemsIds = widget.items.map((e) {
return idMapper(e) <-- it won't throw;
}).toList();
super.initState();
}
Aside from this issue which I have found a workaround I'm having a similar issue, in this example I'm using a Freezed union type class:
lib/utils/types/freezed_example_type.dart
@freezed
class FreezedExampleType<T extends Object> with _$ExampleClass<T> {
factory FreezedExampleType.example({
required TypedFnExample<T> callback,
}) = _Example<T>;
}
typedef TypedFnExample<T> = String Function(T item);
The idea is that later on I can push many instances of FreezedExampleType into an array and later I can put some logic to act depending on the class type, in my example I simplified this by just pushing one item:
class FreezedPage extends StatefulWidget {
const FreezedPage({super.key});
@override
State<FreezedPage> createState() => _FreezedPageState();
}
class _FreezedPageState extends State<FreezedPage> {
bool _makeItCrash = false;
bool _makeItWork = false;
List<FreezedExampleType> exampleType = List<FreezedExampleType>.of([]);
@override
void initState() {
exampleType.add(
FreezedExampleType<ExampleListItem>.example(
callback: (item) {
return item.id;
},
),
);
super.initState();
}
...
Later on, we access the type on the array and try to act on it's typed property:
_userType() {
const item = ExampleListItem(
id: "exampleId",
title: "Example Title",
);
print(
exampleType.first.mapOrNull(
example: (value) {
return value.callback(item); <- This will throw! =c
},
),
);
}
Unfortunately this throws with the same type of error: type '(ExampleListItem) => String' is not a subtype of type '(Object) => String'
When the exception was thrown, this was the stack: #0 _FreezedPageState._userType. (package:type_issues/presentation/pages/freezed_example_page.dart:86:24) #1 _$_Example.mapOrNull (package:type_issues/utils/types/freezed_example_type.freezed.dart:198:21) #2 _FreezedPageState._userType (package:type_issues/presentation/pages/freezed_example_page.dart:84:25) #3 _FreezedPageState.build. (package:type_issues/presentation/pages/freezed_example_page.dart:51:17) #4 _InkResponseState.handleTap (package:flutter/src/material/ink_well.dart:1072:21) #5 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:253:24) #6 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:627:11) #7 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:306:5) #8 BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:239:7) #9 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:615:9) #10 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:98:12) #11 PointerRouter._dispatchEventToRoutes. (package:flutter/src/gestures/pointer_router.dart:143:9) #12 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:617:13) #13 PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:141:18) #14 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:127:7) #15 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:460:19) #16 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:440:22) #17 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:337:11) #18 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:395:7) #19 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:357:5) #20 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:314:7) #21 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:295:7) #22 _invoke1 (dart:ui/hooks.dart:167:13) #23 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:341:7) #24 _dispatchPointerDataPacket (dart:ui/hooks.dart:94:31)
Handler: "onTap" Recognizer: TapGestureRecognizer#90ba6
</details>
[1]: https://github.com/flutter/flutter/issues/110718