深入讲解Flutter应用模板源码:计数器示例

栏目: IOS · Android · 发布时间: 6年前

内容简介:用Android Studio和VS Code创建的Flutter应用,源码模板是一个计数器示例,通过讲解分析计数器示例的源码,可以让读者对Flutter应用程序结构有个最基本的了解,在后续的文章中,笔者将会基于此示例,一步一步添加新的功能来介绍Flutter应用的其他概念与技术。(1)、Stateful widget可以拥有状态,这些状态在Widget生命周期中是可以变的,而Stateless widget是不可变的。(2)、Stateful widget至少由两个类组成:

用Android Studio和VS Code创建的Flutter应用,源码模板是一个计数器示例,通过讲解分析计数器示例的源码,可以让读者对Flutter应用程序结构有个最基本的了解,在后续的文章中,笔者将会基于此示例,一步一步添加新的功能来介绍Flutter应用的其他概念与技术。

示例源码

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  
  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text(
              'You have pushed the button this many times:',
            ),
            new Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: new Icon(Icons.add),
      ),
    );
  }
}
复制代码

运行效果

深入讲解Flutter应用模板源码:计数器示例

源码分析

1、导包

import 'package:flutter/material.dart';
复制代码
  • 此行代码作用是导入了Material UI组件库。Material是一种标准的移动端和web端的视觉设计语言,Flutter默认提供了一套丰富的Material风格的UI组件。

2、主函数(应用入口)

void main() => runApp(MyApp());
复制代码
  • 与C/C++、 Java 类似,Flutter应用中 main 函数为应用程序的入口, main 函数中调用了 runApp 方法,它的功能是启动Flutter应用,它接受一个 Widget 参数,在本示例中它是 MyApp 类的一个实例,该参数代表Flutter应用。
  • main 函数使用了( => )符号,这是Dart中单行函数或方法的简写。

3、应用结构

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      //应用名称  
      title: 'Flutter Demo', 
      //应用主题
      theme: new ThemeData(
        //蓝色主题  
        primarySwatch: Colors.blue,
      ),
      //应用首页路由  
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}
复制代码
  • MyApp 类代表Flutter应用,它继承了 StatelessWidget 类,这意味着应用本身也是一个Widget。在Flutter中,一切皆为组件(Widget),包括对齐(alignment)、填充(padding)和布局(layout)。
  • Flutter在构建页面时,会调用组件的 build 方法,Widget的主要工作是提供一个 build 方法来描述如何构建UI界面(通常都是通过组合、拼装其他基础Widget)。
  • MaterialApp 是Material库中提供的Flutter APP框架,通过它可以设置应用的名称、主题、语言和首页及路由列表等。 MaterialApp 也是一个Widget。
  • home 为Flutter应用的首页,它也是一个widget。

4、首页

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
 ...
}
复制代码
  • MyHomePage 是应用的首页,它继承 StatefulWidget 类,表示它是一个有状态的Widget(Stateful widget),那么Stateful widget和Stateless widget有什么区别呢?

(1)、Stateful widget可以拥有状态,这些状态在Widget生命周期中是可以变的,而Stateless widget是不可变的。

(2)、Stateful widget至少由两个类组成:

①、一个 StatefulWidget 类;

