Search code examples
flutterdartexception

Why is the type '($someObject$) => $someWidget$' not a subtype of type '(dynamic) => Widget'?


I have an flutter app that throws this error, at a specific action in the application:

======== Exception caught by widgets library =======================================================
The following _TypeError was thrown building DataFetcher<CompleteVocabularyCollection>(dirty, state: _DataFetcherState<CompleteVocabularyCollection>#da61b):
type '(CompleteVocabularyCollection) => DetailsDisplay' is not a subtype of type '(dynamic) => Widget'

The relevant error-causing widget was: 
  DataFetcher<CompleteVocabularyCollection> DataFetcher:file://***/lib/screens/details/details.dart:56:18
When the exception was thrown, this was the stack: 
#0      _DataFetcherState.build (***/components/data_fetcher.dart:60:31)
[...]

Snippet from details.dart:

// [...]
DataFetcher<CompleteVocabularyCollection>(
            loadData: () async {
              CompleteVocabularyCollection? vocabularyCollection =
                await widget.futureCollection;
              if (vocabularyCollection == null) {
                throw NoDataException();
              }
              return vocabularyCollection;
            },
            loadingWidget: LoadingDisplay(
              infoText: widget.importMode ? "Reading file..." : "Loading data...",
            ),
            onError: (exception) {
              String error = "Not available";

              if (exception is FilePickingAbortedException) {
                Navigator.pop(context);
              } else if (exception is NoDataException) {
                error = "No data found. That means usually means, "
                    "that the requested Vocabulary Collection does not exist.";
              } else if (exception is BrokenFileException) {
                error = "The provided JSON-File is not in the correct format.";
              } else {
                error = exception.toString();
              }

              return PlaceholderDisplay(
                icon: Icons.error,
                headline: "An error occurred",
                moreInfo: "More info:\n$error");
            },
            onFinished: (data) {
              return DetailsDisplay(vocabularyCollection: data, importMode: widget.importMode);
            },
        )
// [...]

data_fetcher.dart file:

import 'package:flutter/material.dart';

import '../models/data_fetching_state.dart';

class DataFetcher<T> extends StatefulWidget {
  final Future<T> Function() loadData;
  final Widget loadingWidget;
  final Widget Function(Object exception) onError;
  final Widget Function(T data) onFinished;

  const DataFetcher(
      {Key? key,
      required this.loadData,
      required this.loadingWidget,
      required this.onError,
      required this.onFinished})
      : super(key: key);

  @override
  State<DataFetcher> createState() => _DataFetcherState<T>();
}

class _DataFetcherState<T> extends State<DataFetcher> {
  final DataFetchingState _state = DataFetchingState<T>();
  
  // [...] The code here is not interresting for the exception
  
  @override
  Widget build(BuildContext context) {
    Widget resultWidget;

    switch(_state.state) {
      case LoadingState.initial:
        resultWidget = const Text("Loading not started. Will be started soon.");
        break;
      case LoadingState.loading:
        resultWidget = widget.loadingWidget;
        break;
      case LoadingState.error:
        resultWidget = widget.onError(_state.exception);
        break;
      case LoadingState.finished:
        resultWidget = widget.onFinished(_state.data); // Here the exception occurs
        break;
    }

    return resultWidget;
  }
}

The definition of the DetailsDisplay looks like this:

class DetailsDisplay extends StatelessWidget {

and the CompleteVocabularyCollection is just a normal dart class.

So if I look through all this, I came up that (CompleteVocabularyCollection) => DetailsDisplay is a subtype of (dynamic) => Widget, because CompleteVocabularyCollection is just a normal class (so it should be a subtype of dynamic) and DetailsDisplay is a widget. So whats wrong?


Solution

  • I'm not entirely sure why it happens but replacing

            onFinished: (data) {
              return DetailsDisplay(vocabularyCollection: data, importMode: widget.importMode);
            },
    

    with

            onFinished: (dynamic data) {
              return DetailsDisplay(vocabularyCollection: data, importMode: widget.importMode);
            },
    

    might solve it. I tried to reproduce your problem with this minimal example and I got the same error when I left out the dynamic. It works fine as written:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MaterialApp(home: MyApp<String>(onFinished: (dynamic c)=> Text(c))));
    }
    
    class MyApp<T> extends StatefulWidget {
    
      final Widget Function(T data) onFinished;
    
      const MyApp({super.key, required this.onFinished});
    
      @override
      MyAppState createState() => MyAppState();
    }
    
    class MyAppState extends State<MyApp>{
      @override
      Widget build(BuildContext context) {
        return Scaffold(
                body: widget.onFinished('test')
        );
      }
    }
    

    It seems it doesn't really do much with the generic type T and considers onFinished to always require a dynamic parameter. I don't really know why