一架梯子,一头程序猿,仰望星空!

Flutter Widget


Flutter 从 React 中吸取灵感,通过现代化框架创建出精美的组件。它的核心思想是用 widget 来构建你的 UI 界面。Widget 描述了在当前的配置和状态下,视图所应该呈现的样子。当 widget 的状态改变时,它会重新构建其描述(展示的 UI),框架则会对比前后变化的不同,以确定底层渲染树从一个状态转换到下一个状态所需的最小更改。

提示:在flutter中所有的UI元素都是Widget,Widget类似html中的div标签,android的view, ios中的UIView。

1. Hello world

创建一个最小的 Flutter 应用简单到仅需调用 runApp() 方法并传入一个 widget 即可:

import 'package:flutter/material.dart';

void main() {
  runApp(
    Center(
      child: Text(
        'Hello, world!',
        textDirection: TextDirection.ltr,
      ),
    ),
  );
}

runApp() 函数会持有传入的 Widget,并且使它成为 widget 树中的根节点。在这个例子中,Widget 树有两个 widgets,Center widget 及其子 widget ——Text 。框架会强制让根 widget 铺满整个屏幕,也就是说“Hello World”会在屏幕上居中显示。在这个例子我们需要指定文字的方向,当使用 MaterialApp widget 时,你需要考虑这一点,之后我们会进一步的描述。

2. 基础widget

flutter自带了很多基础widget, 例如:

  • Text: Text widget 可以用来在应用内创建带样式的文本。
  • Row, Column:这两个 flex widgets 可以让你在水平(Row)和垂直(Column)方向创建灵活的布局。它是基于 web 的 flexbox 布局模型设计的。
  • Stack:Stack widget 不是线性(水平或垂直)定位的,而是按照绘制顺序将 widget 堆叠在一起。你可以用 Positioned widget 作为 Stack 的子 widget,以相对于 Stack 的上,右,下,左来定位它们。Stack 是基于 Web 中的绝对位置布局模型设计的。
  • Container: Container widget 可以用来创建一个可见的矩形元素。Container 可以使用 BoxDecoration 来进行装饰,如背景,边框,或阴影等。Container 还可以设置外边距、内边距和尺寸的约束条件等。另外,Container 可以使用矩阵在三维空间进行转换。

除了基础的widget, Material 组件提供了更多的Material风格的UI组件,这后续的教程会有详细介绍。

做过app开发的人都知道,我们一般一个复杂的UI界面,都是由各种基础组件(widget)组合起来构成一个复杂UI界面。我们也可以将一个复杂页面自定义为一个新的widget组件,提供给其他地方使用。

在写应用的过程中,取决于是否需要管理状态,通常会创建一个新的组件继承 StatelessWidget 或 StatefulWidget。Widget 的主要工作是实现 build 方法,该方法根据其它较低级别的 widget 来描述这个 widget。框架会逐一构建这些 widget,直到最底层的描述 widget 几何形状的 RenderObject。

2. 声明式渲染

学习过Vue,react,微信小程序之类的web前端框架,对声明式渲染都不陌生,简单来说就是我们先定义一个ui组件的状态,然后框架根据这些定义好的状态,渲染UI界面。

Flutter 应用是 声明式 的,这也就意味着 Flutter 构建的用户界面就是应用的当前状态, 这点跟android和ios原生开发不大一样。

2.1. 命令式渲染

android/ios就是命令式渲染,这种风格就是,渲染UI页面,都是通过函数调用的方式设置各种组件的属性实现。

例如

// 设置ui控件的文本, 这个时候界面上对应组件的文本就改变了
t.setText("Hello world");

2.2. 声明式渲染

这种风格,我们先定义好组件有什么属性,然后根据这些属性渲染界面,如果改变属性,然后重新渲染UI界面就可以。

例子:

import 'package:flutter/material.dart';

void main() => runApp(TextDemo());

// 继承StatefulWidget,定义一个有状态组件 
class TextDemo extends StatefulWidget {
  // 重写createState函数,返回我们定义的widget组件
  _TextDemo createState() => _TextDemo();
}

// 自定义widget组件
class _TextDemo extends State<TextDemo> {
  // 定义属性
  String title = "Hello world";

  // build决定我们如何渲染ui
  @override
  Widget build(BuildContext context) {
    // 返回定义的widget布局,这里是在一行中放两个widget, 一个展示文本,一个展示按钮
    return Row(children: <Widget>[
      Text(title),  // Text组件根据title属性,展示文本
      // 按钮widget
      FlatButton(child: const Text("点击改变Text"), onPressed: () { //  点击事件
        // setState告诉框架属性变化了,重新调用build函数渲染界面
        setState(() {
          // 改变属性
          this.title = "标题已经改变了!";
        });
      },)
    ],);
  }
}

3. 无状态Widget

无状态widget指的是,组件的状态是静态的,就是组件的属性初始化之后,就不会改变属性的值。

定义无状态Widget,通过继承StatelessWidget,重载build函数,实现我们自定义的UI布局 (通常是通过各种基础widget组件,组合出复杂的UI布局)。

例子:

import 'package:flutter/material.dart';

void main() => runApp(TextDemo());

// 定义一个简单无状态组件
class TextDemo extends StatelessWidget  {
  // 定义静态属性, 其实就是定义常量,这些属性不会改变
  final String title = "Hello world";
  
  @override
  Widget build(BuildContext context) {
    // 根据title属性,展示一个简单的Text组件,当然也可以组合复杂的组件。
    return Text(title);
  }
}

4. 有状态Widget

有状态widget,通常指的是一些拥有状态数据,而且状态数据会改变的widget组件。

有状态widget主要涉及两个类:

  • StatefulWidget - 负责创建State
  • State - 负责自定义UI视图逻辑部分,也可以包含组件私有状态数据

定义有状态widget,需要分两步;

  • 继承StatefulWidget,自定义有状态组件。
  • 通过自定义有状态组件的createState函数,创建State。

有状态widget的例子,请参考 2.2. 声明式渲染 中的例子。