Flutter入坑指南

前言

本文是一个纯Flutter项目,主要通过实现网络请求新闻表展示以及跳转webview详情页,来初步感受了一下最近很火的Flutter

image.png | left | 200x348.28660436137073

代码在 源码地址 部分代码参考 https://github.com/zhangjianli/flutter_news

安装环境

可以参考 Flutter中文网 我使用的是AndroidStudio,这里需要注意的是下载下来的flutter sdk 在会自动下载dart sdk 具体在 flutter/bin/cache/dark-sdk往往在导入新工程时候需要设置dart sdk 注意设置一下就好

Dart

Flutter的开发语言采用Dart,这是一门很棒的语言,集合了现代语言的很多优点,学习了一下之后感觉有java的影子,也有es6的影子,写起来很简练,Dart有个官方学习网站

Flutter之布局

比如实现一个如下的布局

stack.png | center | 150x150

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
var stack = new Stack(
alignment: const Alignment(0.6, 0.6),
children: [
new CircleAvatar(
backgroundImage: new AssetImage('images/pic.jpg'),
radius: 100.0,
),
new Container(
decoration: new BoxDecoration(
color: Colors.black45,
),
child: new Text(
'Mia B',
style: new TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
],
);
// ...
}
}

乍一看其实挺懵逼的,

要记住一点在Flutter中一切皆为’Widget‘,在flutter的世界里,包括views,view controllers,layouts等在内的概念都建立在Widget之上。widget是flutter功能的抽象描述,掌握Flutter的基础就是学会使用widget开始甚至一个padding就是一个widget,官网是按照子元素来分类的

  • 单个子元素(child)的布局,包括Container、Padding等18种(目前是2018年12月1日,后续我想肯定会增加的);
  • 多个子元素(children)的布局,包括Row、Column等11种;
  • layout helper,例如ListView.Builder,在元素多的时候,用这种方式更加的高效,类似Android的RecyclerView,有自动的回收机制。这种严格意义上不能算是一个种类,我觉得这种helper会越来越多。

常见有的其中常用有 Container、Padding、Center、Flex、Stack、Row、Column、ListView










































类型


特点


Container


只有一个子 Widget。默认充满,包含了padding、margin、color、宽高、decoration 等配置。


Padding


只有一个子 Widget。只用于设置Padding,常用于嵌套child,给child设置padding。


Center


只有一个子 Widget。只用于居中显示,常用于嵌套child,给child设置居中。


Expanded


只有一个子 Widget。在 Column 和 Row 中充满。


Stack


可以有多个子 Widget。 子Widget堆叠在一起。 (类似Android的 FrameLayout)


Column


可以有多个子 Widget。垂直布局。 (类似Android的 LinearLayout vertical)


Row


可以有多个子 Widget。水平布局。 (类似Android的 LinearLayout horizon)


这布局写起来确实很酸爽,不过统一渲染效率更高,但是掌握起来比较费事,感觉平台侧的一些成本转嫁到开发者身上,不知道后期有没有类似xml这种布局单独剥离出来的实现。

widget详解

一切皆为widget,主要分为两类 StatefulWidget(有状态)StatelessWidget(无状态)

  • StatefulWidget(有状态)
    如果一个控件需要动态的去改变或者相应一些状态,例如点击态、色值、内容区域等,那么一般都是继承自StatefulWidget,常见的有CheckBox、AppBar、TabBar等
  • StatelessWidget(无状态)
    如果一个控件自身状态不会去改变,创建了就直接显示,不会有色值、大小或者其他属性的变化,这种widget一般都是继承自StatelessWidget,常见的有Container、ScrollView等

StatefulWidget和StatelessWidget如下所示

image.png | left | 747x214

一个StatelessWidget可以用多个不同的BuildContext构建,而一个StatefulWidget会为每个BuildContext创建一个State对象

对于StatelessWidget,build方法会在如下三种情况下调用,

  1. widget第一次被插入到树中;
  2. widget的父节点更改了配置(configuration);
  3. widget依赖的InheritedWidget改变了;
