Widget概览

1,858 阅读6分钟

欢迎点赞,转载请注明出处

为了避免文章版面过长,影响阅读效果,本节起示例项目源代码提供网上下载链接,本节下载点击这里simple_widget

Widget描述了在当前的配置和状态下视图所应该呈现的样子。当widget的状态改变时,它会重新构建其描述(展示的 UI),框架则会对比前后变化的不同,以确定底层渲染树从一个状态转换到下一个状态所需的最小更改。

基础组件

import 'package:flutter/material.dart';

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

我们重新回到Android Studio,同Hello Flutter章节一样,新建一个Flutter App,将main.dart替换为上述代码段,同时删除掉test/widget_test.dart文件。运行结果如下:

runApp函数的作用是填充一个widget并它关联到设备屏幕上,所以它的入参就是widget,在上例中就是Center这个组件。Center组件的作用是将它的子组件Text在它的内部居中。TextDirection.ltr表示文本方向是从左到右(left to right)的顺序展示,对于一些国家的语言(如阿拉伯语,希伯来语),应该需要设置成rtl,如下图:

如果textDirection: TextDirection.ltr这段代码不写,运行程序则会有以下错误提示:

设备屏幕上也会输出辣眼睛的错误提示堆栈😱:
我们对照检查Text的构造函数定义可以看到:
字符串'Hello!'是赋值给了Text的data实例变量,是必须的入参。而textDirection是命名参数,是可选的,那为什么一定要赋值呢?根据错误堆栈提示,我们可以发现:
也就是在Flutter底层绘制对象时,会检查textDirection这个属性是否为空。如果使用MaterialApp Widget则不需要对textDirection属性显示赋值。 除了居中小部件显示外,我们还有其他显示的方案,如下图所示:

Flutter自带了一套强大的基础Widgets,下面列出了一些常用的:

  • Text Widget可以用来在应用内创建带样式的文本。
  • Row, Column这两个Flex Widgets可以在水平 (Row) 和垂直(Column) 方向创建灵活的布局,它基于Web的Flexbox布局模型设计的。
  • Stack Widget不是线性(水平或垂直)定位的,而是按照绘制顺序将Widget堆叠在一起。可以用Positioned Widget作为Stack的子 Widget,以相对于 Stack 的上,右,下,左来定位它们,它基于Web中的绝对位置布局模型设计的。
  • Container Widget用来创建一个可见的矩形元素。 可以使用BoxDecoration来进行装饰,如背景,边框,或阴影等。 还可以设置外边距、内边距和尺寸的约束条件等。
    我们再看一个官方的示例,完整代码和运行效果如下图:

第66行runApp方法参数MaterialApp构造函数返回StatfulWidget数据类型,StatefulWidget是Widget的派生类。MaterialApp构造函数参数home表示导航路由根部的Widget即MyScaffold,MyScaffold属于StatelessWidget类型,也是Widget的派生类,它使用build方法构建子组件。MyScaffold widget将其子widget组织在垂直列中。在列的顶部,它放置一个MyAppBar实例,并把 Text widget传给它来作为应用的标题。MyScaffold使用Expanded来填充剩余空间,其中包含一个居中的消息。MyAppBar widget 创建了一个高56独立像素,左右内边距8像素的Container。在容器内,MyAppBar以Row布局来组织它的子元素。中间的子 Widget(title widget),被标记为 Expanded,这意味着它会扩展以填充其它子widget未使用的可用空间。这样的描述阅读和理解起来非常吃力,好在Flutter Plugin提供了Flutter Outline的功能,如下图:
是不是Widget的层次结构看的清楚多了?而Flutter Inspector的功能就更强大了,它可以观察运行程序在设备中的实际布局效果,如下图:

Material组件

很容易观察到上图的MyAppBar空间区域跟手机设备的状态栏重叠在一起了,并没有自适应布局。我们将从MyAppBar和MyScaffold 切换到material.dart的AppBar和Scaffold Widgets,可以看到手机设备状态栏布局很好地进行了适配:

Scaffold Widget将几个不同的Widget作为命名参数,每个Widget都会自动放在了Scofford布局中的合适位置。AppBar Widget 构造函数的leading、title、 actions命名参数传递的都是Widget类别。
MaterialApp表示使用Material Design风格的应用程序, 默认情况下,非 Material app不包含AppBar、标题和背景颜色。Material Design(材料设计语言)是由Google推出的设计语言,这种设计语言旨在为手机、平板电脑、台式机和“其他平台”提供更一致、更广泛的“外观和感觉”。 Material Design更关心系统反应的质感、层次、深度和其他物体的叠放逻辑。

Cupertino组件

Cupertino组件是Flutter实现的一组苹果风格的组件。使用时需要先导入:import package:flutter/cupertino.dart
下图为iOS风格的ActionSheet组件效果:

对应的代码如下:

import 'package:flutter/cupertino.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CupertinoApp(
      home: HomeScreen(),
      theme: CupertinoThemeData(primaryColor: Color.fromARGB(255, 0, 0, 255)),
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: CupertinoNavigationBar(
        middle: Text("Cupertino组件"),
      ),
      child: Container(
        child: Center(
          child: CupertinoButton(
            onPressed: () {
              showCupertinoModalPopup(
                  context: context,
                  builder: (context) {
                    return CupertinoActionSheet(
                      title: Text("手机"),
                      message: Text("选一个手机"),
                      actions: <Widget>[
                        CupertinoActionSheetAction(
                          child: Text("苹果手机"),
                          onPressed: () {
                          },
                        ),
                        CupertinoActionSheetAction(
                          child: Text("安卓手机"),
                          onPressed: () {
                          },
                        ),
                      ],
                    );
                  });
            },
            child: Text("点我"),
          ),
        ),
      ),
    );
  }
}

手势

GestureDetector Widget能识别用户的手势,当监测到用户点击事件时,GestureDetector会调用其onTap()回调函数,如下图示例:

还可以使用GestureDetector检测各种输入的手势,包括点击,拖动和缩放。
前面示例中IconButton的onPressed本身也是onTap的同一类型,可以看到2个参数对应的都是GestureTapCallback回调函数类型。

StatefulWidget

无状态Widget(StatelessWidget)接收的参数来自于它的父级Widget,它们储存在final实例变量中。当StatelessWidget需要被build() 时,就是用这些存储的变量为创建的widget生成新的参数。当Widget的用户交互只依赖于配置信息和构建它的上下文环境时,使用无状态Widget是有用的。
StatefulWidget提供更多的用户交互支持。其中Widget是临时对象,用于构造应用当前状态的展示。而 State对象在调用build()之间是持久的,以此来存储信息。下面是一个简单的示例:

填充StatefulWidget时,Flutter Framework会自动调用它的createState方法,而state对象里的setState会再次运行build返回构建的具体Widget对象。

Flutter Plugin的一些图标含义

可以看到Flutter Inspector和Flutter Outline有不同的图标的显示,不同颜色和形状代表不同类型的信息,可以从下图对应的文件名大致可以理解一二,供大家参考。

实验六

设计合理的Widget布局和事件响应,将实验四Dart命令行程序改版为Flutter Material App类程序。
提示:可以使用多个按钮模拟不同字符串的统计结果计算。

上一篇 Dart泛型、库、异步和注释 下一篇 布局构建