我已经意识到,可以使用普通函数来创建小部件,而不用对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
);
}
}
型
所以我一直在想除了语法之外,函数和类在创建小部件时还有什么区别吗?使用函数是一个好的实践吗?
7条答案
按热度按时间bweufnob1#
编辑:Flutter团队现在已经对这个问题采取了官方立场,并声明类更可取。
TL;DR:更喜欢使用类而不是函数来制作可重用的widget-tree。
编辑:为了弥补一些误解:这不是关于引起问题的函数,而是解决问题的类。
如果一个函数可以做同样的事情,Flutter就不会有StatelessWidget。
类似地,它主要针对的是公共小部件,这些小部件可以被重用,而对于只使用一次的私有函数来说,这并不重要--尽管意识到这种行为仍然是好的。
使用函数而不是类之间有一个重要的区别,那就是:框架不知道函数,但可以看到类。
考虑下面的“widget”函数:
用这种方法:
它是等价类:
是这样使用的:
从理论上看,两者似乎做着完全相同的事情:创建2个
Container
,其中一个嵌套到另一个中,但实际情况略有不同。对于函数,生成的小部件树如下所示:
使用类时,小部件树为:
这一点很重要,因为它改变了框架在更新小部件时的行为方式。
为什么这很重要
通过使用函数将小部件树拆分为多个小部件,您会暴露在bug中,并错过一些性能优化。
不能保证您使用函数会有bug,但是通过使用类,您保证不会面临这些问题。
下面是Dartpad上的几个交互式示例,您可以自己运行这些示例来更好地理解问题:
此示例展示了如何通过将应用拆分为函数,意外地破坏
AnimatedSwitcher
等功能结论
下面列出了使用函数和类之间的区别:
1.类别:
showDialogs
和类似的热重新加载)ClassWidget
,这有助于理解屏幕上显示的内容如果发生异常(如ProviderNotFound),框架将给予当前构建的小部件的名称。如果只在函数+
Builder
中拆分小部件树,则错误不会有有用的名称1.职能:
总的来说,由于这些原因,在类上使用函数来重用小部件被认为是一种不好的做法。
你可以,但它可能会咬你在未来。
u5i3ibmn2#
这两天我一直在研究这个问题,我得出了以下结论:将应用程序的各个部分分解为函数是可以的。理想情况下,这些函数返回
StatelessWidget
,以便进行优化,例如将StatelessWidget
设为const
,这样它就不会在不必要的情况下重新构建。例如,下面这段代码完全有效:函数的使用非常好,因为它返回一个
const StatelessWidget
。如果我说错了,请纠正我。eh57zj3b3#
1 -大多数时候构建方法(子小部件)调用同步和异步函数的数量。
例如:
因此构建方法需要保留在单独的类小部件中(因为通过build()方法调用的所有其他方法可以保留在一个类中)
2 -使用widget类,您可以创建许多其他类,而无需反复编写相同的代码(Use Of Inheritance(extends))。
并且还使用继承(扩展)和多态性(覆盖)您可以创建自己的自定义类。(下面的例子,在那里我将通过扩展MaterialPageRoute自定义(覆盖)动画(因为它的默认过渡我想自定义)。👇
3 -函数不能为其参数添加条件,但使用类小部件的构造函数可以做到这一点。
代码示例下方👇(框架小部件大量使用此功能)
4 -函数不能使用const,而类小部件可以使用const作为它们的构造函数。(这会影响主线程的性能)
5 -您可以使用同一个类(类/对象的示例)创建任意数量的独立小部件,但函数不能创建独立的小部件(示例),但重用可以。
[each示例有自己的 * 示例变量 *,完全独立于其他小部件(对象),但 * 函数的局部变量 * 依赖于每个函数调用 *(这意味着,当您更改局部变量的值时,它会影响使用此函数的应用程序的所有其他部分)]
类相对于函数有很多优点......(以上只是一些用例)
我最后的想法
因此,不要把函数作为应用程序的构建块,只把它们用于操作。否则,当你的应用程序变得可伸缩时,它会导致许多不可改变的问题。
6jjcrrmo4#
正如Remi反复雄辩地指出的那样,问题并不是函数本身造成的,问题是我们认为使用函数与使用新的小部件有类似的好处。
不幸的是,这个建议正在演变成“仅仅使用函数的行为是低效的”,而对于为什么会这样,人们往往会做出不正确的猜测。
使用一个函数几乎等同于使用函数返回的值来代替该函数。因此,如果调用一个小部件构造函数并将其作为另一个小部件的子构造函数,那么将该构造函数调用移到函数中并不会使代码效率低下。
在效率方面并不比
对第二个问题进行如下论证是可以的:
AnimatedSwitcher
等。Scaffold
ChangeNotifier
,则其重新生成不包含在函数中但这样说是不对的:
创建新小部件可带来以下性能优势:
ChangeNotifier
不会根据更改重新生成其父项const
(如果可能)创建它可以防止父级的重建const
构造函数然而,如果你没有这些情况,并且你的构建函数看起来越来越像pyramid of doom,那么最好将它的一部分重构为一个函数,而不是保留金字塔。你可能会发现自己在写代码时要占用大约20个字符的空间。我看到很多新手都落入了这个陷阱。给这些新手的信息应该是“你真的应该在这里创建新的小部件。但是如果你不能,至少创建一个函数。",而不是“你必须创建一个小部件或其他!"。这就是为什么我认为我们必须更具体地在我们促进小部件的功能,并避免在效率方面的事实错误。
为了方便起见,我对Remi's code进行了重构,以说明问题不在于简单地使用函数,而在于避免创建新的小部件。因此,如果您要将这些函数中的小部件创建代码放在调用这些函数的位置(重构内联)你有着和使用函数完全一样的行为,但是没有使用函数!所以,问题不在于使用函数,它避免了创建新的小部件类。
(记得关闭null safety,因为原始代码是2018年的)
下面是Dartpad上的几个交互式示例,您可以自己运行这些示例来更好地理解问题:
https://dartpad.dev/1870e726d7e04699bc8f9d78ba71da35此示例展示了如何通过将应用拆分为函数来意外中断AnimatedSwitcher等功能
非功能版本:https://dartpad.dev/?id=ae5686f3f760e7a37b682039f546a784
https://dartpad.dev/a869b21a2ebd2466b876a5997c9cf3f1这个例子展示了类如何允许更细粒度地重建小部件树,从而提高性能
非功能版本:https://dartpad.dev/?id=795f286791110e3abc1900e4dcd9150b
https://dartpad.dev/06842ae9e4b82fad917acb88da108eee这个例子展示了如何通过使用函数,使自己暴露在滥用BuildContext和在使用InheritedWidget(例如主题或提供者)时面临bug的情况下
非功能版本:https://dartpad.dev/?id=65f753b633f68503262d5adc22ea27c0
你会发现在函数中没有小部件也会产生同样的行为,所以是添加小部件给你带来了好处,而不是添加函数带来了问题。
因此,建议应该是:
setState
等)。然后提取你的部件,并找到添加这些东西的方法。将函数传递给构造函数可能是可以的(想想onPressed)。使用状态管理系统可能更好。我希望这能帮助提醒我们为什么我们更喜欢小部件而不是函数,以及简单地使用函数并不是一个大问题。
**编辑:**在整个讨论中遗漏了一点:当你widgetize的时候,兄弟不再互相重建。这个Dartpad演示了这一点:https://dartpad.dartlang.org/?id=8d9b6d5bd53a23b441c117cd95524892
czfnxgou5#
调用Flutter小部件时,请确保使用const关键字。
ioekq8ef6#
如果这有助于任何人通过这种方式,我在我的Flutter概念模型中的一些东西是从这个问题发展而来的,并与Flutter一般(警告:我仍然可能对这些东西感到深深的困惑和错误)。
一个
Widget
是你想要的,而Element
是你拥有的,渲染引擎的工作就是尽可能有效地协调这两者。使用
Key
s,他们可以帮助很大。构建上下文是一个元素。
任何
Thing.of(context)
都可能引入构建依赖项。如果Thing
发生更改,则将触发从context
元素进行重建。在
build()
中,如果您从嵌套小部件访问BuildContext
,则是在操作子树顶部的Element
。AnimatedSwitcher
是一个狡猾的野兽--它必须能够区分它的子函数。如果函数返回不同的类型,或者返回相同的类型但Key
不同,则可以使用函数如果你正在编写一个
Widget
,使用class
而不是Function
,但是你可以随意地用函数/方法重构你的1000行build()
方法,结果是相同的 *。1hdlvixo7#
功能:
通过编写一个接受一些参数并返回一个小部件的函数,函数可以用来创建可重用的小部件,这允许您快速创建具有不同属性的小部件。
示例:
课程:
类用于创建可重用的小部件,方法是创建一个扩展基本小部件(如StatelessWidget或StatefulWidget)的类。这允许您创建具有不同属性的小部件,还可以添加自定义行为。
示例:
差异:
函数是快速创建小部件的简单方法,而类允许您创建具有自定义行为的更复杂的小部件。此外,类具有更强的可扩展性,允许您创建修改小部件行为的子类。