创建可重用窗口小部件的函数和类之间有什么区别?
问题内容:
我已经意识到,可以使用普通函数创建小部件,而不用继承StatelessWidget的子类。一个例子是这样的:
Widget function({ String title, VoidCallback callback }) {
return GestureDetector(
onTap: callback,
child: // some widget
);
}
这很有趣,因为它需要 远远 比一个全面的类更少的代码。例:
class SomeWidget extends StatelessWidget {
final VoidCallback callback;
final String title;
const SomeWidget({Key key, this.callback, this.title}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: callback,
child: // some widget
);
}
}
所以我一直在想:创建小部件的函数和类之间在语法上是否有区别?使用函数是否是一种好习惯?
问题答案:
TL; DR:相对于函数,更喜欢使用类来制作 可重用的 小部件树。
编辑 :弥补一些误解:这不是引起问题的函数,而是解决一些问题的类。
如果一个函数可以做同样的事情,Flutter将不会有StatelessWidget。
同样,它主要针对可重用的公共小部件。私有功能只使用一次并不重要,尽管知道这种行为仍然很好。
使用函数而不是使用类之间有一个重要的区别,即:框架不知道函数,但是可以看到类。
考虑以下“窗口小部件”功能:
Widget functionWidget({ Widget child}) {
return Container(child: child);
}
使用这种方式:
functionWidget(
child: functionWidget(),
);
它相当于类:
class ClassWidget extends StatelessWidget {
final Widget child;
const ClassWidget({Key key, this.child}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
child: child,
);
}
}
这样使用:
new ClassWidget(
child: new ClassWidget(),
);
在纸上,两者似乎做的完全一样:创建2 Container
,一个嵌套在另一个中。但是实际情况略有不同。
对于函数,生成的窗口小部件树如下所示:
Container
Container
在使用类时,小部件树为:
ClassWidget
Container
ClassWidget
Container
这很重要,因为它会更改更新小部件时框架的行为。
为什么这么重要
通过使用函数将窗口小部件树拆分为多个窗口小部件,您将面临许多错误,并错过了一些性能优化。
不能保证通过使用函数 会 遇到错误,但是通过使用类,可以 保证 不会遇到这些问题。
以下是Dartpad上的一些交互式示例,您可以运行这些示例来更好地理解问题:
-
https://dartpad.dev/1870e726d7e04699bc8f9d78ba71da35
此示例展示了如何通过将应用拆分为功能来意外破坏诸如AnimatedSwitcher
-
https://dartpad.dev/a869b21a2ebd2466b876a5997c9cf3f1
此示例展示了类如何允许对小部件树进行更细粒度的重建,从而提高了性能 -
https://dartpad.dev/06842ae9e4b82fad917acb88da108eee
此示例展示了如何通过使用函数使自己暴露于滥用BuildContext并在使用InheritedWidgets(例如Theme或provider)时面临错误。
结论
以下是精选的使用函数和类之间的区别的列表:
-
类:
-
允许性能优化(const构造函数,更精细的重建)
- 确保在两个不同的布局之间切换可以正确处理资源(功能可以重用某些先前的状态)
- 确保热装正常(使用功能可能会中断热装等
showDialogs
) - 已集成到小部件检查器中。
- 我们
ClassWidget
在devtool所显示的小部件树中看到,这有助于了解屏幕上的内容 - 我们可以重写debugFillProperties来打印传递给窗口小部件的参数是
- 我们
-
更好的错误消息
如果发生异常(例如ProviderNotFound),框架将为您提供当前正在构建的小部件的名称。如果仅将小部件树拆分为功能+
Builder
,则错误将没有有用的名称 -
可以定义键
-
可以使用上下文API
-
功能:
-
更少的代码(可以使用生成代码的functional_widget来解决)
总体而言,由于这些原因,在类上使用函数来重用窗口小部件被认为是不好的做法。
您 可以 ,但是将来可能会咬您。