Flutter: ошибка подтипа при создании виджета с обобщениями - PullRequest
0 голосов
/ 29 января 2020

Я использую модели представления для моего приложения, которые расширяют ChangeNotifier. Чтобы использовать их, я пытаюсь реализовать базовый c базовый виджет:


    class BasePageWidget<ViewModelType extends ChangeNotifier>
        extends StatefulWidget {
      final Function(ViewModelType vm) init;
      final Widget Function(BuildContext context, ViewModelType vm) builder;
      final ViewModelType _vm = sl.get<ViewModelType>();

      BasePageWidget({Key key, this.init, this.builder}) : super(key: key);

      @override
      _BasePageWidgetState createState() => _BasePageWidgetState<ViewModelType>();
    }

    class _BasePageWidgetState<ViewModelType extends ChangeNotifier>
        extends State<BasePageWidget> {
      @override
      Widget build(BuildContext context) {
        return ChangeNotifierProvider<ViewModelType>.value(
          value: widget._vm,
          child: Consumer<ViewModelType>(
            builder: (context, _vm, child) => widget.builder(context, widget._vm),
          ),
        );
      }

      @override
      void initState() {
        if (widget.init != null) {
          widget.init(widget._vm);
        }
        super.initState();
      }
    }

Базовый виджет имеет параметр типа и получает экземпляр модели представления с помощью сервисного локатора пакета get_it.

Но когда я пытаюсь запустить приложение, при использовании виджета:

void main() {
  setup();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: BasePageWidget<ViewModel>(
        init: (vm) => vm.setTitle("Set init State"),
        builder: (context, vm) => FlatButton(
          onPressed: () {
            vm.setTitle("State2");
          },
          child: Text(vm.text),
        ),
      ),
    );
  }
}

Flutter выдает ошибку времени выполнения:

flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
flutter: The following assertion was thrown building Builder:
flutter: type '(ViewModel) => void' is not a subtype of type '(ChangeNotifier) => dynamic'
flutter:
flutter: The relevant error-causing widget was:
flutter:   MaterialApp 
lib/main.dart:15
flutter:
flutter: When the exception was thrown, this was the stack:
flutter: #0      _BasePageWidgetState.initState 
package:viewmodel_test/base_widget.dart:31
flutter: #1      StatefulElement._firstBuild 
package:flutter/…/widgets/framework.dart:4456
flutter: #2      ComponentElement.mount 
package:flutter/…/widgets/framework.dart:4302
flutter: #3      Element.inflateWidget 
package:flutter/…/widgets/framework.dart:3297
flutter: #4      Element.updateChild 
package:flutter/…/widgets/framework.dart:3091
flutter: #5      SingleChildRenderObjectElement.mount 

Кажется, ошибки вызваны аргументы функции:

final Function(ViewModelType vm) init;
final Widget Function(BuildContext context, ViewModelType vm) builder;

Когда я обмениваю тип generi c (ViewModelType) с Dynami c, приложение работает нормально (конечно, без безопасности типов):

final Function(ViewModelType vm) init;
final Widget Function(BuildContext context, ViewModelType vm) builder;

Есть идеи, как заставить его работать с параметрами c?

Вот моя модель представления:

class ViewModel extends ChangeNotifier {
  String text = '';

  void setTitle(String text) {
    this.text = text;
    notifyListeners();
  }
}

и настройка get_it:

GetIt sl = GetIt.instance;

Future<void> setup() async {
  sl.registerFactory(() => ViewModel());
}

1 Ответ

0 голосов
/ 30 января 2020

Вы можете скопировать полный код вставки ниже
, измените State<BasePageWidget> на State<BasePageWidget<ViewModelType>>

с

class _BasePageWidgetState<ViewModelType extends ChangeNotifier>
    extends State<BasePageWidget> {

на

class _BasePageWidgetState<ViewModelType extends ChangeNotifier>
extends State<BasePageWidget<ViewModelType>> {

рабочий демо

enter image description here

полный код

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:get_it/get_it.dart';

class BasePageWidget<ViewModelType extends ChangeNotifier>
    extends StatefulWidget {
  final Function(ViewModelType vm) init;
  final Widget Function(BuildContext context, ViewModelType vm) builder;
  final ViewModelType _vm = sl.get<ViewModelType>();

  BasePageWidget({Key key, this.init, this.builder}) : super(key: key);

  @override
  _BasePageWidgetState createState() => _BasePageWidgetState<ViewModelType>();
}

class _BasePageWidgetState<ViewModelType extends ChangeNotifier>
    extends State<BasePageWidget<ViewModelType>> {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<ViewModelType>.value(
      value: widget._vm,
      child: Consumer<ViewModelType>(
        builder: (context, _vm, child) => widget.builder(context, widget._vm),
      ),
    );
  }

  @override
  void initState() {
    if (widget.init != null) {
      widget.init(widget._vm);
    }
    super.initState();
  }
}

class ViewModel extends ChangeNotifier {
  String text = '';

  void setTitle(String text) {
    this.text = text;
    notifyListeners();
  }
}

GetIt sl = GetIt.instance;

Future<void> setup() async {
  sl.registerFactory(() => ViewModel());
}

void main() {
  setup();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: BasePageWidget<ViewModel>(
        init: (vm) => vm.setTitle("Set init State"),
        builder: (context, vm) => Scaffold(
          body: Center(
            child: FlatButton(
              onPressed: () {
                vm.setTitle("State2");
              },
              child: Text(vm.text),
            ),
          ),
        ),
      ),
    );
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...