0

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

  1. Execute flutter run on the code sample
  2. There are two type of flows that behaves in a similar way, I've created the project above to represent those scenarios
  3. 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.

Code sample

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'

Logs ``` 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
  • I am not sure why it expects `(dynamic) => String` there. But for a function *g* to be a subtype of some function *f*, *g*'s parameter would actually have to be a supertype of *f*'s, as explained [here](https://stackoverflow.com/questions/1150372/confused-about-function-subtyping). Hopefully that clears up that little aside of yours, anyway. – Noah Aug 31 '22 at 22:20

1 Answers1

2

In the state class of TypeErrorWidget, set the Type like so:

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<T>> createState() => _TypeErrorWidgetState<T>();
}

class _TypeErrorWidgetState<T> extends State<TypeErrorWidget<T>> {
  late List<String> _itemsIds;

  @override
  void initState() {
    _itemsIds = widget.items.map((e) {
      return widget.idMapper(e);
    }).toList();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Text("I'll crash"),
        Text(_itemsIds.toString()),
      ],
    );
  }
}
Josteve
  • 11,459
  • 1
  • 23
  • 35
  • Interesting @josteve, I made the change and it now works. Thank you! So that was 1 usecase, do you think it's possible to make it work also for the 2nd usecase (freezed - union) example? – Victor Rene Sep 01 '22 at 01:19