Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

passing generic type by Function(T) in flutter

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(){

  }
}
like image 704
quantum apps Avatar asked Sep 11 '19 09:09

quantum apps


2 Answers

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>>
like image 72
Abion47 Avatar answered Nov 11 '22 04:11

Abion47


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,
      ),
    );
  }
}
like image 27
Rene Avatar answered Nov 11 '22 03:11

Rene