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?
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