做为忠实与较资深的Android汪, 最近抽出了一些时间研究了一下Google的亲儿子Flutter, 尚属皮毛, 只能算是个简单的记录吧.html
Google自2017年第一次提出Flutter, 到2018年Beta, 再加之RN的各类风波与问题, 使得Flutter的热度不断上升, 国内很多公司都公布Flutter在其产品中的应用, 如美团, 闲鱼等.android
PS欢迎你们加入Android技术开发交流群:653583088
本群提供免费的学习指导以及免费的解答不懂得问题均可以在本群提出来 以后还会有职业生涯规划以及面试指导进群修改群备注:开发年限-地区-经验方便解答问题程序员
Flutter做为跨平台框架, 经常被人拿出来与React Native, 以及Xamarin进行对比, 除了你们都是跨平台框架以外且能达到近乎Native的体验以外, Flutter与这二者的原理大不相同.面试
让咱们来看看这三者的结构图吧.canvas
可能有一些复杂, 咱大体解释一下.网络
React Native跟Xamarin都是基于mapping native代码来实现所谓的Native体验的框架, 只是RN基于JS引擎 + Bridge与native打交道, 而且在运行时进行绑定, 而Xamarin是基于微软的基于Linux的C#虚拟机mono + JNI与native进行通讯.app
这里Android与iOS仍是有差异的, 如RN在iOS上JS引擎不支持JIT, 会必定程度影响效率, Xamarin在iOS上能够直接编译成iOS平台能够执行的程序, 因此在实际运行起来的性能是同样的, 惟一的差异就是微软得更快的支持API同步.框架
对于Flutter来讲, 因为他的渲染引擎使用了Skia直绘, 加上基于C++的Dart引擎, 因此在不一样平台上没有差异, 加之其实现了Android Material Design与iOS Cupertino两套UI组件, 因此即使是自绘组件, 看起来仍是跟原生的一个样子.less
经过对三种跨平台引擎的大体了解, 咱们能够看出来, 他们都达到了必定程度的Native体验, 然则各自都有必定的性能损耗, 好比RN的JS引擎加载JS, 以及Bridge通讯的损耗, Xamarin Mono虚拟机与Java通讯的损耗, 以及Flutter Skia渲染与Native Android渲染的差别等.异步
Android须要在Manfest里面指定带有MAIN action与LAUNCHER category的Activity声明, 而Flutter只须要一行.
void main() => runApp(MyApp());
其中MyApp就是一个普通的Widgets(View).
Flutter没有View, 与之对应的是Widget, 而且分为StatelessWidgets与StatefulWidgets, 前者是个静态View, 后者是动态经过Data来更新的View.
Text( 'I like Flutter!', );
class StatefulText extends StatefulWidget { @override State<StatefulWidget> createState() => _TextState(); } class _TextState extends State<StatefulText> { // Default placeholder text String textToShow = "I Like Flutter"; void _updateText() { setState(() { // update the text textToShow = "Flutter is Awesome!"; }); } @override Widget build(BuildContext context) { ...invoke _updateText } }
其实是由于StatefulWidgets经过调用State
的setState
方法来触发整个Widgets树的重绘, 而且在重绘以前会调用传进去的(){ ... }
block.
实际上Flutter没有xml了, 而且是经过Widgets的嵌套来实现一个布局的.
如:
Center
是一个能够把子View放置在中央的容器.Row
对应的就是LinearLayout + Horizontal, Column
对应的就是LinearLayout + Vertical, 他们都具有一个属性叫作crossAxisAlignment
, 有点相似gravity
, 来控制子View相对于父View的位置.Expanded
支持一个相似weight的属性, 叫flex
.Container
是一个具备decoration
属性的容器, 能够用来控制背景色, border, margin等等.Stack
有点像是一个特殊的RelatetiveLayout或者ConstraintLayout, children
属性指定了它的子View, 第一个是Base View, alignment
属性指定了后面的子View相对于BaseView的位置, 如alignment: const Alignment(0.6, 0.6)
指定了位于BaseView右下角的位置.ListTile
是一个特殊的ListItem, 有三个属性, 分别是左边的Icon (leading), 文字 (title), 以及右边的Icon (trailing).ListView
, GridView
, Card
等等比较熟悉的Widgets.另外有一个相似于咱们Activity的Widgets:
MaterialApp
, 能够指定theme
, title
, 以及子View home
, 还有更重要的页面跳转routes
.MaterialApp( title: 'Welcome to Flutter', home: ..., routes: <String, WidgetBuilder> ..., theme: ThemeData( primaryColor: Colors.white ), )
还有一个相似于Fragment的:
Scaffold
, 中文意思是脚手架
, 它包含一个appBar (ActionBar)与一个body, appBar能够指定title与actions (相似于action button的点击事件).Scaffold( appBar: AppBar( title: Text(widget.title), actions: <Widget>[...], ), body: ..., )
答案是没有... 由于在Flutter看来, Widgets的树结构是不能够被更改的, 可是若是想更改, 则是经过StatefulWidgets的方法, 经过setState来更改Data, 触发Widgets重绘, 从而替换掉以前的Widgets.
Flutter一样支持, CustomPaint
做为一个 Widgets就支持传入一个实现CustomPainter
抽象类的参数, 而CustomPainter
的抽象方法也相似于Android View的onDraw
.
void paint(Canvas canvas, Size size) bool shouldRepaint(CustomPainter oldDelegate)
不用继承, 而使用相似Android ViewGroup的办法, 经过组合(composing)与封装的方法来实现, 经过小Widgets组合成须要的新Widgets.
貌似在讲相似于Activity的MaterialApp
的时候剧透了...
就是使用Navigator
与Routes
来实现界面跳转, 其实是整个Widgets的替换.
routes: <String, WidgetBuilder> { '/a': (BuildContext context) => MyPage(title: 'page A'), '/b': (BuildContext context) => MyPage(title: 'page B'), '/c': (BuildContext context) => MyPage(title: 'page C'), } Navigator.of(context).pushNamed('/b');
实际上仍是须要在Flutter App的Android壳子中注册这个filter, 而后在FlutterActivity中拿到存下来,
FlutterView初始化后再经过Bridge, 官方叫MethodChannel
从Java里获取,进行下一步逻辑.
能够看个简单的例子.
new MethodChannel(getFlutterView(), "app.channel.shared.data").setMethodCallHandler( new MethodCallHandler() { @Override public void onMethodCall(MethodCall call, MethodChannel.Result result) { if (call.method.contentEquals("getSharedText")) { result.success(sharedText); sharedText = null; } } }); getSharedText() async { var sharedData = await platform.invokeMethod("getSharedText"); if (sharedData != null) { setState(() { dataShared = sharedData; }); } }
这个Flutter有彻底对应的办法, 并且用起来很方便, 结合以前说的页面跳转:
Map xxx = await Navigator.of(context).pushNamed('/xxx'); Navigator.of(context).pop({xxx});
Flutter有点像JS, 是一个单线程模式, 因此只是经过模拟来构建简单的异步, 关键字就是相似于kotlin coroutines同样, 经过await
+async
来处理.
如:
loadData() async { response = await http.get(xxx); setState(() {xxx}); }
可是因为它的单线程, 因此没法作很长的阻塞操做, 像http请求的延迟正常状况可能都是毫秒级的, 可是数据的处理等, 可能就得秒级了.
这也是RN在线程方面的作android程序的一个痛点, Flutter采用了比较容易想到的曲线救国的办法, 提供了一个叫Isolate
的对象, 它实际是一个基于socket的数据通道, 至关于把数据放在一个独立的进程进行处理, 而后再经过socket发送回程序进程, 还记得进程间通讯办法之一的管道
吗...
自带了http库, 直接http.get(url)
, 在线程部分的代码实例里也有涉及.
经过相似gradle的文件pubspec.yaml
引入.
dependencies: ... http: ^0.12
^
表示不升大版本, 并取最新版本, 比gradle的+要范围更小.
Flutter有一个widget叫作ProgressIndicator
, 好比咱们指望有一个转圈圈的Loading界面在数据加载出来以前.
咱们就能够经过StatefulWidgets, 根据数据, 或者List Widgets的个数 (若是是显示一个List的话)来判断是否显示Loading, 使用子类CircularProgressIndicator
, 来替换页面的Widgets.
固然也是经过setState(() {...})来触发界面刷新的, 能够在initState()内触发加载数据的异步操做.
这个有点像iOS了, 即有1x,2x,3x:
images/my_icon.png // Base: 1.0x image images/2.0x/my_icon.png // 2.0x image images/3.0x/my_icon.png // 3.0x image
不同的一点还须要添加到相似gradle的文件pubspec.yaml
里.
assets: - images/my_icon.jpeg
Flutter没有像Android的string.xml
的东西, 目前来讲最好的就就是存成静态字符串.
class Strings { static String welcomeMessage = "Welcome To Flutter"; } Text(Strings.welcomeMessage)
前面说网络库, 图片资源的时候提到过, 提供了一个叫pubspec.yaml
的文件
以前作过类比, 如MaterialApp
有点相似于Activity, 而Scaffold
都点相似Fragment, 实际上他们两个都是Flutter的Widgets, 也就是说其实只有View的概念了.
Flutter有一个叫作WidgetsBinding
的能够提供相似生命周期的回调.
四种状态inactive
(iOS专用), paused
(至关于onPause, 退后台), resumed
(至关于onPostResume, 到前台), suspending
(android专用, 至关于onStop).
通常在StatefulWidgets的State中注册与反注册.
@override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); }
Flutter没有ScrollView, 合并到了ListView, 经过ListView.builder建立的ListView提供了View复用的逻辑.
ListView.builder( itemCount: widgets.length, itemBuilder: (BuildContext context, int position) { return Text(xxx); }))
其中itemBuilder有点像Android ListView的getView, 官方文档说它会自动回收Element给你, 可是事实上每次你都须要根据position生成新的Widgets, 因此呢应该是Flutter在内部回收了以前的Widgets并在你从新建立的时候又用上了.
BTW, 经过ListView构造来显示就不具有这种特性, 因此大量数据须要用Builder.
由于它实际上仍是借助了Android程序的壳子, 因此若是AndroidManifect定义了android:configChanges="orientation|screenSize"
, 则Flutter会本身hanlde.
Flutter提供了GestureDetector
, 它至关于一个Container, 将咱们指望接收手势的Widgets放进去, 再实现事件回调就好了.
GestureDetector( child: FlutterLogo( size: 200.0, ), onTap: () { print("tap"); }, )
它一样支持其余的手势, 如onDoubleTap
等等等.
首先须要在pubspec.yaml
里面配置须要的字体库:
fonts: - family: MyCustomFont fonts: - asset: fonts/MyCustomFont.ttf - style: italic
而后在Text的style
属性进行配置.
Text( 'This is a custom font text', style: TextStyle(fontFamily: 'MyCustomFont'), )
对于输入框的Hint基本一致, 可能就是换了个名字, 一看便知.
TextField( decoration: InputDecoration(hintText: "This is a hint", errorText: _getErrorText()), )
Flutter在视图渲染上另辟蹊径, 性能优点凸显, 在跨平台框架属于一匹黑马, 又有Google撑腰, 值得在Mobile勤耕多年的同窗入手.
因为做者曾经从事过2年的Webkit开发工做, 拜读了Flutter的渲染模式, 很像是Webkit/Chrome/Blink的思路, 经过查证, 起草者确实有大批一样的人, 若是你尚未入坑RN, 或许Flutter能够做为跨平台方案学习的首选哦.
一样Google本身也有不少Plugin去支持更多扩展功能, 如GPS, Camera, SharePreference, Database. 还例如Firebase这种亲儿子级的服务也是全面支持Flutter.
固然也能够本身去开发须要的Plugin来适配须要的功能, 基于的技术就是上面有提的MethodChannel
, NDK的支持也是一样的道理.
PS欢迎你们加入Android技术开发交流群:653583088 本群提供免费的学习指导以及免费的解答不懂得问题均可以在本群提出来 以后还会有职业生涯规划以及面试指导进群修改群备注:开发年限-地区-经验方便解答问题