Flutter:使用泛型创建小部件时出现子类型错误
Flutter: Subtype Error when creating an widget with generics
我正在为我的应用程序使用扩展 ChangeNotifier 的视图模型。为了使用它们,我正在尝试实现一个通用的基本小部件:
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;
当我将通用类型 (ViewModelType) 与动态交换时,应用 运行 没问题(当然没有类型安全):
final Function(ViewModelType vm) init;
final Widget Function(BuildContext context, ViewModelType vm) builder;
知道如何使用通用参数 运行 吗?
这是我的视图模型:
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());
}
您可以复制粘贴 运行 下面的完整代码
请将 State<BasePageWidget>
更改为 State<BasePageWidget<ViewModelType>>
来自
class _BasePageWidgetState<ViewModelType extends ChangeNotifier>
extends State<BasePageWidget> {
至
class _BasePageWidgetState<ViewModelType extends ChangeNotifier>
extends State<BasePageWidget<ViewModelType>> {
工作演示
完整代码
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),
),
),
),
),
);
}
}
我正在为我的应用程序使用扩展 ChangeNotifier 的视图模型。为了使用它们,我正在尝试实现一个通用的基本小部件:
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;
当我将通用类型 (ViewModelType) 与动态交换时,应用 运行 没问题(当然没有类型安全):
final Function(ViewModelType vm) init;
final Widget Function(BuildContext context, ViewModelType vm) builder;
知道如何使用通用参数 运行 吗?
这是我的视图模型:
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());
}
您可以复制粘贴 运行 下面的完整代码
请将 State<BasePageWidget>
更改为 State<BasePageWidget<ViewModelType>>
来自
class _BasePageWidgetState<ViewModelType extends ChangeNotifier>
extends State<BasePageWidget> {
至
class _BasePageWidgetState<ViewModelType extends ChangeNotifier>
extends State<BasePageWidget<ViewModelType>> {
工作演示
完整代码
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),
),
),
),
),
);
}
}