Flutter—使用网络请求的页面搭建流程、State生命周期、一些组件的应用

使用网络请求的页面搭建流程

​ 在开发APP时,我们常常会遇到如下场景:进入一个页面后,要先进行网络调用,然后使用调用返回的数据进行页面渲染。

​ 这种页面搭建流程大致为:调用网络请求,获得json格式的数据—解析获得的数据为Dart类 — 将Dart数据传回UI。在返回数据前,可以在页面先放置一个加载动画;获得数据后,使用数据进行进行页面重绘。

网络请求

​ Flutter的网络请求常常使用的库有httpdio,其中dio是一个强大的Dart Http请求库,支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时、自定义适配器等。dio的详细介绍与使用方法可以参见github-dio官方文档

  • 在pubspec.yaml中引入包依赖

    dependencies:
    dio: ^3.x.x // 请使用pub上3.0.0分支的最新版本
  • 极简示例

    import 'package:dio/dio.dart';
    void getHttp() async {
    try {
    Response response = await Dio().get("http://www.baidu.com");
    print(response);
    } catch (e) {
    print(e);
    }
    }

数据解析

​ 当调用Dio.get()方法时,可以从网络获得json数据,我们可以将这些数据进行解析处理,方便UI搭建的使用。 实例如下:

//该函数从调用服务器某一个接口,获得json数据,并将解析后的Dart model返回。
Future<Course> getCourse(String studentID) async {
Dio dio = new Dio();
Response response;
response = await dio.request(
'http://www.baidu.com',//将此替换为后端服务器地址
options: Options(method: "GET", responseType: ResponseType.plain),
);
String ss = response.data;
dynamic jsonList = json.decode(ss);
Course tempStr = new Course.fromJson(jsonList);//将json解析为Dart类,
return tempStr;
}

Course.fromJson的实现有很多种方式,可以手写或者自动生成。在此推荐一个json-Dart 转换的网页json to Dart。该网页可以自动根据给定的json数据生成Dart类。例如对于如下格式的json数据:

{
"course_name": "计算机网络",
"department": "计算机学院"
}

生成的Dart 类为:

class course {
String courseName;
String department; course({this.courseName, this.department}); course.fromJson(Map<String, dynamic> json) {
courseName = json['course_name'];
department = json['department'];
} Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['course_name'] = this.courseName;
data['department'] = this.department;
return data;
}
}

FutureBuilder页面渲染