②、一个 State 类, StatefulWidget 类本身是不变的,但是 State 类中持有的状态在Widget生命周期中可能会发生变化。

  • _MyHomePageState 类是 MyHomePage 类对应的状态类。看到这里,细心的读者可能已经发现,和 MyApp 类不同的是, MyHomePage 类中并没有 build 方法,取而代之的是, build 方法被挪到了 _MyHomePageState 方法中,至于为什么这么做,先留个疑问,在分析完完整代码后再来解答,接下来,我们看看_MyHomePageState中都包含哪些东西:
    • 状态

      int _counter = 0;
      复制代码

      _count 为保存屏幕右下角带“+”号按钮点击次数的状态。

    • 设置状态的自增函数

      void _incrementCounter() {
          setState(() {
              _counter++;
          });
      }
      复制代码

      当按钮点击时,会调用此函数,该函数的作用是先自增 _counter ,然后调用 setState 方法。 setState 方法的作用是通知Flutter框架,有状态发生了改变,Flutter框架收到通知后,会执行 build 方法来根据新的状态重新构建界面,Flutter对此方法做了优化,使重新执行变的很快,所以读者可以重新构建任何需要更新的东西,而无需分别去修改各个Widget。

    • 构建UI界面

      构建UI界面的逻辑在 build 方法中,当 MyHomePage 第一次创建时, _MyHomePageState 类会被创建,当初始化完成后,Flutter框架会调用Widget的 build 方法来构建Widget树,最终将Widget树渲染到设备屏幕上。所以,我们看看 _MyHomePageStatebuild 方法中都干了什么事:

      Widget build(BuildContext context) {
          return new Scaffold(
            appBar: new AppBar(
              title: new Text(widget.title),
            ),
            body: new Center(
              child: new Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  new Text(
                    'You have pushed the button this many times:',
                  ),
                  new Text(
                    '$_counter',
                    style: Theme.of(context).textTheme.display1,
                  ),
                ],
              ),
            ),
            floatingActionButton: new FloatingActionButton(
              onPressed: _incrementCounter,
              tooltip: 'Increment',
              child: new Icon(Icons.add),
            ),
          );
        }
      复制代码
    • Scaffold 是Material库中提供的一个Widget,它提供了默认的导航栏、标题和包含主屏幕widget树的body属性。路由默认都是通过 Scaffold 创建。

    • body 的Widget树中包含了一个 Center Widget, Center 可以将其子Widget树对齐到屏幕中心, Center 子Widget是一个 Column Widget, Column 的作用是将其所有子Widget沿屏幕垂直方向依次排列,此例中 Column 包含两个 Text 子Widget,第一个 Text Widget显示固定文本“You have pushed the button this many times:”,第二个 Text Widget显示 _counter 状态的数值。

    • floatingActionButton是页面右下角的带“+”的悬浮按钮,它的 onPressed 属性接受一个回调函数,代表它被点击后的处理器,本例中直接将 _incrementCounter 作为其处理函数。

5、总结

现在,我们将整个流程串起来:当右下角的floatingActionButton按钮被点击之后,会调用 _incrementCounter 函数,在 _incrementCounter 函数中,首先会自增 _counter 状态(计数器),然后 setState 会通知Flutter框架状态发生变化,接着,Flutter会调用 build 方法以新的状态重新构建UI,最终显示在设备屏幕上。

疑问解答

为什么要将build方法放在State中,而不是放在StatefulWidget中?

现在,我们回答之前提出的问题,为什么 build 方法在State中,而不是StatefulWidget中?这主要是为了开发的灵活性。如果将 build 方法在StatefulWidget中则会有两个问题:

  • 状态访问不便

    试想一下,如果我们的Stateful widget有很多状态,而每次状态改变都要调用 build 方法。由于状态是保存在State中的,如果将 build 方法放在StatefulWidget中,那么构建时读取状态将会很不方便,因为构建用户界面过程需要依赖State,所以 build 方法必须加一个State参数,如下所示:

    Widget build(BuildContext context, State state) {
        //state.counter
        ...
    }
    复制代码

    这样的话,只能将State的所有状态声明为公开的状态,这样才能在State类外部访问状态,但将状态设置为公开后,状态将不再具有私密性,这样的依赖对状态的修改将会变的不可控。而将 build 方法放在State中的话,构建过程则可以直接访问状态,故而方便。

  • 继承StatefulWidget不便

    例如,Flutter中有一个动画Widget的基类 AnimatedWidget ,它继承自 StatefulWidget 类。 AnimatedWidget 中引入了一个抽象方法 build(BuildContext context) ,继承自 AnimatedWidget 的动画Widget都要实现这个 build 方法。试想一下,如果StatefulWidget类中已经有了一个 build 方法,正如上述所述,此时 build 方法需要接收一个State对象,这就意味着 AnimatedWidget 必须将自己的State对象(记为 _animatedWidgetState )提供给其子类,因为子类需要在其 build 方法中调用父类的 build 方法,代码如下:

    class MyAnimationWidget extends AnimatedWidget{
        @override
        Widget build(BuildContext context, State state){
            //由于子类要用到AnimatedWidget的状态对象_animatedWidgetState,
            //所以AnimatedWidget必须通过某种方式将其状态对象_animatedWidgetState暴露给其子类
            super.build(context, _animatedWidgetState)
        }
    }
    复制代码

    这样很不合理:

    • AnimatedWidget 的状态对象是 AnimatedWidget 内部实现细节,不应该暴露给外部。
    • 如果要将父类状态暴露给子类,那么必须得有一种传递机制,而做这一套传递机制是无意义的,因为父子类之间状态的传递和子类本身逻辑是无关的。

    综上所述,对于 StatefulWidget ,将 build 方法放在 State 中,可以给开发带来很大的灵活性。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Sovereign Individual

The Sovereign Individual

James Dale Davidson、William Rees-Mogg / Free Press / 1999-08-26 / USD 16.00

Two renowned investment advisors and authors of the bestseller The Great Reckoning bring to light both currents of disaster and the potential for prosperity and renewal in the face of radical changes ......一起来看看 《The Sovereign Individual》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换