I'm trying to create a generic consumer widget that facilitates the ViewModel to its child. therefor I have two functions. one that has a function(T) after init of the ViewModel and the other for passing the model to its child Widget.
in the generic class is a child of ChangeNotifier and that works fine until I want to send the T value in the two Functions.
then I get the following errors:
type '(OnBoardingViewModel) => Null' is not a subtype of type '(ChangeNotifier) => void'
and
type '(BuildContext, OnBoardingViewModel, Widget) => Scaffold' is not a subtype of type '(BuildContext, ChangeNotifier, Widget) => Widget'
But when i change the extends type from ChangeNotifier to OnBoardingViewModel, everything works fine.
can someone help me and or explain why this ain't working??
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:provider/provider.dart';
class StateFullConsumerWidget<T extends ChangeNotifier> extends StatefulWidget{
StateFullConsumerWidget({@required this.builder,Key key,this.onPostViewModelInit,this.child}) : super(key : key);
final Widget Function(BuildContext context, ChangeNotifier value, Widget child) builder;
final Widget child;
final void Function(T) onPostViewModelInit;
@override
_StateFullConsumerWidgetState<T> createState() => _StateFullConsumerWidgetState<T>();
}
class _StateFullConsumerWidgetState<T extends ChangeNotifier> extends State<StateFullConsumerWidget>{
T _viewModel;
@override
void initState() {
// assign the model once when state is initialised
_viewModel = GetIt.instance.get<T>();
widget.onPostViewModelInit(_viewModel);
super.initState();
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<T>(
builder: (context) => _viewModel,
child: Consumer<T>(
builder: widget.builder,
child: widget.child,
),
);
}
}
my widget
StateFullConsumerWidget<OnBoardingViewModel>(
onPostViewModelInit: (viewModel){
buildIntroList(viewModel);
viewModel.maxPages = _introWidgetsList.length;
},
builder: (context,viewModel,child) {
return Scaffold(
key: widget.scaffoldKey,
body: SafeArea(
child: Container(),
),
),
);
},
);
my ViewModel
import 'package:flutter/material.dart';
class OnBoardingViewModel extends ChangeNotifier{
OnBoardingViewModel(){
}
}
The way you are using the generic type T
is incomplete. The relationship between the StateFullConsumerWidget
and the _StateFullConsumerWidgetState
classes as written in your code are such that StateFullConsumerWidget
creates its state using the same T
type parameter as itself, so the widget knows the state uses the same generic type that it does. From the perspective of _StateFullConsumerWidgetState
, though, the class is declared as such:
class _StateFullConsumerWidgetState<T extends ChangeNotifier>
extends State<StateFullConsumerWidget>
The problem is the state class is using the general form of StateFullConsumerWidget
, so there is no explicit relationship between the T
that _StateFullConsumerWidgetState
is receiving as the type parameter and the T
that StateFullConsumerWidget
is using. Dart doesn't know how to reconcile this ambiguous relationship, so it defaults to the lowest common denominator the type constraints allow, which is ChangeNotifier
.
Because of this, when you try to treat T
as OnBoardingViewModel
, Dart throws an error because, as far as the state class knows, the T
of the parent widget is ChangeNotifier
, not OnBoardingViewModel
.
You can fix this by passing the type parameter along when you declare your state class:
class _StateFullConsumerWidgetState<T extends ChangeNotifier>
extends State<StateFullConsumerWidget<T>>
I am not exactly sure why this is the case, but the dart compiler doesn't recognize the type T from the StateFullConsumerWidget as the same type T of the _StateFullConsumerWidgetState. If you pass the functions to the State everything works as expected.
Resulting code:
class StateFullConsumerWidget<T extends ChangeNotifier> extends StatefulWidget{
StateFullConsumerWidget({@required this.builder,Key key,this.onPostViewModelInit,this.child}) : super(key : key);
final Widget Function(BuildContext context, T value, Widget child) builder;
final Widget child;
final Function(T viewModel) onPostViewModelInit;
@override
_StateFullConsumerWidgetState<T> createState() => _StateFullConsumerWidgetState<T>(onPostViewModelInit, builder);
}
class _StateFullConsumerWidgetState<T extends ChangeNotifier> extends State<StateFullConsumerWidget>{
final Function(T viewModel) _onPostViewModelInit;
final Widget Function(BuildContext context, T value, Widget child) _builder;
T _viewModel;
_StateFullConsumerWidgetState(this._onPostViewModelInit, this._builder);
@override
void initState() {
// assign the model once when state is initialised
_viewModel = GetIt.instance.get<T>();
_onPostViewModelInit(_viewModel);
super.initState();
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<T>(
builder: (context) => _viewModel,
child: Consumer<T>(
builder: _builder,
child: widget.child,
),
);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With