组件

flutter 将一切都视为组件,你看到的丰富多彩的的页面本质上都是由一个个小组件组合而成,如 Text、Image、Icon 等,可以把组件理解为跟网页开发中的 HTML 标签 其概念是一样的。

基础组件

flutter内置许多基础小组件,比如上文中出现的最简单的 Text组件,他们都位于 flutter/widgets.dart 包中,这里可以查看所有基础小组件的 列表和相关用法!

布局组件

除了图片、文本组件,在flutter中,还有一些单纯为布局而生的布局组件 类似于html中的div、span,如 Align、Padding、Center 等。它们也是内置基础组件的一种!

输入组件

对应html表单级别的组件,比如RichText、TextField、Checkbox等等。
除了基础组件包提供了一些输入组件,Material包中还提供了许多高级输入组件,比如 TextFormField、CheckboxListTile 等。

手势组件

GestureDetector 是 flutter 内置的功能非常强大基础组件,支持多种手势识别(如点击、拖动、缩放等):

import 'package:flutter/widgets.dart';

// 应用入口程序
void main() {
  runApp(const MyAppWidget());
}

class MyAppWidget extends StatelessWidget {
  const MyAppWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Center(  // 将 Center 放在最外层来居中整个 GestureDetector
      child: GestureDetector(
        onTap: () {
          print('哈哈,点我了');
        },
        child: const Text(
          '哈哈',
          textDirection: TextDirection.ltr,
        ),
      ),
    );
  }
}

高级主题组件 Material 里 也提供了一个处理手势且样式比较好看的组件 InkWell和GestureDetector,点击时显示水波纹效果。

有状态组件

在实际开发中,某些Widget情况下我们展示的数据并不是一层不变的: 比如Flutter默认程序中的计数器案例,点击了+号按钮后,显示的数字需要+1; 再比如在开发中,我们会进行下拉刷新、上拉加载更多,这时数据也会发生变化;

所以 flutter 将组件分为有状态(继承StatelessWidget)和无状态(继承StatefulWidget)两种(和react的无状态组件类似),上边的例子就是无状态组件,我们来写一个有状态的:

import 'package:flutter/widgets.dart';

// 应用入口程序
void main() {
  runApp(const MyAppWidget());
}

class MyAppWidget extends StatefulWidget {
  const MyAppWidget({super.key});

  // 它是一个用于创建与该 widget 关联的 State 对象 的 工厂函数(固定写法w)!
  @override
  MyAppWidgetState createState() => MyAppWidgetState();
}

class MyAppWidgetState extends State<MyAppWidget> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Center(  // 将 Center 放在最外层来居中整个 GestureDetector
      child: GestureDetector(
        onTap: () {
          setState(() {
            count++;
          });
        },
        child: Text(
          '点我后边会自增: $count',
          textDirection: TextDirection.ltr,
        ),
      ),
    );
  }
}

组件和状态分离

编写有状态组件的时候,你会观察到 我们将 (Stateful)Widget 和 State分离成了两个 class,实际上这是 flutter故意这么设计的:

  • 性能:因为在Flutter中,只要数据改变了Widget就需要重新构建(rebuild),避免到 widget 不会被重复构建才这么做!
  • 分工:State 是 Widget 内部状态 类,负责管理组件的状态和界面渲染,而 Widget 是个 外部可传递参数的组件(有点像hook)!

更新状态

注意的是 设置状态必须用 setState 才能触发 flutter更新视图,这个早期的react也一样!

传递参数

外部参数

最简单的示例如下,我们自定义了 红色文本显示组件,然后传递它文本即可

import 'package:flutter/widgets.dart';

// 主方法(入口)
void main() {
  runApp(const MyAppWidget());
}

// 这是你应用程序的根部件(widget)。
class MyAppWidget extends StatelessWidget {
  const MyAppWidget({super.key});
  @override
  Widget build(BuildContext context) {
    return const RedTextWidget(str: '你好'); // 通过构造,传递参数
  }
}

// 这是自定义的RedTextWidget组件
class RedTextWidget extends StatelessWidget {
  final String str; // 通过定义实例的属性(相当于prop)
  const RedTextWidget({super.key, required this.str});
  @override
  Widget build(BuildContext context) {
    return Text(str, 
        style: const TextStyle(color: Color(0xFFFF0000)), 
        textDirection: TextDirection.ltr 
        );
  }
}

内部参数

如上说述,在有状态组件中,状态和组件是分离两个类,那状态类如何获取组件类定义的属性呢?

通过 widget.xx 获取!

import 'package:flutter/widgets.dart';

// 应用入口程序
void main() {
  runApp(const MyAppWidget());
}

class MyAppWidget extends StatefulWidget {
  final String str = '哈哈';
  const MyAppWidget({super.key});
  @override
  MyAppWidgetState createState() => MyAppWidgetState();
}

class MyAppWidgetState extends State<MyAppWidget> {
  @override
  Widget build(BuildContext context) {
    return Text(
      '来自Widget的str属性: ${widget.str}',
      textDirection: TextDirection.ltr,
    );
  }
}

快速构建App类型组件

你可能会觉得自己使用布局组件来进行繁琐的布局太麻烦!
没关系,flutter 提供了帮助你快速构建App的组件,它们提供了应用的全局配置,比如主题、路由、导航等,定义应用的样式和行为,并提供了一些生命周期函数!

  • WidgetsApp组件,不依赖任何设计风格, 提供最基础的 App 类型组件,适合完全自定义的应用,提供最小功能集,比如导航、路由配置等(在flutter/widgets.dart中)。
  • 适合合安卓风格的 MaterialApp,并包含WidgetsApp所有能力(在flutter/material.dart中)。
  • 适合Ios的CupertinoApp ,并包含WidgetsApp所有能力(在flutter/cupertino.dart中)。

这3个组件都, 都可以满足你 帮你快速搭建出 干净或不同主题的 app 😊!

提示

后两者都基于前者做了扩展,但是一般情况下,不会直接使用WidgetsApp,而是使用MaterialApp或者CupertinoApp。

最佳实践:MaterialApp 是大多数 Flutter 应用的默认选择,很多 Flutter 开发者都是从 MaterialApp 开始的,这也是为什么官方推荐使用 MaterialApp

提示

需注意 WidgetsApp中不允许使用 Material/CupertinoApp 的组件, 这跟原生html中不允许使用vue或react组件是一个道理。