​ 在实现网络请求的调用和数据解析后,我们需要把获得的数据应用到UI的渲染中。此时就要用到``FutureBuilder`。

FutureBuilder是一个将异步操作和异步UI更新结合在一起的类,通过它我们可以将网络请求,数据库读取等的结果更新的页面上。其构造方法如下:

FutureBuilder({
Key key, Future<T> future, //表示此构建器当前连接的异步计算。
T initialData,
@required
AsyncWidgetBuilder<T> builder //是一个基于异步交互构建widget的函数,这个函数接受两个参数`BuildContext context`与 `AsyncSnapshot<T> snapshot`,并应该返回一个widget。其中异步计算获得的数据就保存在`snapshot.data`中,在builder中可以通过`snapshot.data`获得异步数据。
})

一个使用实例

//网络调用未返回数据时,返回加载动画;返回数据后展示返回数据的'course_name'值。
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('主题'),
),
body: FutureBuilder<CommonModel>(
future: getCourse(),//调用上面数据解析部分封装好的网络请求函数getCourse()为异步计算函数。
builder:
(BuildContext context, AsyncSnapshot<CommonModel> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return new Text('Input a URL to start');
case ConnectionState.waiting:
return new Center(child: new CircularProgressIndicator());
case ConnectionState.active:
return new Text('');
case ConnectionState.done:
if (snapshot.hasError) {
return new Text(
'${snapshot.error}',
style: TextStyle(color: Colors.red),
);
} else {
return Text(${snapshot.data.courese_name}$);
}
}
}),
),
);
}

State生命周期

​ State是构造StatefulWidget必不可少的组件。State的生命周期主要包括三个阶段:创建、更新、销毁。

  • 创建

    State Widget的创建流程为:

    1. 构造方法,接受父组件的数据,并调用createState
    2. initState。在这个函数中可以对State中的状态值进行初始化。
    3. didChangeDependencies 。在initState后被调用,可以用来处理 State 对象依赖关系的变化
    4. build。完成以上的函数后,在build方法中,将根据父 Widget 传递过来的初始化配置数据及 State 的当前状态,创建一个 Widget 然后返回。
  • 更新

    State Widget的状态更新可以通过三种途径:setState、didChangeDependencies 或 didUpdateWidget 触发。

    • setState。当状态值发生改变的时候,可以通过调用setState方法通知flutter对Widget进行重绘(build)。
    • didChangeDependencies。当State依赖的数据发生改变时,Flutter 会回调该方法,随后触发组件构建。
    • didUpdateWidget。Widget 的配置发生变化时,或热重载时,系统会回调该方法。
  • 销毁

    当 State 被永久地从视图树中移除时,Flutter 会调用 dispose 方法,而一旦 dispose 方法被调用,组件就要被销毁了,因此可以在 dispose 方法中进行最终的资源释放、移除监听、清理环境等工作

State生命周期可以用一下图来描述:

一些组件的应用

在出现弹窗时禁用实体返回键

​ 典型场景:在进行网络请求过程中,我们不希望用户在中途进行其他操作。此时,我们可以使用一个过渡动画来提示用户目前正在进行网络调用。

​ 过渡动画的实现方法有很多,最常见的是进行弹窗提醒。所以我们需要维持网络请求过程中UI始终为弹窗页。即要求:

  1. 点击对话框以外的区域不隐藏弹窗——barrierDismissible属性
  2. 点击实体返回键不隐藏弹窗——在原有弹窗组件中外包裹一层WillPopScope,并设置onWillPop属性为false。

举例:

showDialog(
barrierDismissible: false,//barrierDismissible属性,控制点击对话框以外的区域是否隐藏对话框,为flase时不隐藏
context: context
builder: (context) {
return WillPopScope(//在原有的弹窗Wigdet外,嵌套WillPopScope,并设置onWillPop的返回值为false
child: Center(
Text('加载中'),
),
onWillPop: () {
return new Future.value(false);
},
);
});

点击空白处回收键盘

​ 在与键盘有交互的页面,用户常常进行的一个操作是点击空白处回收键盘。实现这一功能的也很简单,只需要在页面的最外层包裹一层GestureDetector,并在onTap时取消键盘焦点。

GestureDetector用于用户交互,进行基本的手势控制,其基本属性如下所示。

GestureDetector({
Key key,
this.child,
this.onTapDown,//可能导致点击的指针已联系到屏幕的特定位置
this.onTapUp,//触发点的指针已停止在特定位置与屏幕联系
this.onTap,//发生了点击。
this.onTapCancel,//触发onTapDown的指针取消触发
this.onDoubleTap,//双击
this.onLongPress,//长按
this.onLongPressUp,//长按结束
this.onVerticalDragDown,//
this.onVerticalDragStart,//指针已经接触到屏幕,而且可能开始垂直移动。
this.onVerticalDragUpdate,//与屏幕接触并垂直移动的指针沿垂直方向移动
this.onVerticalDragEnd,//以前与屏幕接触并垂直移动的指针不再与屏幕接触,并且当其停止接触屏幕时以特定速度移动。
this.onVerticalDragCancel,//
this.onHorizontalDragDown,//
this.onHorizontalDragStart,//
this.onHorizontalDragUpdate,//
this.onHorizontalDragEnd,//
this.onHorizontalDragCancel,//
// onPan可以取代onVerticalDrag或者onHorizontalDrag,三者不能并存
this.onPanDown,//指针已经接触屏幕并开始移动
this.onPanStart,//与屏幕接触并移动的指针再次移动
this.onPanUpdate,//先前与屏幕接触并移动的指针不再与屏幕接触,并且当它停止接触屏幕时以特定速度移动
this.onPanEnd,//先前触发 onPanDown 的指针未完成
this.onPanCancel,//
// onScale可以取代onVerticalDrag或者onHorizontalDrag,三者不能并存,不能与onPan并存
this.onScaleStart,//
this.onScaleUpdate,//
this.onScaleEnd,//
this.behavior,
this.excludeFromSemantics = false
})

实例:

class _CourseCommentWritePage extends State<CourseCommentWritePage> {
TextEditingController commentController = new TextEditingController();
FocusNode commentNode = new FocusNode(); Widget build(BuildContext context) {
print("build_write");
return Scaffold(
appBar: AppBar(
title: Text("标题"),
),
body: GestureDetector(
onTap: () {
commentNode.unfocus();//取消键盘焦点
},
child:Container(
decoration: BoxDecoration(
border: new Border.all(width: 2.0, color: Colors.black12),
borderRadius: new BorderRadius.all(new Radius.circular(10.0)),
),
child: TextField(
enabled: _enable,
maxLines: 12,
focusNode: commentNode,
controller: commentController,
decoration: InputDecoration(
hintText: '请输入你对课程的评价',
border: InputBorder.none,
),
),
),
)
}

在弹出键盘时,防止页面溢出

在我们实际的项目开发中,经常会遇到页面UI内容过多,导致手机一屏展示不完的情况出现,在Flutter中我们通常使用SingleChildScrollView处理滑动,即使用SingleChildScrollView组件将普通组件包裹。

同样,可以使用SingleChildScrollView中将表单TextFiled包裹,以防止溢出键盘弹出导致的溢出。

SingleChildScrollView组件:

const SingleChildScrollView({
Key key,//滚动方向,默认是垂直方向
this.scrollDirection = Axis.vertical,//是否按照阅读方向相反的方向滑动
this.reverse = false,//内容边距
this.padding,//是否使用widget树中默认的PrimaryScrollController
bool primary,//此属性接受一个ScrollPhysics类型的对象,它决定可以滚动如何响应用户操作,比如用户滑动完抬起手指后,继续执行动画,或者滑动到边界时,如何显示。
this.controller,
this.child,
})

在特定条件下禁用RaiseButton

​ RaiseButton是一个按钮组件,其一些属性作用如下:

const RaisedButton({
Key key,
@required VoidCallback onPressed,//被点击时的回调函数
ValueChanged<bool> onHighlightChanged,//水波纹高亮变化回调,按下返回true,抬起返回false
ButtonTextTheme textTheme,//按钮的主题
Color textColor,//文字的颜色
Color disabledTextColor,//按钮禁用时候文字的颜色
Color color,//按钮的背景颜色
Color disabledColor,//按钮被禁用的时候显示的颜色
Color highlightColor,//点击或者toch控件高亮的时候显示在控件上面,水波纹下面的颜色
Color splashColor,//水波纹的颜色
Brightness colorBrightness,//按钮主题高亮
double elevation,//按钮下面的阴影
double highlightElevation,//高亮时候的阴影
double disabledElevation,//按下的时候的阴影
EdgeInsetsGeometry padding,
ShapeBorder shape,//设置形状
Clip clipBehavior = Clip.none,
MaterialTapTargetSize materialTapTargetSize,
Duration animationDuration,
Widget child,
})

disabledColor字段用于设置按钮被禁用时显示的颜色。但是可以发现其实并没有将按钮设为禁用的字段。查找资料发现,当onpress()的返回值为null时,按钮自动被设为禁用。

例如:

class MyPage extennds StatelessWidget(){
bool _enable = true;
Widget build(BuildContext context){
return RaisedButton(
child: Text("修改"),
color: Colors.lightBlue,
disabledColor: Colors.grey,
onPressed:
_enable ? () => setTextEnable() :null,//当_enable为false时,禁用按钮
),
}
void setTextEnable() {
setState(() {
_enable = !_enable;
});
}
} //值得注意的是,不能将onpress的回调函数写为以下形式,写成这样并不能在当_enable为false时,禁用按钮
onpress:() {
if(_enable){
setTextEnable()
}else{
return null;
}
}

【技术博客】Flutter—使用网络请求的页面搭建流程、State生命周期、一些组件的应用的更多相关文章

  1. 搭建自己的技术博客系列(一)使用 hexo 搭建一个精美的静态博客

    1.安装 Git 和 nodejs https://hexo.io/zh-cn/docs/

  2. 技术人如何利用 github+Jekyll ,搭建一个独立免费的技术博客

    上次有人留言说,技术博客是程序员的标配,但据我所知绝大部分技术同学到现在仍然没有自己的技术博客.原因有很多,有的是懒的写,有的是怕写不好,还有的是一直想憋个大招,幻想做到完美再发出来,结果一直胎死腹中 ...

  3. 【新版】Android技术博客精华汇总

    [新版]Android技术博客精华汇总(原文链接内持续更新) http://www.apkbus.com/thread-313856-1-1.html Kotlin Kotlin学习资料汇总 http ...

  4. 一文搞定scrapy爬取众多知名技术博客文章保存到本地数据库,包含:cnblog、csdn、51cto、itpub、jobbole、oschina等

    本文旨在通过爬取一系列博客网站技术文章的实践,介绍一下scrapy这个python语言中强大的整站爬虫框架的使用.各位童鞋可不要用来干坏事哦,这些技术博客平台也是为了让我们大家更方便的交流.学习.提高 ...

  5. [技术博客] BeautifulSoup4分析网页

    [技术博客] BeautifulSoup4分析网页 使用BeautifulSoup4进行网页文本分析 前言 进行网络爬虫时我们需要从网页源代码中提取自己所需要的信息,分析整理后存入数据库中. 在pyt ...

  6. 如何写出高质量的技术博客 这边文章出自http://www.jianshu.com/p/ae9ab21a5730 觉得不错直接拿过来了 好东西要大家分享嘛

        如何写出高质量的技术博客?答案是:如果你想,就一定能写出高质量的技术博客.看起来很唯心,但这就是事实.有足够愿力去做一件目标明确,有良好反馈系统的事情往往很简单.就是不停地训练,慢慢地,你自己 ...

  7. 【转】【技术博客】Spark性能优化指南——高级篇

    http://mp.weixin.qq.com/s?__biz=MjM5NjQ5MTI5OA==&mid=2651745207&idx=1&sn=3d70d59cede236e ...

  8. [转]有哪些值得关注的技术博客(Java篇)

    有哪些值得关注的技术博客(Java篇)   大部分程序员在自学的道路上不知道走了多少坑,这个视频那个网站搞得自己晕头转向.对我个人来说我平常在学习的过程中喜欢看一些教程式的博客.这些博客的特点: 1. ...

  9. 最值得收藏的java技术博客(Java篇)

    第一个:java_my_life 作者介绍:找不到原作者信息.大概做了翻阅全部是2012年的博客. 博客主要内容:主要内容是关于Java设计模式的一些讲解和学习笔记,在相信对学习设计模式的同学帮助很大 ...

随机推荐

  1. Redis的读写分离

    1.概述 随着企业业务的不断扩大,请求的并发量不断增长,Redis可能终会出现无法负载的情况,此时我们就需要想办法去提升Redis的负载能力. 读写分离(主从复制)是一个比较简单的扩展方案,使用多台机 ...

  2. SQL-UPDATE触发器练习

    &练习一 如下所示三张表( student,grade,student_updata_before ): student表 grade表 Student_update_before表 # 触发 ...

  3. 常量&&变量

    一.常量:程序运行期间不变 常量使用: 二.八大基本数据类型变量: 其中,一个Byte类型数据占八个bit位,所以范围是(-2^7 ~ 2^7-1),数据最小的存储单位,是四分之一的int类型空间,所 ...

  4. JVM-深入

    目录 Java类的加载机制 什么是类的加载 类的生命周期 加载 连接 类加载器 类的加载 双亲委派模型 自定义类加载器 JVM内存结构 Java堆(Heap) 方法区(Method Area) 程序计 ...

  5. DevExpress Silverlight DXChart特效总结

    1.  主题修改 引用  xmlns:core=http://schemas.devexpress.com/winfx/2008/xaml/core 在Grid中添加core:ThemeManager ...

  6. 为什么要设置GOROOT/GOPATH

    设置GOROOT的原因 编译器的位置指定的时候,需要指定GO开发包的安装位置,然后设置环境变量PATH的时候,需要指定到安装包下的bin目录,其中就有以下的编译/执行器.所以GOROOT指定了前面的路 ...

  7. 论文解读(BYOL)《Bootstrap Your Own Latent A New Approach to Self-Supervised Learning》

    论文标题:Bootstrap Your Own Latent A New Approach to Self-Supervised Learning 论文方向:图像领域 论文来源:NIPS2020 论文 ...

  8. Django学习day07随堂笔记

    今日考题 """ 今日考题 1.必知必会N条都有哪些,每个都是干啥使的 2.简述神奇的双下划线查询都有哪些方法,作用是什么 3.针对多对多外键字段的增删改查方法有哪些,各 ...

  9. linux命令(用户)

    一.常用命令 1.1 ls ls 命令是 linux 下最常用的命令,ls 命令就是 list 的缩写. ls 用来打印出当前目录的清单.如果 ls 指定其他目录,那么就会显示指定目录里的文件及文件夹 ...

  10. appium+python自动化:获取元素属性get_attribute

    使用get_attribute()获取元素属性,括号里应该填写什么? 查看appium源码 如果是获取resource-id,填写resourceId self.driver.find_element ...