1
2
3
4
5
6
7
8
class Frog extends StatelessWidget {
const Frog({ Key key }) : super(key: key);

@override
Widget build(BuildContext context) {
return new Container(color: const Color(0xFF2DBD3A));
}
}

StatefulWidget的两个主要类别:

  • 在initState中创建资源,在dispose中销毁,但是不依赖于InheritedWidget或者调用setState方法,这类widget基本上用在一个应用或者页面的root;
  • 使用setState或者依赖于InheritedWidget,这种会在生命周期中会被重建(rebuild)很多次。
1
2
3
4
5
6
7
8
9
10
11
12
13
class YellowBird extends StatefulWidget {
const YellowBird({ Key key }) : super(key: key);

@override
_YellowBirdState createState() => new _YellowBirdState();
}

class _YellowBirdState extends State<YellowBird> {
@override
Widget build(BuildContext context) {
return new Container(color: const Color(0xFFFFE306));
}
}

State

状态有何作用呢

  • 在widget构建的时候可以被同步读取;
  • 在widget的生命周期中可能会被改变。

    State生命周期

    对应的生命周期如下

  • created:当State对象被创建时候,State.initState方法会被调用;

  • initialized:当State对象被创建,但还没有准备构建时,State.didChangeDependencies在这个时候会被调用;
  • ready:State对象已经准备好了构建,State.dispose没有被调用的时候;
  • defunct:State.dispose被调用后,State对象不能够被构建

image.png | left | 747x391

完整生命周期如下:

  • 创建一个State对象时,会调用StatefulWidget.createState;
  • 和一个BuildContext相关联,可以认为被加载了(mounted);
  • 调用initState;
  • 调用didChangeDependencies;
  • 经过上述步骤,State对象被完全的初始化了,调用build;
  • 如果有需要,会调用didUpdateWidget;
  • 如果处在开发模式,热加载会调用reassemble;
  • 如果它的子树(subtree)包含需要被移除的State对象,会调用deactivate;
  • 调用dispose,State对象以后都不会被构建;
  • 当调用了dispose,State对象处于未加载(unmounted),已经被dispose的State对象没有办法被重新加载(remount)。

setState

当修改状态时,调用setState widget会被更新

如何布局

每个页面设计都不一样,相同页面可选择的布局方式也不一样,可以参考下Flutter官方的布局教程。整个过程,基本上按照拆解、组件封装、具体布局这三步来的。

Flutter之网络请求

在Android开发者我们可以使用okHttp,Flutter给我们封装好了,在pubspec.yaml文件中接入网络依赖库,相当于gradle的dependency,可以在 包查找 中找到最新版本,在pubspec.yaml接入时候主要空格就好

1
2
3
4
5
6
7
8
9
10
11
dependencies:
flutter:
sdk: flutter

json_annotation: ^2.0.0
flutter_webview_plugin: ^0.3.0+2
http: ^0.12.0

# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2

具体使用起来也比较简单

1
2
3
4
5
6
7
8
9
10
11
12
class NewsApi {
static Future<NewsList> getHeadlines(
{String category: "general", int page: 0}) async {
final response = await http.get(
"https://newsapi.org/v2/top-headlines?country=us&apiKey=$apikey&page=$page&category=$category");
return compute(parseResult, response.body);
}

static NewsList parseResult(String respond) {
return NewsList.fromJson(json.decode(respond));
}
}

具体可以学习官网 在Flutter中发起HTTP网络请求

Flutter之数据解析

官网也给了反序列化的方法但是感觉比较古老,就跟json一样拿到msg一个个解析,有个更好的方式直接反序列化成对象,后面使用更加快捷

首先需要添加依赖

1
2
3
4
5
6
7
dependencies:
……
json_annotation: ^2.0.0
dev_dependencies:
……
build_runner: ^1.1.2
json_serializable: ^2.0.0

具体用法可以参考 https://juejin.im/post/5b5f00e7e51d45190571172f#heading-13

参考

0%