Flutter学习-基础Widget

  • 1. Flutter编程范式
    • 1.1 编程范式的理解
    • 1.2 flutter的编程范式
  • 2. Text Widget
    • 2.1 普通文本展示
    • 2.2 富文本
    • 2.3 扩展小知识点
    • 2.4 android studio 开发flutter一些快捷键
  • 3 Button Widget
    • 3.1 基础Button
    • 3.2 Button的间距问题
    • 3.3 Button的大小修改和内边距的修改
  • 4 Image Widget
    • 4.1 加载网络图片
    • 4.2 加载本地图片
    • 4.3 实现图片的圆角
      • 4.3.1 CircleAvatar
      • 4.3.2 ClipOval
      • 4.3.3 ClipRRect
    • 4.4 FadeImage(可以设置占位图)
    • 4.5 图片的缓存问题
    • 4.6 Icon
  • 5 TextFiled
    • 5.1 TextFile的基础使用
    • TextField的controller
  • 6 Form表单
    • 6.1 Form表单的基本使用
    • 6.2 保存和获取表单数据
    • 6.3 验证填写的表单数据

1. Flutter编程范式

这个章节又讲解一些理论的东西,可能并不会直接讲授Flutter的知识,但是会对你以后写任何的代码,都具备一些简单的知道思想;
建议大家遇到一些问题的时候,可以先查看文档和源码,因为很多东西文档中都有说明

1.1 编程范式的理解

编程范式对于初学编程的人来说是一个虚无缥缈的东西,但是却是我们日常开发中都在默认遵循的一些模式和方法;

比如我们最为熟悉的 面向对象编程就是一种编程范式,与之对应或者结合开发的包括:面向过程编程、函数式编程、面向协议编程;

另外还有两个对应的编程范式:命令式编程 和 声明式编程

  • 命令式编程: 命令式编程非常好理解,就是一步步给计算机命令,告诉它我们想做什么事情;
  • 声明式编程: 声明式编程通常是描述目标的性质,你应该是什么样的,依赖哪些状态,并且当依赖的状态发生改变时,我们通过某些方式通知目标作出相应

1.2 flutter的编程范式

从2009年开始(数据来自维基百科),声明式编程就开始流行起来,并且目前在Vue、React、包括iOS中的SwiftUI中以及Flutter目前都采用了声明式编程。

现在我们来开发一个需求:显示一个Hello World,之后又修改成了Hello Flutter

如果是传统的命令式编程,我们开发Flutter的模式很可能是这样的:(注意是想象中的伪代码)

  • 整个过程,我们需要一步步告诉Flutter它需要做什么;
final text = new Text();
var title = "Hello World";
text.setContent(title);// 修改数据
title = "Hello Flutter";
text.setContent(title);

如果是声明式编程,我们通常会维护一套数据集:

  • 这个数据集可能来自己父类、来自自身State管理、来自InheritedWidget、来自统一的状态管理的地方;
  • 总之,我们知道有这么一个数据集,并且告诉Flutter这些数据集在哪里使用;
var title = "Hello World";Text(title); // 告诉Text内部显示的是title// 数据改变
title = "Hello Flutter";
setState(() => null); // 通知重新build Widget即可

上面的代码过于简单,可能不能体现出Flutter声明式编程的优势所在,但是在以后的开发中,我们都是按照这种模式在进行开始,我们一起来慢慢体会;

2. Text Widget

在Android中,我们使用TextView,iOS中我们使用UILabel来显示文本;
Flutter中,我们使用Text组件控制文本如何展示;

2.1 普通文本展示

在Flutter中,我们可以将文本的控制显示分成两类

  • 控制文本布局的参数: 如文本对齐方式 textAlign、文本排版方向 textDirection,文本显示最大行数 maxLines、文本截断规则 overflow 等等,这些都是构造函数中的参数
    文本默认情况下就是包裹内容的大小,也就是说Text()组件的大小取决于其内容的大小

  • 控制文本样式的参数: 如字体名称 fontFamily、字体大小 fontSize、文本颜色 color、文本阴影 shadows 等等,这些参数被统一封装到了构造函数中的参数 style 中

  • 示例属性的使用

