在一个正常的应用程序中, 表单是用户交互的很重要的一部分

flutter 中你可以自己”绑定”用户的输入数据和 state 中的字段

当然, 还有另一个选择, Flutter 内置了 Form 组件给我们使用, 这个组件是 flutter 框架提供出来帮助我们操作表单的一个组件, 应该是官方较为推荐的方案

不过 Form 的相关中文文章不太多, 基本都是介绍一下 TextFormField 的使用, 更多的 api 云里雾里的, 可能有些朋友不太好理解
我粗略的解析下源码和自定义, 帮助看过的朋友理解下 Form 体系

Form 体系的简单使用

自动校验

官方提供给了我们一些组件来结合 Form 使用, 最常见的就是 TextFormField

import 'package:flutter/material.dart';

class SimpleUseComponent extends StatefulWidget {
  @override
  _SimpleUseComponentState createState() => _SimpleUseComponentState();
}

class _SimpleUseComponentState extends State<SimpleUseComponent> {
  @override
  Widget build(BuildContext context) {
    return Form(
      onChanged: () {
        print("form change");
      },
      child: Column(
        children: <Widget>[
          TextFormField(
            initialValue: "你好",
            validator: (value) {
              if (value.isEmpty) return "不能为空";
              return null;
            },
            autovalidate: true,
          ),
        ],
      ),
    );
  }
}

当我输入/删除时, 会出现日志: ‘form change’

20190722153013.png 有一个autovalidate的属性, 这个属性可以在TextFormField设置,也可以在Form设置, 自动提交验证的意思, 默认是 false.
当我将所有字符都删除时, 会出现’不能为空’的提示. 这个是由 validator 实现的, 因为我设置了autovalidatetrue, 然后这个返回值如果是 null, 则说明验证通过, 不通过则返回不通过的字符串.

点击后校验

当然,有些表单不要求自动校验, 可能是点击某个按钮后来校验, 这种情况下, 就需要使用 Form.of 来获取到表单状态

话不多说, 代码在下面:


class SimpleUseClickComponent extends StatefulWidget {
  @override
  _SimpleUseClickComponentState createState() =>
      _SimpleUseClickComponentState();
}

class _SimpleUseClickComponentState extends State<SimpleUseClickComponent> {
  @override
  Widget build(BuildContext context) {
    return Form(
      onChanged: () {
        print("form change");
      },
      child: Column(
        children: <Widget>[
          TextFormField(
            validator: (value) {
              if (value.isEmpty) return "不能为空";
              return null;
            },
          ),
          Builder( // 这个 Builder 是
            builder: (ctx) => RaisedButton(
              child: Text('check'),
              onPressed: () {
                final formState = Form.of(ctx);
                formState.validate(); // 验证
                // formState.reset(); // 重置
                // formState.save(); // 保存
              },
            ),
          ),
        ],
      ),
    );
  }
}

validate 会触发 FormStatevalidation 回调

reset 会触发 FormFieldStatereset 回调, 默认实现是将输入框设置为初始值

save 会触发 FormFieldStatesave 回调, 可以在里面写一些逻辑(比如保存信息到数据库等等)

Form 体系源码

架构查看

Form 组件是 widgets 包内的的一个 Widget, 不是 MaterialCupertino 包内的, 它没有用户界面, 更多的是逻辑层面的一个组件

20190722152219.png

20190722152305.png

Form 体系中有 5 个类, 3 个 typedef 组成

FormScope 是一个InheritedWidget, 一般带of方法的组件都会对应一个这个东西, 是获取 FormFormState 的容器类

FormState 就是 Form 对应的 state

FormFieldForm下的子组件的通用类, 每个实现/继承了这个类的子类就可以被Form管理, 比如 TextFieldForm 就是这么一个组件

20190722161208.png

FormFieldStateFormField 对应的状态, 其中包含了一些状态信息

源码解析

通过回调等手段实现了内部的交互, 详细看截图

Form 解析

2019-07-22 at 17.02.png

FormState 解析

2019-07-22 at 17.06.png

FormField 解析

2019-07-22 at 17.10.png

FormFieldState 解析

2019-07-22 at 16.56.png

自定义 Form 子组件

有的时候表单中并不只有文本输入, 还会有 Checkbox 等组件, 我这里举两个例子来自定义一个 Form 子组件

自定义 CheckboxFormField

import 'package:flutter/material.dart';

class CheckboxFormField extends FormField<bool> {
  CheckboxFormField({
    bool initValue,
  }) : super(
          builder: CheckboxFormField.buildWidget,
          initialValue: initValue,
        );

  static Widget buildWidget(FormFieldState<bool> field) {
    return Checkbox(
      onChanged: (bool value) {
        field.didChange(value);
      },
      value: field.value,
    );
  }
}

一个简单的自定义 CheckboxFormField 就完成了

置入 Form

import 'package:flutter/material.dart';

import 'custom_field.dart';

class FormComponent extends StatefulWidget {
  @override
  _FormComponentState createState() => _FormComponentState();
}

class _FormComponentState extends State<FormComponent> {
  @override
  Widget build(BuildContext context) {
    return Form(
      onChanged: () {
        print("on change");
      },
      child: ListView(
        children: <Widget>[
          CheckboxFormField(),
        ],
      ),
    );
  }
}

当我点击时, 就会回调 on change

自定义 SliderFormField

class SliderFormField extends FormField<double> {
  SliderFormField()
      : super(
          builder: SliderFormField.buildWidget,
          initialValue: 50,
        );

  static Widget buildWidget(FormFieldState<double> field) {
    return Slider(
      onChanged: (double value) {
        field.didChange(value);
      },
      value: field.value,
      min: 0,
      max: 100,
      divisions: 100,
    );
  }
}

置入 Form 中

import 'package:flutter/material.dart';

import 'custom_field.dart';

class FormComponent extends StatefulWidget {
  @override
  _FormComponentState createState() => _FormComponentState();
}

class _FormComponentState extends State<FormComponent> {
  GlobalKey<FormState> formKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Form(
      key: formKey,
      onChanged: () {
        print("on change");
      },
      child: ListView(
        children: <Widget>[
          CheckboxFormField(),
          SliderFormField(),
          RaisedButton(
            onPressed: () {
              formKey.currentState.reset();
            },
            child: Text("reset"),
          ),
        ],
      ),
    );
  }
}

加入了一个Key用于获取FormState, 触发reset方法

截图:

Kapture 2019-07-22 at 17.25.30.gif

后记

本篇解析了一下 Form 的使用和源码, 帮助朋友们更好的理解使用

项目仓库

以上