Flutter 第一个项目是一个计数器
Flutter 的Dart代码是写在lib 文件夹下,而 lib/main.dart 是项目的入口,打开main.dart ,下边是代码:
//引入material UI库,可以理解为iOS的UIKit
import 'package:flutter/material.dart';
// 传说中的main 函数,使用dart 的特有的 => 调用方法
// 调用了MyApp函数
void main() => runApp(new MyApp());
// Widget(部件),可以理解为iOS中的UIView,iOS中叫控件,都由view封装而成,Flutter中都叫部件(小部件),都由widget封装而成
// extends:继承(OC不这么写,因为OC 一个类有2个文件组成 .h和.m)--OC 是最NB的[偷笑]!
class MyApp extends StatelessWidget {
//重写 build 方法(描述如何构建UI界面)
@override
Widget build(BuildContext context) {
// MaterialApp :是Material库中提供的Flutter APP框架,通过它可以设置应用的名称、主题、语言、首页及路由列表等
return new MaterialApp(
//名称
title: 'Flutter Demo',
//主题
theme: new ThemeData(
primarySwatch: Colors.blue,
),
//应用首页路由
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
//首页
class MyHomePage extends StatefulWidget {
//可选参数,首页导航标题,如果不清楚可以
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
// 加载首页的状态类,StatefulWidget 的特殊写法,下边详细介绍
@override
_MyHomePageState createState() => new _MyHomePageState();
}
// State 状态
// <MyHomePage> 这这个类的状态
class _MyHomePageState extends State<MyHomePage> {
//记录计数器的数字
int _counter = 0;
// 点击 “+” 按钮调用的方法,实现 i++
// _incrementCounter 这种下划线的命名的方法,表示是一个私有方法
void _incrementCounter() {
// 刷新页面
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
// Scaffold :是Material库中提供的页面脚手架,它包含导航栏和Body以及FloatingActionButton
// 就是一个封装了导航栏等等功能的小部件,可以简单理解为UINavgation
return new Scaffold(
// 导航栏
appBar: new AppBar(
title: new Text(widget.title),
),
// Center : 一个空白的view (web 中的 div )
body: new Center(
// Column: 一种布局方案,下一章布局详细介绍
child: new Column(
// 布局中的对齐,居中
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// lab
new Text(
'You have pushed the button this many times:',
),
new Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
// 按钮
floatingActionButton: new FloatingActionButton(
onPressed: _incrementCounter,// 调用方法
tooltip: 'Increment',
child: new Icon(Icons.add),// icon
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
代码还是比较简单的,点击按钮,调用 _incrementCounter 方法,_counter++,刷新页面,让 Text 显示
接下来,详细介绍一下,里边包含的知识点:
StatelessWidget 和 StatefulWidget
StatelessWidget: 无状态Widget,一旦创建,不能改变
StatefulWidget: 有状态的Widget,可以根据状态改变
- StatefulWidget 其实就是 StatelessWidget + 状态组成
- 其实 StatefulWidget 能够改变,也只是保存了“状态”,删除了老的StatelessWidget,重新创建了一个新的
- 所以StatelessWidget 是一个方法,而StatefulWidget 要多写一个 State 方法
刷新页面 setState
// 下边2种写法都可以
_counter++;
setState(() {});
setState(() {
_counter++;
});
- 调用刷新页面,删除了老的StatelessWidget,新建一个StatelessWidget,重新展示改变状态以后的页面
- 代码逻辑其实重新执行build 方法,来重绘,所以build 方法里边只放和页面布局相关的东西,不要放逻辑代码
widget 底层
widget 继承DiagnosticableTree,主要的包括key,Element ,canUpdate等等。
- key:这个key属性类似于React/Vue中的key,主要的作用是决定是否在下一次build时复用旧的widget,决定的条件在canUpdate()方法中。
- canUpdate:是否用新的Widget对象去更新旧UI树上所对应的Element对象的配置;只要newWidget与oldWidget的runtimeType和key同时相等时就会用newWidget去更新Element对象的配置,否则就会创建新的Element。
- Element属性
事实上 Widget 只是 Element 的一个配置描述 ,告诉 Element 这个实例如何去渲染。
从上图注释也可知: Widget 和 Element 之间是一对多的关系 。
而Element 里边有renderObject属性
而renderObject 是Flutter布局和绘制的基本单元
由此可以得出: Widget 生成了 Element,而后创建 RenderObject 关联到 Element 的内部 renderObject 对象上,最后Flutter 通过 RenderObject 数据来布局和绘制。
说到 RenderObject ,就不得不说 RenderBox ,从源码注释可以看出,它是在继承 RenderObject 基础的布局和绘制功能上,实现了“笛卡尔坐标系”:以 Top、Left 为基点,通过宽高两个轴实现布局和嵌套的。
RenderBox 避免了直接使用 RenderObject 的麻烦场景,其中 RenderBox 的布局和计算大小是在 performLayout() 和 performResize() 这两个方法中去处理,很多时候我们更多的是选择继承 RenderBox 去实现自定义。
综合上述情况,我们知道:
- Widget只是显示的数据配置,所以相对而言是轻量级的存在,而 Flutter 中对 Widget 的也做了一定的优化,所以每次改变状态导致的 Widget 重构并不会有太大的问题。
- RenderObject 就不同了,RenderObject 涉及到布局、计算、绘制等流程,要是每次都全部重新创建开销就比较大了。
所以针对是否每次都需要创建出新的 Element 和 RenderObject 对象,Widget 都做了对应的判断以便于复用,比如:在 newWidget 与oldWidget 的 runtimeType 和 key 相等时会选择使用 newWidget 去更新已经存在的 Element 对象,不然就选择重新创建新的 Element。
由此可知:Widget 重新创建,Element 树和 RenderObject 树并不会完全重新创建。
那么再来个图理解一下:
widget 底层这块参考大神所写:
https://juejin.im/post/5c7e853151882549664b0543