创建窗口小部件的函数和类有什么区别?

时间:2018-11-10 00:03:57

标签: dart flutter

我已经意识到,可以使用普通函数而不是子类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
      );
  }
}

所以我一直在想:除了创建小部件的函数和类的语法以外,还有什么区别吗?使用函数是一种好习惯吗?

5 个答案:

答案 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. 课程:

    • 允许性能优化(const构造函数,operator ==覆盖,更精细的重建)
    • 具有热重载
    • 已集成到小部件检查器(debugFillProperties)
    • 可以定义键
    • 可以使用上下文API
    • 确保所有小部件都以相同的方式使用(始终是构造函数)
    • 确保在两个不同的布局之间切换可以正确处理资源(功能可能会重用某些先前的状态)
  2. 功能:

    • 具有更少的代码(甚至在那里,我也制作了代码生成器以使类与函数一样小:functional_widget

结论应该已经很清楚了:

请勿使用功能创建窗口小部件

答案 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();