Flutter入门篇
初识
Flutter 是 Google推出并开源的移动应用开发框架,主打跨平台、高保真、高性能。使用Skia绘制引擎实现自绘UI系统,实现自绘UI+原生的跨平台框架,目前已默认支持iOS、Android、Fuchsia(Google新的自研操作系统)三个移动平台,还有Web开发(Flutter for web)和PC开发。
高性能主要靠两点来保证,首先,Flutter采用Dart语言开发。Dart是为数不多同时支持JIT和AOT的语言。在开发阶段,Dart在 JIT(即时编译)模式下,速度与 JavaScript基本持平。 Dart在发布期使用 AOT,当以 AOT模式运行时,JavaScript便远远追不上了。速度的提升对高帧率下的视图数据计算很有帮助。其次,Flutter使用自己的渲染引擎来绘制UI,布局数据等由Dart语言直接控制,所以在布局过程中不需要像RN那样要在JavaScript和Native之间通信,这在一些滑动和拖动的场景下具有明显优势,因为在滑动和拖动过程往往都会引起布局发生变化,所以JavaScript需要和Native之间不停的同步布局信息,这和在浏览器中要JavaScript频繁操作DOM所带来的问题是相同的,都会带来比较可观的性能开销。
框架结构
Embedder:作为在不同平台下与底层系统协调获取服务的针对各平台入口。利用此底层嵌入器,flutter可以将code作为module嵌入原生app,也可作为完整app跨平台使用。
Engine:纯 C++实现的 SDK,其中包括了 Skia引擎、Dart运行时、文字排版引擎等。通过dart:ui包暴露classes
FrameWork:纯 Dart实现的 SDK,提供一套基础库。
从下往上支持:
- Foundation和Animation、Painting、Gestures在Google的一些视频中被合并为一个dart UI层,对应的是Flutter中的
dart:ui
包,它是Flutter引擎暴露的底层UI库,提供动画、手势及绘制能力。 - Rendering 抽象的布局层,它依赖于dart UI层,Rendering层会构建一个UI树,当UI树有变化时,会计算出有变化的部分,然后更新UI树,最终将UI树绘制到屏幕上,这个过程类似于React中的虚拟DOM。Rendering层可以说是Flutter UI框架最核心的部分,它除了确定每个UI元素的位置、大小之外还要进行坐标变换、绘制(调用底层dart:ui)。
- Widgets层是Flutter提供的的一套基础组件库,在基础组件库之上,Flutter还提供了 Material 和Cupertino两种视觉风格的组件库。而我们Flutter开发的大多数场景,只是和这两层打交道
demo
从项目结构上看,创建project默认支持ios和android平台,同时引入需要的libraries
项目代码路径位于…/lib下,程序入口为Flutter的入口在"lib/main.dart"的main()
函数
中
认识demo
void main() {
runApp(MyApp());//flutter入口
}
class MyApp extends StatelessWidget {
// 作为应用的根组件
@override
Widget build(BuildContext context) {
return MaterialApp( //应用使用material主题框架
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
// This makes the visual density adapt to the platform that you run
// the app on. For desktop platforms, the controls will be smaller and
// closer together (more dense) than on mobile platforms.
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: HomePage(),//主页widget构建显示
);
}
}
//statefuleWidget由widget类和对应state类实现
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);//widget构造函数
//Key作为重要元素,当前仅理解与rebuild对比相关,在组件树中有唯一性
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
//dart语言以"_"开头表示private
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {//变动后,需引发rebuild的导火线
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(//脚手架
appBar: AppBar(
title: Text(widget.title),//state类直接拥有相关widget类对象
),
body: Center(//layout widget
child: Column(//layout widget
mainAxisAlignment: MainAxisAlignment.center,//对齐方式
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',//变量使用
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,//按钮响应函数
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Widget
我们知道在Flutter中几乎所有的对象都是一个Widget。与原生开发中“控件”不同的是,Flutter中的Widget的概念更广泛,它不仅可以表示UI元素,也可以表示一些功能性的组件如:用于手势检测的 GestureDetector
widget、用于APP主题数据传递的Theme
等等。
Widget作为控件大致分为StatelessWidget和StatefulWidget。
StatelessWidget内部直接使用build方法构造显示元素
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: new Container(
color: Colors.white,
child: new Text("hello world"),
),
);
}
}
StatefulWidget试例由demo可见,通过setState()引发页面改动部分重新渲染。
StatefulWidget状态管理用法:
class ClickPage extends StatelessWidget {
final String title = 'clickPage';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: ParentWidgetC(),
));
}
}//页面快速构建
class ParentWidgetC extends StatefulWidget {
@override
_ParentWidgetCState createState() => new _ParentWidgetCState();
}
class _ParentWidgetCState extends State<ParentWidgetC> {
bool _active = false;
void _handleTapboxChanged(bool newValue) {
setState(() {
_active = newValue;
});
}
@override
Widget build(BuildContext context) {
print("mt ParentWidgetC build");
return new Container(
child: new Tapbox(
active: _active,//父Widget管理变量改动
onChanged: _handleTapboxChanged, //函数作为参数传入
),
);
}
}
----------------------------子widget构建---------------------------------
class Tapbox extends StatefulWidget {
Tapbox({Key key, this.active: false, @required this.onChanged})
: super(key: key);
final bool active;//父Widget管理的状态变量,由子Widget渲染具体内容
final ValueChanged<bool> onChanged;//回调引发父Widget rebuild
@override
_TapboxState createState() => new _TapboxState();
}
class _TapboxState extends State<Tapbox> {
bool _highlight = false;//子Widget控制触碰边框的状态变量
void _handleTapDown(TapDownDetails details) {
setState(() {
_highlight = true;
});
}
void _handleTapUp(TapUpDetails details) {
setState(() {
_highlight = false;
});
}
void _handleTapCancel() {
setState(() {
_highlight = false;
});
}
void _handleTap() {
widget.onChanged(!widget.active);
}
@override
Widget build(BuildContext context) {
print("mt Tapbox build");
// 在按下时添加绿色边框,当抬起时,取消高亮
return new GestureDetector(//手势事件也可设定区域作为widget
onTapDown: _handleTapDown,
onTapUp: _handleTapUp,
onTap: _handleTap,
onTapCancel: _handleTapCancel,
child: new Container(
child: new Center(
child: new Text(widget.active ? 'Active' : 'Inactive',
style: new TextStyle(fontSize: 32.0, color: Colors.white)),
),
width: 200.0,
height: 200.0,
decoration: new BoxDecoration(
color: widget.active ? Colors.lightGreen[700] : Colors.grey[600],
border: _highlight
? new Border.all(
color: Colors.teal[700],
width: 10.0,
)
: null,
),
),
);
}
}
StatefulWidget的状态管理可由自身、父类或者两者混合管理。
State生命周期
State类声明周期如上图所示:
**initState:**当Widget第一次插入到Widget树时会被调用,对于每一个State对象,Flutter framework只会调用一次该回调,所以,通常在该回调中做一些一次性的操作,如状态初始化、订阅子树的事件通知等。
didChangeDependencies()_:_当State对象的依赖发生变化时会被调用;例如:在之前build() 中包含了一个InheritedWidget,然后在之后的build() 中InheritedWidget发生了变化,那么此时InheritedWidget的子widget的didChangeDependencies()回调都会被调用。典型的场景是当系统语言Locale或应用主题改变时,Flutter framework会通知widget调用此回调。
**build():**用于构建Widget子树
reassemble():此回调是专门为了开发调试而提供的,在热重载(hot reload)时会被调用。
didUpdateWidget():在widget重新构建时,framework会调用Widget.canUpdate来检测Widget树中同一位置的新旧节点,然后决定是否需要更新,如果Widget.canUpdate返回true则会调用此回调。canUpdate中实际对比key和runtimeType
_deactivate():_当State对象从树中被移除时,会调用此回调。在一些场景下,framework会将State对象重新插到树中,如包含此State对象的子树在树的一个位置移动到另一个位置时(可以通过GlobalKey来实现)。如果移除后没有重新插入到树中则紧接着会调用dispose()方法。
_dispose():_当State对象从树中被永久移除时调用;通常在此回调中释放资源。