- 与iOS开发的异同。例:Widget和UIView,交互等
- 生命周期,Widget
- 常用Widget介绍
- 路由跳转
- 网络请求,json解析
一、与原生开发的区别
首先要了解flutter。什么是flutter?flutter和ReactNative、Weex有什么区别?和原生开发有什么区别?
什么是flutter?先引用一段官方解释:
Flutter is Google’s mobile app SDK for crafting high-quality native interfaces on iOS and Android in record time. Flutter works with existing code, is used by developers and organizations around the world, and is free and open source.
官方的说法很简单,flutter就是一个移动应用的SDK,它跨平台(iOS和Android),也支持混合开发,且开源免费。我们可以使用flutter编写高质量的具有原生体验的应用。
要彻底了解flutter,我们必须要先了解下Fuchsia
、Skia
、Dart
。
Fuchsia
Fuchsia
是谷歌开发的一款全新的操作系统,他不同于谷歌之前开发的系统,如ChromeOS、Android都是基于Linux内核,而Fuchsia
是基于全新内核Zircon,对于硬件要求更低,性能更加卓越。
对于这款系统,谷歌一直很低调,就连最初的亮相也是因为于2016年8月份在Github开源的项目而公之于众而非官宣,但是对于这款操作系统的愿景和目的,谷歌也一直很隐晦,但是从谷歌的动作不难推测,它是想开发一款能够运行在所有不同设备的系统,智能手机、平板电脑、个人电脑等,胃口不可谓不大,大众一度推测,这款全新系统大有日后逐步取代Android的趋势。
而Flutter跟Fuchsia
有什么关系呢?
Flutter是Fuchsia
操作系统构建UI的SDK。这种关系,不需多言,Flutter的定位一下清晰了很多。
Skia
Skia
是一款用C++开发的性能彪悍的2d图像绘制引擎,于2005年被谷歌收购。因为其出色的绘制表现,被广泛应用,如ChromeOS、ChromiumOS、FirefoxOS、Android等产品,当然,Flutter的底层2D图像引擎用的就是Skia
。
现在我们明白了,原来Flutter的实现思路,完全不同于React Native,Weex之类通过建立JSBridge/JSCore来桥接iOS/Android的原生控件,而是做的比较彻底,==直接基于全新的2d图像引擎Skia
来绘制图像==!
这是一项伟大的突破,因为,真正的跨平台,如果不甩开JSBridge,就如同带着脚铐跳舞。而Flutter做到了,这也是其相比于其他方案的绝对优势之一。
Dart
Flutter的开发语言是Dart
。
Dart
是谷歌推出的编程语言,谷歌一直想拥有一门能够完美编写前端、后端的语言,而自家的Go语言,是静态强类型、编译型,显然不适合谷歌想Productive编写前端应用的愿景。
为什么没有选择JS呢?JS作为一名编程语言界的冉冉新星,粉丝拥簇,Google而没有选择它,理由很社会:Dart
语言组就在自己隔壁,Flutter需要的一些新特性,Dart
能够快速落地实现,而如果选择了JS,Google如果想为语言添加新特性,就必须经过各种委员会和浏览器提供商的决议...。
而事实上,Dart
的确做到了亲兄弟间的密切支持,2018年2月份发布的Dart2.0
,2019年12月份最新发布的Dart2.1
,就是为Flutter量身定制了许多语法特性以及改善了很多语法结构。
当然,选择Dart
,还有其他更坚定的理由:
Dart
支持即时编译JIT(Just In Time)
和事前编译AOT(Ahead of Time)
。
JIT为Flutter的HotReload(热加载)提供了可能。热加载是,当你在编辑器上修改了代码想看效果,不像传统的iOS/Android开发那样必须重新编译运行等待好长时间才能看结果(当然,现在Android/RN等也可以)。而Flutter的热加载,实现了在不到1秒的时间内就能看到修改后的结果,真是溜到飞起。要知道,热加载是开发者提高生产力的关键。而AOT将Dart
编译成更加高效的本地代码,代码性能和用户体验更加卓越。Dart
是一门易于上手的语言,事实上,Dart作为一门现代化语言,集百家之长,可以随处可见Java、Swift、JS的影子,降低了跨语言开发的学习成本。富有语言表现力,如对函数式编程的支持相比于其他语言如Java要强太多。
性能卓越。如在VM方面,谷歌凭借着其在Dalvik、Go的技术沉淀,
Dart
VM在内存回收、吞吐量都有着不错的表现。
flutter和ReactNative、Weex有什么区别?
这个问题在上面介绍flutter中已经给出答案了。最大区别就是flutter是抛开JSBridge的方式,而是建立在自己的2d图形绘制引擎上绘制UI。也就是说和原生UI组件毫无关系,不受原生API的影响。而ReactNative和Weex其实都是在通过jsbridge调用原生控件实现。
下面通过一张表来看下整体的一个对比:
比较内容 | Flutter | Weex | ReactNative |
---|---|---|---|
平台实现 | 通过Dart虚拟机编译成机器码 | 1、Vue编写的Web页面编译成JSBundle;2、Native端解析DOM,生成真实的Native控件; | 1、React编写JS文件,如果是UI界面,会映射到Virtual DOM;2、通过C++编写的Bridge调用原生的API,控件则是根据DOM映射到原生的View; |
绘制引擎 | Skia | 1、2D绘制:JSCore+Skia;2、3D绘制:JSCore+OpenGL ES | 1、2D绘制:JS V8+Skia;2、3D绘制:JS V8+OpenGL ES |
开发语言 | Dart | Vue | React |
上手难度 | 一般 | 容易 | 较难 |
社区 | 丰富、谷歌团队支持 | 较小、阿里支持(目前托管Apache) | 活跃、Facebook支持 |
跨平台 | Android、iOS、Web | Android、iOS、Web | Android、iOS |
热更新 | 支持 | 支持 | 支持 |
性能 | 自带绘制引擎、号称性能接近原生,但目前实际效果较原生还是有些距离 | 因为多了一层JS解析,渲染慢一些 | 同Weex |
讲了这么多,那么和原生开发有什么区别呢?
以 iOS 为例。在 iOS 中,你在 UI 中创建的大部分视图都是UIView
的实例。而在构造布局时,这些视图也可以作为其他视图的容器。
在 Flutter 中,同UIView
能够进行类比的就是Widget
了。但Widget
和 iOS 里的视图并不能同等对待,不过当你想要了解 Flutter 的工作原理时,你可以把它理解为“声明和构造 UI 的方法”。
然而,Widget
和UIView
还是有着相当一部分区别的。首先,widget
拥有着不同的生命周期:整个生命周期内它是不可变的,且只能够存活到被修改的时候。一旦 widget
实例或者它的状态发生了改变, Flutter 框架就会创建一个新的由 Widget
实例构造而成的树状结构。而在 iOS 里,修改一个视图并不会导致它重新创建实例,它作为一个可变对象,只会绘制一次,只有在发生 setNeedsDisplay()
调用之后才会发生重绘。
还有,和 UIView
不同,Flutter 的 widget
是很轻量的,一部分原因就是源于它的不可变特性。因为它并不是视图,也不直接绘制任何内容,而是作为对 UI 及其特性的一种描述,而被“注入”到视图中去。
Flutter
包含了 Material Components
库。内容都是一些遵循了 Material Design
设计规范 的组件。Material Design
是一种灵活的 支持全平台 的设计体系,其中也包括了 iOS。
但是 Flutter 的灵活性和表现力使其能够适配任何的设计语言。在 iOS 中,你可以通过 Cupertino widgets
来构造类似于 Apple iOS 设计语言 的接口。
二、flutterWidget
和State
的生命周期
因为在 Flutter 中,widget 都是不可变的,所以也不能够直接对其修改。所以,你必须通过修改 widget 的 state 来达到更新视图的目的。
于是就引入了 Stateful widget 和 Stateless widget 的概念。
StatelessWidget的生命周期
StatelessWidget的生命周期很简单,只有build。
StatefulWidget的生命周期
StatefulWidget的生命周期比较复杂,我们先创建一个继承StatefulWidget
的子类和对应的State
来探究其生命周期。
class _Body extends StatefulWidget {
@override
State createState() {
return new _BodyState();
}
}
class _BodyState extends State {
bool showPage = false;
@override
Widget build(BuildContext context) {
return new Center(
child: new Row(
children: [
new RaisedButton(onPressed: (){
setState(() {
showPage = !showPage;
});
}, child: new Text('切换'),),
showPage ? new Text('不展示page') : new _MyhomePage(),
],
),
);
}
}
class _MyhomePage extends StatefulWidget {
// 指需要变化的那部分UI
@override
StatefulElement createElement() {
__p('createElement');
return super.createElement();
}
@override
State createState() {
__p('createState');
return new _CountState();
}
}
class _CountState extends State<_MyhomePage> {
var i = 0;
@override
void initState() {
super.initState();
__p('initState');
}
@override
void didUpdateWidget(_MyhomePage oldWidget) {
super.didUpdateWidget(oldWidget);
__p('didUpdateWidget');
}
@override
void deactivate() { // 使无效
super.deactivate();
__p('deactivate');
}
@override
void dispose() { // 处理
super.dispose();
__p('dispose');
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
__p('didChangeDependencies');
}
@override
Widget build(BuildContext context) {
__p('build');
return new Container(
child: new FlatButton(
onPressed: (){
setState(() {
__p('setState');
i++;
});
},
child: new Text('点击$i',textDirection: TextDirection.ltr,),
),
width: 100,
height: 40,
color: Colors.red,
margin: EdgeInsets.symmetric(horizontal: 50,vertical: 50),
);
}
}
启动程序后打印结果如下:
flutter: ==createElement
flutter: ==createState
flutter: ==initState
flutter: ==didChangeDependencies
flutter: ==build
点击按钮后打印结果如下:
flutter: ==setState
flutter: ==build
当点击切换将_MyHomePage从tree中移除时,打印结果如下:
flutter: ==deactivate
flutter: ==dispose
因此,总结下来StatefulWidget的生命周期就可以用如下这张图表示。
flutter中常见的Widget
文本Text
new Text(
'Hello Hello HelloHello你好',
// 文字样式
style: new TextStyle(
inherit: false,
color: Colors.red, // 文字颜色
backgroundColor: Colors.blue, // 背景色
fontSize: 20, // 字体大小
fontWeight: FontWeight.w400, // 字体粗细
fontStyle: FontStyle.italic, // 正常还是斜体
letterSpacing: 2, // 字符间距
wordSpacing: 5, // 实测为单词间的间距
textBaseline: TextBaseline.ideographic, // 基线模式
height:2, // ??
decoration: TextDecoration.underline, // 下划线
decorationColor: Colors.black, // 划线的颜色
decorationStyle: TextDecorationStyle.wavy, // 这个style可能控制画实线,虚线,两条线,点, 波浪线等
decorationThickness:2, // 线的厚度
),
)
富文本RichText
RichText(
text: TextSpan(
text: '登录即视为同意',
style: TextStyle(color: Colors.black, fontSize: 16),
children: [
TextSpan(
text: '《xx协议》',
style: TextStyle(color: Colors.blue),
recognizer: new TapGestureRecognizer()
..onTap = () {
print('点击协议');
}),
WidgetSpan(child: Icon(Icons.account_balance)),
]),
);
其中RichText的属性定义为:final InlineSpan text;
而继承自InlineSpan
的类则有TextSpan和WidgetSpan。*WidgetSpan有个child属性,支持各种widget,因此我们可以轻易的实现图文排布的功能。
图片Image
先看看Image类
构造器中的必填参数final ImageProvider image;
其中ImageProvider
是个抽象类,它的抽象子类有以下这些:
- AssetBundleImageProvider
- ResizeImage
- NetworkImage
- FileImage
- MemoryImage
Image
就是通过ImageProvider
的各种实现类来加载图片的。
- Image.asset:用来加载本地资源图片
- Image.file:用来加载本地(File文件)图片
- Image.network:用来加载网络图片
- Image.memory:用来加载Uint8List资源(byte数组)图片
Image.asset
的使用如下,
Column(
children: [
Image.asset('images/25.jpg'),
Image(image: AssetImage('images/icon_ppt_step.png'),width: 40,height: 90,),
],
);
并且需要在配置文件pubspec.yaml
中需要声明图片的路径:
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
assets:
- images/25.jpg
- images/icon_ppt_step.png #表示引入单张图片
# 或者如下写法,不用每张图片路径都写
- images/ #表示引入整个images文件夹的图片资源
千万要注意不要多空格,一旦格式不对,否则不能加载图片。
网络图片加载:
Image.network('http://n.sinaimg.cn/sports/2_img/upload/cf0d0fdd/107/w1024h683/20181128/pKtl-hphsupx4744393.jpg')
按钮
class | 描述 |
---|---|
RaisedButton | 凸起的按钮,其实就是Material Design风格的Button |
FlatButton | 扁平化的按钮 |
OutlineButton | 线框按钮 |
IconButton | 图标按钮 |
ButtonBar | 按钮组 |
FloatingActionButton | 浮动按钮 |
布局
Container 矩形布局
- 构造函数
Container({
Key key,
this.alignment,
this.padding,
Color color,
Decoration decoration,
this.foregroundDecoration,
double width,
double height,
BoxConstraints constraints,
this.margin,
this.transform,
this.child,
})
- 属性解析
key:Container唯一标识符,用于查找更新。
alignment:控制child的对齐方式,如果container或者container父节点尺寸大于child的尺寸,这个属性设置会起作用,有很多种对齐方式。
padding:decoration内部的空白区域,如果有child的话,child位于padding内部。padding与margin的不同之处在于,padding是包含在content内,而margin则是外部边界,设置点击事件的话,padding区域会响应,而margin区域不会响应。
color:用来设置container背景色,如果foregroundDecoration设置的话,可能会遮盖color效果。
decoration:绘制在child后面的装饰,设置了decoration的话,就不能设置color属性,否则会报错,此时应该在decoration中进行颜色的设置。
foregroundDecoration:绘制在child前面的装饰。
width:container的宽度,设置为double.infinity可以强制在宽度上撑满,不设置,则根据child和父节点两者一起布局。
height:container的高度,设置为double.infinity可以强制在高度上撑满。
constraints:添加到child上额外的约束条件。
margin:围绕在decoration和child之外的空白区域,不属于内容区域。
transform:设置container的变换矩阵,类型为Matrix4。
child:container中的内容widget。
参考资料:
- https://flutter.cn/docs/get-started
- https://www.jianshu.com/p/841bc1d7345b
- https://flutter.cn/docs/get-started/flutter-for/ios-devs#uiview-相当于-flutter-中的什么
- https://flutter.cn/docs/development/ui/widgets