我已经意识到,可以使用普通函数而不是子类StatelessWidget来创建窗口小部件。例如:
Widget function({ String title, VoidCallback callback }) {
return GestureDetector(
onTap: callback,
child: // some widget
);
}
这很有趣,因为与成熟的类相比,它所需的代码少了 far 。示例:
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
);
}
}
所以我一直在想:除了创建小部件的函数和类的语法以外,还有什么区别吗?使用函数是一种好习惯吗?
答案 0 :(得分:72)
TL; DR:永远不要在类上使用函数来制作可重用的小部件树。始终将它们提取到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
这非常重要,因为它从根本上改变了更新小部件时框架的行为。以下是精选的差异列表:
课程:
功能:
结论应该已经很清楚了:
请勿使用功能创建窗口小部件。
答案 1 :(得分:5)
什么函数和什么类做什么有很大的区别。
让我从头开始解释它。?(仅关于命令)
编程历史,我们都知道从简单的基本命令(例如-:Assembly)开始。
下一步Flow控件附带结构化编程(例如,-:if,switch,while,for等) 这种范例使程序员可以有效地控制程序流,并且可以最大程度地减少循环的代码行数。
下一步是过程编程,它将指令分为过程(函数)。 这给程序员带来了两个主要好处。
1。将语句(操作)分组到单独的块中。
2。可以重复使用这些块。(功能)
但是最重要的是,没有给出了管理应用程序的解决方案。 过程编程也只能用于小规模的应用程序。 不能用于开发大型Web应用程序(例如,银行,谷歌,YouTube,Facebook,stackoverflow等),不能创建android sdk,flutter sdk等框架……
因此,工程师需要做更多的研究来以正确的方式管理程序。
最终,面向对象编程随附了用于管理各种规模应用程序的所有解决方案。(从问候世界到使用系统创建的数万亿人,例如Google,amazon,如今90%应用程序。)
在所有应用程序中,所有应用程序都是围绕对象构建的。这意味着应用程序是这些对象的集合。
所以对象是任何应用程序的基础。
类(运行时对象)分组数据和与那些变量(数据)相关的功能。 因此对象由数据及其相关操作组成。
[这里我不打算解释oop]
???好,现在就来看看flutter框架。???
-Dart同时支持过程和oop但是,Flutter框架完全通过使用classes(oop)构建。 (因为大型可管理框架无法使用过程创建)
在这里,我将列出使用类而不是用于制作小部件的函数的原因的列表。???
1-大多数情况下,构建方法(子窗口小部件)调用同步和异步功能的数量。
例如:
因此,构建方法需要保留在单独的类窗口小部件中(因为build()方法调用的所有其他方法都可以保留在一个类中)
2-使用窗口小部件类,您可以创建另一个类的编号,而不必一次又一次地编写相同的代码(** Use of Inheritance **(扩展))。
还可以使用继承(扩展)和多态性(覆盖)来创建自己的自定义类。 (在下面的示例中,我将通过扩展MaterialPageRoute来自定义(覆盖)动画(因为我要自定义其默认过渡)。?
class MyCustomRoute<T> extends MaterialPageRoute<T> {
MyCustomRoute({ WidgetBuilder builder, RouteSettings settings })
: super(builder: builder, settings: settings);
@override //Customize transition
Widget buildTransitions(BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
if (settings.isInitialRoute)
return child;
// Fades between routes. (If you don't want any animation,
// just return child.)
return new FadeTransition(opacity: animation, child: child);
}
}
3-函数无法为其参数添加条件,但是可以使用类窗口小部件的构造函数。
下面的代码示例?(框架小部件大量使用此功能)
const Scaffold({
Key key,
this.bottomNavigationBar,
this.bottomSheet,
this.backgroundColor,
this.resizeToAvoidBottomPadding,
this.resizeToAvoidBottomInset,
this.primary = true,
this.drawerDragStartBehavior = DragStartBehavior.start,
this.extendBody = false,
this.extendBodyBehindAppBar = false,
this.drawerScrimColor,
this.drawerEdgeDragWidth,
}) : assert(primary != null),
assert(extendBody != null),
assert(extendBodyBehindAppBar != null),
assert(drawerDragStartBehavior != null),
super(key: key);
4-函数不能使用const,而Class小部件可以将const用于其构造函数。 (影响主线程的性能)
5-您可以使用相同的类(类/对象的实例)创建任意数量的独立小部件 但是函数不能创建独立的小部件(实例),但是可以重用。
[每个实例都有自己的 instance变量,并且完全独立于其他小部件(对象),但是 function的局部变量取决于每个函数调用*(这意味着,当您更改局部变量的值时,它将影响使用该功能的应用程序的所有其他部分)]
与函数相比,类有很多优点。(仅少数用例)
?我的最终想法
因此,请勿将函数用作应用程序的构建块,而仅将它们用于进行操作。 否则,当您的应用程序变得可扩展时,它将导致许多难以解决的问题。
感谢阅读
答案 2 :(得分:1)
过去2天,我一直在研究此问题。我得出以下结论:可以将应用程序的各个部分分解为功能。理想的情况是这些函数返回一个StatelessWidget
,因此可以进行优化,例如制作StatelessWidget
const
,这样就不必重建它。
例如,这段代码是完全有效的:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
++_counter;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
const MyWidgetClass(key: const Key('const')),
MyWidgetClass(key: Key('non-const')),
_buildSomeWidgets(_counter),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
Widget _buildSomeWidgets(int val) {
print('${DateTime.now()} Rebuild _buildSomeWidgets');
return const MyWidgetClass(key: Key('function'));
// This is bad, because it would rebuild this every time
// return Container(
// child: Text("hi"),
// );
}
}
class MyWidgetClass extends StatelessWidget {
const MyWidgetClass({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
print('${DateTime.now()} Rebuild MyWidgetClass $key');
return Container(
child: Text("hi"),
);
}
}
使用函数非常好,因为它返回一个const StatelessWidget
。如果我错了,请纠正我。
答案 3 :(得分:0)
由函数返回的小部件在每次重新构建小部件树时都会重新构建,无论它们是否包含状态。
但是,只有状态无状态或有状态的窗口小部件包含的状态发生变化时,才会在该窗口小部件树中重建(仅它们)。
建议将小部件提取到各自的类中,以提高应用程序的性能。最小化重建了多少个小部件...
答案 4 :(得分:-1)
在调用Flutter小部件时,请确保使用const关键字。例如const MyListWidget();