class GYHomeContent extends StatelessWidget {@overrideWidget build(BuildContext context) {// TODO: implement buildreturn Text("《定风波》 苏轼 莫听穿林打叶声,何妨吟啸且徐行。竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生。",textAlign: TextAlign.left,//设置文本左对齐maxLines: 2,//设置最大行数2行overflow: TextOverflow.ellipsis,//设置超出部分...style: TextStyle(fontSize: 30,//设置字体color: Colors.orange//设置字体颜色),);}
}

上述代码只是展示了一些常用的属性,更多的属性用法我们需要去看源码, 还有哪些属性,大家可以多尝试下。

  • 注意:Text组件是不可以直接设置一个宽度的,而是包裹内容的

2.2 富文本

代码示例:

class GYHomeContent extends StatelessWidget {@overrideWidget build(BuildContext context) {return Text.rich(TextSpan(children: [TextSpan(text: "《定风波》", style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold, color: Colors.black)),TextSpan(text: "苏轼", style: TextStyle(fontSize: 18, color: Colors.redAccent)),TextSpan(text: "\n莫听穿林打叶声,何妨吟啸且徐行。\n竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生。")],),style: TextStyle(fontSize: 20, color: Colors.purple),textAlign: TextAlign.center,);}
}

效果图:

2.3 扩展小知识点

上述我们一直在写的Text()组件,其实不是flutter最终渲染的Widegt。

void main() => runApp(MyApp())class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(home: Scaffold(appBar: AppBar(title: Text("基础widget"),),body: GYHomeContent()));}
}

上述代码中main函数选择的MyApp对象吗? 其实不是,实际渲染的MyApp中build中返回的widget,所以我们我们要想知道使用Text小组件,flutter最终渲染的widget是什么,我们查看Text组件的build方法

上述源码中我们可以看出我们最终渲染的是RichText的一个组件,RichText是可以直接使用的

2.4 android studio 开发flutter一些快捷键

  • Option + command + b : 查看抽象类的实现类
  • Option + command + w :将build出的widget抽取到一个单独的widget中
  • Option + enter: 将Statelesswidget转换成Statefulwidget
  • stl :代表StatelessWidget
  • stf :StatefulWidget
  • Option + Enter :在widget包裹一个新的widget。
  • Option + Up :选择这个widget。
  • Option + Command + M :方法抽离或重构。
  • Command + Shift + Enter :代码快速补全。
  • Option + Command + V : 抽离局部变量。
  • Option + Command + F :抽离成员变量。
  • Command + Option + L : 代码格式化。
  • Command + F12 :快速查看当前文件所有方法。
  • Command + shift + F :全局搜索。
  • Command + shift + R :全局替换。
  • Command + F :当前文件搜索。
  • Command + R :当前文件替换。

3 Button Widget

3.1 基础Button

Material widget库中提供了多种按钮Widget如FloatingActionButton、RaisedButton、FlatButton、OutlineButton等

  • 案例示例:
class GYHomeContent extends StatelessWidget {@overrideWidget build(BuildContext context) {return Column(children: [FloatingActionButton(onPressed: (){print("FloatingActionButton click");}, child: Text("FloatingActionButton")),/*** @Deprecated('Use ElevatedButton instead. See the migration guide in flutter.dev/go/material-button-migration-guide). ''This feature was deprecated after v1.26.0-18.0.pre.',)该按钮在v1.26.0-18.0.pre版本后被弃用了, 使用ElevatedButton 来替代RaisedButton*/RaisedButton(onPressed: (){print("RaisedButton click");}, child: Text("RaisedButton")),ElevatedButton(onPressed: (){print("ElevatedButton click");}, child: Text("ElevatedButton")),/*** @Deprecated('Use TextButton instead. See the migration guide in flutter.dev/go/material-button-migration-guide). ''This feature was deprecated after v1.26.0-18.0.pre.',该按钮在v1.26.0-18.0.pre版本后被弃用了, 使用TextButton 来替代 FlatButton*/FlatButton(onPressed: (){print("FlatButton click");}, child: Text("FlatButton")),TextButton(onPressed: (){print("TextButton click");}, child: Text("TextButton")),/*** @Deprecated('Use OutlinedButton instead. See the migration guide in flutter.dev/go/material-button-migration-guide). ''This feature was deprecated after v1.26.0-18.0.pre.',)该按钮在v1.26.0-18.0.pre版本后被弃用了, 使用OutlinedButton 来替代 OutlineButton*/OutlineButton(onPressed: (){print("OutlineButton click");}, child: Text("OutlineButton")),OutlinedButton(onPressed: (){print("OutlinedButton click");}, child: Text("OutlinedButton"))],);}
}

如果想要自己定制一些按钮, 我们完全可以自己通过按钮中的一些属性来控制,达到自己想要的按钮样式,大家可以自行演示下

运行结果:

3.2 Button的间距问题

默认情况下Button上下会有一定的间距

  • 通过上面运行的结果我们可以发现,RaisedButtonElevateButton上面和下面好像都有些许的间距,主要是button有一个属性,MaterialTapTargetSize这个属性,这是一个枚举,默认值是padded,看源码描述是,如果按钮的大小不足48px大小,会默认扩展到48px的大小。如果我们想要按钮今包裹内容,那么我们可以设置MaterialTapTargetSize这个属性的值为shrinkWrap,这样按钮的大小就会包裹内容

3.3 Button的大小修改和内边距的修改

  • 当我们不给Button设置任何内容的时候,我们可以发现Button依旧会有一个默认的大小

那么我们如果让Button的大小变小点了?这里以FlatButton为例

我们通过文档和源码,我们都可以看到,FlatButton最终是继承自MaterialButton,然后继承自StatelessWidget,我们知道,如果widget是继承自StatelessWidget的,那么flutter最终渲染的是build返回的组件

文档描述:

通过文档我们可以看到,最终渲染的ButtonTheme有一个默认大小,然后在结合源码,我们可以看到ThemeButton有两个属性,minwidthminheight表示最小宽度和最小高度的

由源码可知我们的ThemeButton是通过上下文来获取,所以我们可以FlatButton的外层包裹一个ThemeButton然后设置minWidth、minHeight,然后通过这个两个属性我们就可以调整任意大小的Button

虽然这里我们设置了最小宽度,但是如果我们的Button是有内容的,这个Button还是可以包裹内容的

  • 但是我们发现Button还是有一点内边距,我们可以发现Button有一个属性padding,通过修改这个属性我们可以达到修改Button内边距的问题,这个属性和上面讲述的ButtonTheme的最小宽和最小高的属性可能会冲突

4 Image Widget

图片可以让我们的应用更加丰富多彩,Flutter中使用Image组件

Image组件有很多的构造函数,我们这里主要学习两个:

  • Image.assets:加载本地资源图片;
  • Image.network:加载网络中的图片

4.1 加载网络图片

相对来讲,Flutter中加载网络图片会更加简单,直接传入URL并不需要什么配置,所以我们先来看一下Flutter中如何加载网络图片

我们先来看看Image有哪些属性可以设置

const Image({required this.image, //必传参数 图片...this.width, //图片的宽this.height, //图片的高this.color, // 图片的混合色值this.colorBlendMode, //混合模式this.fit, //缩放模式this.alignment = Alignment.center, //对齐方式this.repeat = ImageRepeat.noRepeat, //重复方式...})
  • width、height:用于设置图片的宽、高,当不指定宽高时,图片会根据当前父容器的限制,尽可能的显示其原始大小,如果只设置width、height的其中一个,那么另一个属性默认会按比例缩放,但可以通过下面介绍的fit属性来指定适应规则。
  • fit:该属性用于在图片的显示空间和图片本身大小不同时指定图片的适应模式。适应模式是在BoxFit中定义,它是一个枚举类型,有如下值:
    • fill:会拉伸填充满显示空间,图片本身长宽比会发生变化,图片会变形。
    • cover:会按图片的长宽比放大后居中填满显示空间,图片不会变形,超出显示空间部分会被剪裁。
    • contain:这是图片的默认适应规则,图片会在保证图片本身长宽比不变的情况下缩放以适应当前显示空间,图片不会变形。
    • fitWidth:图片的宽度会缩放到显示空间的宽度,高度会按比例缩放,然后居中显示,图片不会变形,超出显示空间部分会被剪裁。
    • fitHeight:图片的高度会缩放到显示空间的高度,宽度会按比例缩放,然后居中显示,图片不会变形,超出显示空间部分会被剪裁。
    • none:图片没有适应策略,会在显示空间内显示图片,如果图片比显示空间大,则显示空间只会显示图片中间部分。
  • colorcolorBlendMode:在图片绘制时可以对每一个像素进行颜色混合处理,color指定混合色,而colorBlendMode指定混合模式;
  • repeat:当图片本身大小小于显示空间时,指定图片的重复规则。

示例代码

class GYHomeContent extends StatelessWidget {@overrideWidget build(BuildContext context) {return Center(child: Container(child: Image.network("http://img0.dili360.com/ga/M01/48/3C/wKgBy1kj49qAMVd7ADKmuZ9jug8377.tub.jpg",alignment: Alignment.topCenter,//顶部居中repeat: ImageRepeat.repeatY, //Y轴上重复color: Colors.red,colorBlendMode: BlendMode.colorBurn,),width: 300,height: 300,),);}
}

4.2 加载本地图片

加载本地图片这里比较麻烦一点,需要配置。

  • 首先需要把图片导入工程,例如在images文件加下放入一张图片
  • 然后再pubspec.yaml文件中配置图片资源
  • 代码中根据图片路径来加载图片
class GYHomeContent extends StatelessWidget {@overrideWidget build(BuildContext context) {return Center(child: Container(child: Image.asset("images/test.jpeg"),width: 300,height: 300,),);}
}

4.3 实现图片的圆角

4.3.1 CircleAvatar

CircleAvatar可以实现圆角头像,也可以添加一个子Widget:

const CircleAvatar({Key? key,this.child, //子widgetthis.backgroundColor, // 背景颜色this.backgroundImage, // 背景图像this.foregroundImage, // 前景颜色...this.foregroundColor,this.radius, //半径this.minRadius,//最小半径this.maxRadius,//最大半径})

我们需要实现一个圆形头像

class GYHomeContent extends StatelessWidget {@overrideWidget build(BuildContext context) {return CircleAvatar(child: Text("圆角图片", style: TextStyle(color: Colors.orange)),backgroundImage: NetworkImage("https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg"),radius: 80,);}
}
  • CircleAvatar:本身就是一个圆角的widget,这里backgroundImage,属性需要我们传入一个ImageProvider?类型的小组件, 但是我们发现该组件是一个抽象类,然后我们可以查看该抽象类的实现类,所以这里我们传入的是NetworkImage小组件

4.3.2 ClipOval

ClipOval也可以实现圆角头像,而且通常是在只有头像时使用

class GYHomeContent extends StatelessWidget {@overrideWidget build(BuildContext context) {return ClipOval(child: Image.network("https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg",width: 200,height: 200,),);}
}//如果图片是不规则的, 我们可以调整图片的填充方式,来达到圆角充满的效果,但是这里的图片可能会根据你设置的不同模式的填充方式,有拉伸效果
ClipOval(child: Image.asset("images/test.jpeg",width: 200,height: 200,fit: BoxFit.fill,),);

4.3.3 ClipRRect

ClipRRect用于实现圆角效果,可以设置圆角的大小。

class GYHomeContent extends StatelessWidget {@overrideWidget build(BuildContext context) {return Center(child: ClipRRect(borderRadius: BorderRadius.circular(20),//随意调整圆角大小child: Image.network("https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg",width: 200,height: 200,),),);}
}

4.4 FadeImage(可以设置占位图)

我们可以使用FadeImage组件来加载网络图片,然后设置占位图,该组件本身自带淡出效果,我们可以通过设置两个属性来达到控制动画效果的时间

@overrideWidget build(BuildContext context) {// 1.占位图的问题: FadeInImagereturn FadeInImage(fadeOutDuration: Duration(milliseconds: 1),//占位图消失时间fadeInDuration: Duration(milliseconds: 1),//网络图片展示时间placeholder: AssetImage("assets/images/juren.jpeg"),//占位图image: NetworkImage(imageURL),//真正显示的网络图片);}

4.5 图片的缓存问题

首先在flutter开发中,我们可能多个地方使用同一张网络图片, 我们可能想这个图片的缓存怎么处理(如果不错缓存每次都加载同一张图片是非常消耗性能的)?

  • 在flutter中我们并不要自己给图片来做缓存,flutter默认情况下是会给我们的图片做缓存的,fliutter在加载图片的时候,如果发现图片的地址和图片的缩放,就会直接加载原来的那张图片,所以flutter对图片默认情况下是有内存缓存的,默认最多缓存1000张图片,最大只能缓存100M的空间

根据文档我们发现可以去调整 缓存的大小(maximumSize),但是我们一般不去调整

4.6 Icon

通过源码我们看出Icon需要我们传入一个IconData的widget,其实flutter已经帮我们创建好了很多这种IconData,直接使用Icons这个widget就可以了

  • Icon图标其实是一种字体图标,那么Icon图标和我们的图片图标的区别在哪里?

    • 字体图标是矢量图,放大的时候不会失真
    • 字体图标,本质上也是一个字体,我们也可以设置颜色的
    • 字体图标很多时,相对来说占据的空间会更小
@overrideWidget build(BuildContext context) {//    return Icon(Icons.pets, size: 300, color: Colors.orange,);
//    return Icon(IconData(0xe91d, fontFamily: 'MaterialIcons'), size: 300, color: Colors.orange,);return Text("\ue91d", style: TextStyle(fontSize: 100, color: Colors.orange, fontFamily: "MaterialIcons"),);}
  • 由于Icon本身是一个字体,那么我们是不是可以使用Text来加载,如果使用Text来加载必须满意一下条件:

    • 0xe91d -> unicode编码(需要把这个16进制的标记转换成unicode编码)
    • 2.设置对应的字体(需要告诉Text,你使用的是哪个字体)

5 TextFiled

5.1 TextFile的基础使用

TextField用于接收用户的文本输入,它提供了非常多的属性,我们来看一下源码:

  • 但是我们没必要一个个去学习,很多时候用到某个功能时去查看是否包含某个属性即可
const TextField({Key key,this.controller,this.focusNode,this.decoration = const InputDecoration(),TextInputType keyboardType,this.textInputAction,this.textCapitalization = TextCapitalization.none,this.style,this.strutStyle,this.textAlign = TextAlign.start,this.textAlignVertical,this.textDirection,this.readOnly = false,ToolbarOptions toolbarOptions,this.showCursor,this.autofocus = false,this.obscureText = false,this.autocorrect = true,this.maxLines = 1,this.minLines,this.expands = false,this.maxLength,this.maxLengthEnforced = true,this.onChanged,this.onEditingComplete,this.onSubmitted,this.inputFormatters,this.enabled,this.cursorWidth = 2.0,this.cursorRadius,this.cursorColor,this.keyboardAppearance,this.scrollPadding = const EdgeInsets.all(20.0),this.dragStartBehavior = DragStartBehavior.start,this.enableInteractiveSelection = true,this.onTap,this.buildCounter,this.scrollController,this.scrollPhysics,
})

我们来学习几个常见的属性把

  • 一些属性比较简单:keyboardType键盘的类型,style设置样式,textAlign文本对齐方式,maxLength最大显示行数等等;
  • decoration:用于设置输入框相关的样式
    • icon:设置左边显示的图标
    • labelText:在输入框上面显示一个提示的文本
    • hintText:显示提示的占位文字
    • border:输入框的边框,默认底部有一个边框,可以通过InputBorder.none删除掉
    • filled:是否填充输入框,默认为false
    • fillColor:输入框填充的颜色
    • suffixIcon: 输入框右侧(尾部)显示widget
    • prefixIcon:输入框左侧(前面)显示widget
  • controller:关联的控制器
  • onChanged:监听输入框内容的改变,传入一个回调函数
  • onSubmitted:点击键盘中右下角的down时,会回调的一个函数
import 'package:flutter/material.dart';class TextFieldDemo extends StatelessWidget {final usernameTextEditController = TextEditingController();final passwordTextEditController = TextEditingController();@overrideWidget build(BuildContext context) {return Theme(data: ThemeData(primaryColor: Colors.red //可以达到控制输入框边框的颜色),child: Padding(padding: const EdgeInsets.all(8.0),child: Column(children: <Widget>[TextField(controller: usernameTextEditController,decoration: InputDecoration(labelText: "username",icon: Icon(Icons.people),hintText: "请输入用户名",border: InputBorder.none,//去掉边框filled: true,fillColor: Colors.red[100]),onChanged: (value) {print("onChange:$value");},onSubmitted: (value) {print("onSubmitted:$value");},),SizedBox(height: 10,),TextField(controller: passwordTextEditController,decoration: InputDecoration(labelText: "password",icon: Icon(Icons.lock),border: OutlineInputBorder(),),),SizedBox(height: 10,),Container(width: double.infinity,height: 40,child: FlatButton(child: Text("登 录", style: TextStyle(fontSize: 20, color: Colors.white),),color: Colors.blue,onPressed: () {// 1.获取用户名和密码final username = usernameTextEditController.text;final password = passwordTextEditController.text;print("账号:$username 密码:$password");usernameTextEditController.text = "";passwordTextEditController.text = "";},),)],),),);}
}

运行结果:

TextField的controller

  • 我们可以给TextField添加一个控制器(Controller),可以使用它设置文本的初始值,也可以使用它来监听文本的改变;
  • 事实上,如果我们没有为TextField提供一个Controller,那么会Flutter会默认创建一个TextEditingController的,这个结论可以通过阅读源码得到:
  @overridevoid initState() {super.initState();// ...其他代码if (widget.controller == null)_controller = TextEditingController();}

我们也可以自己来创建一个Controller控制一些内容:

class _TextFieldDemoState extends State<TextFieldDemo> {final textEditingController = TextEditingController();@overridevoid initState() {super.initState();// 1.设置默认值textEditingController.text = "Hello World";// 2.监听文本框textEditingController.addListener(() {print("textEditingController:${textEditingController.text}");});}// ...省略build方法
}

6 Form表单

在我们开发注册、登录页面时,通常会有多个表单需要同时获取内容或者进行一些验证,如果对每一个TextField都分别进行验证,是一件比较麻烦的事情。
做过前端的开发知道,我们可以将多个input标签放在一个form里面,Flutter也借鉴了这样的思想:我们可以通过Form对输入框进行分组,统一进行一些操作。

6.1 Form表单的基本使用

Form表单也是一个Widget,可以在里面放入我们的输入框。
但是Form表单中输入框必须是FormField类型的

  • 我们查看刚刚学过的TextField是继承自StatefulWidget,并不是一个FormField类型;
  • 我们可以使用TextFormField,它的使用类似于TextField,并且是继承自FormField的;

我们通过Form的包裹,来实现一个注册的页面:

class FormDemo extends StatefulWidget {@override_FormDemoState createState() => _FormDemoState();
}class _FormDemoState extends State<FormDemo> {@overrideWidget build(BuildContext context) {return Form(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[TextFormField(decoration: InputDecoration(icon: Icon(Icons.people),labelText: "用户名或手机号"),),TextFormField(obscureText: true,decoration: InputDecoration(icon: Icon(Icons.lock),labelText: "密码"),),SizedBox(height: 16,),Container(width: double.infinity,height: 44,child: RaisedButton(color: Colors.lightGreen,child: Text("注 册", style: TextStyle(fontSize: 20, color: Colors.white),),onPressed: () {print("点击了注册按钮");},),)],),);}
}

6.2 保存和获取表单数据

有了表单后,我们需要在点击注册时,可以同时获取和保存表单中的数据,怎么可以做到呢?

  1. 需要监听注册按钮的点击,在之前我们已经监听的onPressed传入的回调中来做即可。(当然,如果嵌套太多,我们待会儿可以将它抽取到一个单独的方法中)
  2. 监听到按钮点击时,同时获取用户名密码的表单信息。
  • 如何同时获取用户名密码的表单信息?
  • 如果我们调用FormState对象的save方法,就会调用Form中放入的TextFormFieldonSave回调:
TextFormField(decoration: InputDecoration(icon: Icon(Icons.people),labelText: "用户名或手机号"),onSaved: (value) {print("用户名:$value");},
),
  • 但是,我们有没有办法可以在点击按钮时,拿到 Form对象 来调用它的save方法呢?

    • 知识点:在Flutter如何可以获取一个通过一个引用获取一个StatefulWidget的State对象呢?
    • 答案:通过绑定一个GlobalKey即可。

案例代码演示:

class FormDemo extends StatefulWidget {@override_FormDemoState createState() => _FormDemoState();
}class _FormDemoState extends State<FormDemo> {final registerFormKey = GlobalKey<FormState>();String username, password;void registerForm() {registerFormKey.currentState.save();print("username:$username password:$password");}@overrideWidget build(BuildContext context) {return Form(key: registerFormKey,child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[TextFormField(decoration: InputDecoration(icon: Icon(Icons.people),labelText: "用户名或手机号"),onSaved: (value) {this.username = value;},),TextFormField(obscureText: true,decoration: InputDecoration(icon: Icon(Icons.lock),labelText: "密码"),onSaved: (value) {this.password = value;},),SizedBox(height: 16,),Container(width: double.infinity,height: 44,child: RaisedButton(color: Colors.lightGreen,child: Text("注 册", style: TextStyle(fontSize: 20, color: Colors.white),),onPressed: registerForm,),)],),);}
}

6.3 验证填写的表单数据

在表单中,我们可以添加验证器,如果不符合某些特定的规则,那么给用户一定的提示信息
比如我们需要账号和密码有这样的规则:账号和密码都不能为空。
按照如下步骤就可以完成整个验证过程:

  1. 为TextFormField添加validator的回调函数;
  2. 调用Form的State对象的validate方法,就会回调validator传入的函数;

也可以为TextFormField添加一个属性:autovalidate

不需要调用validate方法,会自动验证是否符合要求;

Flutter学习-基础Widget相关推荐

  1. Flutter之基础Widget

    原文博客地址: Flutter之基础Widget Flutter和Dart系列文章 项目GitHub地址 Flutter作为一种全新的响应式,跨平台,高性能, 完全免费.开源的移动开发框架 Widge ...

  2. Flutter 学习 - Widget 之 对话框

    前言 本篇我们介绍Flutter中常用的对话框,先看下效果图 正文 Flutter 中对话框也是Widget,有两种显示对话框的方法,对于对画框对使用还有特殊要求,我们后面介绍,先来看下这两种方法 1 ...

  3. iOS程序猿的flutter学习之路

    日常学习Flutter开发的积累 推荐一些平时自己学习Flutter开发当中接触到的优秀文章 -------------------------基础知识 ----------------------- ...

  4. Flutter 学习之打包 - 纯Flutter项目生成Android包

    ###前言 前面介绍了Flutter中一些基本Widget的使用方法,下面我们来先下实战中是如何构建生成Android包的,本文我们主要介绍纯Flutter项目生成Android包的方法,以及遇到的问 ...

  5. Flutter学习之认知基础组件

    一.前言 前一天,学习了Dart语法,对Dart的语法和特性有了更深一步的了解.今天,来学习Flutter的基础控件,身为Android开发者都知道,一开始入坑Android就要熟悉学习其控件,如:T ...

  6. Flutter (四) 基础 Widgets、Material Components Widget 全面介绍

    基础 Widgets 重要概念 一切皆组件.Flutter 所有的元素都是由组件组成.比如一个布局元素.一个动画.一个装饰效果等. 容器 Container 容器组件 Container 包含一个子 ...

  7. Flutter学习-多子布局Widget

    Flutter学习-多子布局Widget 1. Flex 2. Row组件 2.1 Row组件介绍 2.2 属性解析 2.2.1 mainAxisSize 2.2.2 mainAxisAlignmen ...

  8. Flutter学习 Widget简介

    目录 1. Widget 概述 1.1 Widget概念 1.2 Widget 分类 2. Widget 接口 3. StatelessWidget 和 StatefulWidget 3.1 Flut ...

  9. Flutter学习-单子布局Widget

    Flutter学习-单子布局Widget 1. 概述 2. Align 2.1 Align的对齐方式 2.2 Align的区域大小控制 2. Center 3. Padding 4. Containe ...

最新文章

  1. Javascript删除JSON元素
  2. [Hibernate]在VS2010中应用NHibernate 3.2与MySQL
  3. 解决“无法找到运行搜索助理需要的一个文件”
  4. 我的年龄又快被5整除了......
  5. OpenCV检测计算并匹配BRISK和AORB KAZE描述的实例(附完整代码)
  6. python脚本转换成apk_使用Python-For-Android将Python脚本导出到Android可执行文件(.apk)...
  7. java迭代器cas,java提高篇(三十)-Iterator - Java 技术驿站-Java 技术驿站
  8. rpc wmi 服务不可用_在Windows上修复“RPC服务器不可用”的方法
  9. 存储器RAM ROM FLASH介绍
  10. 二元函数的洛必达法则
  11. Delphi7--运算符和表达式
  12. 固件工程师到底是干什么?
  13. 从计算机视觉到人脸识别:一文看懂颜色模型、信号与噪声
  14. 常见随机变量的数学期望和方差
  15. Element的使用
  16. linux tac命令,Linux tac 命令 command not found tac 命令详解 tac 命令未找到 tac 命令安装 - CommandNotFound ⚡️ 坑否...
  17. 查询student表中姓童的学生情况
  18. 为何苹果电脑虚拟机如此受欢迎
  19. 身份认证的动态密码器解决方案
  20. 敷完面膜后要擦水乳吗_敷完面膜还要擦水乳吗?

热门文章

  1. JavaScript 经典代码(1)
  2. 数据装载命令Load
  3. 游泳防水耳机推荐,推荐四款高质量游泳防水耳机
  4. hive数据仓库安装
  5. 高性能的连接管理和数据路由组件,OceanBase 生态工具 ODP 详解
  6. C# PostgreSQL 教程
  7. windows下搭建Zephyr开发环境
  8. 2019未来杯高校AI挑战赛-跨设备说话人识别
  9. ERROR: Could not install packages due to an OSError: [Errno 28] No space left on device的解决方案
  10. 【论文阅读】Object affordance based multimodal fusion for natural Human-Robot interaction (视音信息融合)