一位安卓程序员入坑Flutter后整理出一份超详细的学习笔记
wptr33 2024-12-05 17:00 22 浏览
作为忠实与较资深的Android汪,最近抽出了一些时间研究了一下Google的亲儿子Flutter,尚属皮毛,只能算是个简单的记录吧。
Google自2017年第一次提出Flutter,到2018年Beta,再加之RN的各种风波与问题,使得Flutter的热度不断上升,国内不少公司都公布Flutter在其产品中的应用,如美团,闲鱼等。
前言
Flutter作为跨平台框架,常常被人拿出来与React Native,以及Xamarin进行对比,除了大家都是跨平台框架之外且能达到近乎Native的体验之外,Flutter与这两者的原理大不相同。
让我们来看看这三者的结构图吧:
可能有一些复杂,咱大致解释一下:
React Native跟Xamarin都是基于mapping native代码来实现所谓的Native体验的框架,只是RN基于JS引擎 + Bridge与native打交道,并且在运行时进行绑定,而Xamarin是基于微软的基于Linux的C#虚拟机mono + JNI与native进行通信。
这里Android与iOS还是有差别的,如RN在iOS上JS引擎不支持JIT,会一定程度影响效率,Xamarin在iOS上可以直接编译成iOS平台可以执行的程序,所以在实际运行起来的性能是一样的,唯一的差别就是微软得更快的支持API同步。
对于Flutter来说,由于他的渲染引擎使用了Skia直绘,加上基于C++的Dart引擎,所以在不同平台上没有差别,加之其实现了Android Material Design与iOS Cupertino两套UI组件,所以即便是自绘组件,看起来还是跟原生的一个样子。
通过对三种跨平台引擎的大致了解,我们可以看出来,他们都达到了一定程度的Native体验,然则各自都有一定的性能损耗,比如RN的JS引擎加载JS,以及Bridge通信的损耗,Xamarin Mono虚拟机与Java通信的损耗,以及Flutter Skia渲染与Native Android渲染的差异等。
Flutter笔记
如何启动一个app
Android需要在Manfest里面指定带有MAIN action与LAUNCHER category的Activity声明,而Flutter只需要一行。
void main() => runApp(MyApp());
其中MyApp就是一个普通的Widgets(View).
View vs Widgets
Flutter没有View,与之对应的是Widget,并且分为StatelessWidgets与StatefulWidgets,前者是个静态View,后者是动态通过Data来更新的View。
- Stateless
Text(
'I like Flutter!',
);
- Stateful
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。
怎么写Layout, XML到哪里去了
实际上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: ...,
)
如何从父View中Remove一个元素
答案是没有... 因为在Flutter看来吗,Widgets的树结构是不可以被更改的,但是如果想更改,则是通过StatefulWidgets的方法,通过setState来更改Data,触发Widgets重绘,从而替换掉之前的Widgets。
喜欢画Canvas的同学怎么办?
Flutter同样支持,CustomPaint作为一个 Widgets就支持传入一个实现CustomPainter抽象类的参数,而CustomPainter的抽象方法也类似于Android View的onDraw。
void paint(Canvas canvas, Size size)
bool shouldRepaint(CustomPainter oldDelegate)
如何自定义View
不用继承,而使用类似Android ViewGroup的办法,通过组合(composing)与封装的方法来实现,通过小Widgets组合成需要的新Widgets。
页面跳转怎么办,四大组件之一的Intent跑哪里去了
貌似在讲类似于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');
如何处理外部的Intent
实际上还是需要在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;
});
}
}
常用的startActivityForResult怎么办.
这个Flutter有完全对应的办法,而且用起来很方便,结合之前说的页面跳转。
Map xxx = await Navigator.of(context).pushNamed('/xxx');
Navigator.of(context).pop({xxx});
异步怎么办, runOnUiThread()哪里去了
Flutter有点像JS,是一个单线程模式,所以只是通过模拟来构建简单的异步,关键字就是类似于kotlin coroutines一样,通过await+async来处理。
如:
loadData() async {
response = await http.get(xxx);
setState(() {xxx});
}
但是由于它的单线程,所以无法做很长的阻塞操作,像http请求的延迟正常情况可能都是毫秒级的,但是数据的处理等,可能就得秒级了。
这也是RN在线程方面的做android程序的一个痛点,Flutter采用了比较容易想到的曲线救国的办法,提供了一个叫Isolate的对象,它实际是一个基于socket的数据通道,相当于把数据放在一个独立的进程进行处理,然后再通过socket发送回程序进程,还记得进程间通信办法之一的管道吗...
具体API可以参考文档1...,2....
Flutter 替代OkHttp的网络库
自带了http库,直接http.get(url),在线程部分的代码实例里也有涉及。
通过类似gradle的文件pubspec.yaml引入。
dependencies:
...
http: ^0.12
^表示不升大版本,并取最新版本,比gradle的+要范围更小。
常见的LCE(Loading Content Error)里面的Loading怎么show
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)
Gradle变成什么了
前面说网络库,图片资源的时候提到过,提供了一个叫pubspec.yaml的文件,具体支持的规则可以查看这个文档。
Fragment与Activity呢?
之前做过类比,如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();
}
ScrollView vs ListView
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。
Flutter横竖屏怎么玩
因为它实际上还是借助了Android程序的壳子,所以如果AndroidManifect定义了android:configChanges="orientation|screenSize",则Flutter会自己hanlde。
怎么处理Gesture
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哪里去了, 错误信息怎么输出
对于输入框的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。这些都可以通过Dartlang来查询。
当然也可以自己去开发需要的Plugin来适配需要的功能,基于的技术就是上面有提的MethodChannel,NDK的支持也是同样的道理。
最后分享一份字节跳动厂内部超高质量Flutter+Kotlin笔记!技术与实战篇:
1.为什么 Flutter 是跨平台开发的终极之选?
2.在 Windows 上搭建Flutter 开发环境
3.编写您的第一个 Flutter App
4.Flutter 开发环境搭建和调试
5.Dart 语法篇之基础语法(一)
6.Dart 语法篇之集合的使用与源码解析(二)
7.Dart 语法篇之集合操作符函数与源码分析(三)
8.Dart 语法篇之集合操作符函数与源码分析(三)
9.Dart 语法篇之面向对象基础(五)
10.Dart 语法篇之面向对象继承和Mixins(六)
对于Android开发的朋友来说应该是最全面最完整的Flutter学习笔记,为了更好地整理每个模块,我也参考了很多网上的优质博文和项目,力求不漏掉每一个知识点,很多朋友靠着这些内容进行复习,拿到了BATJ等大厂的offer,希望也能帮助到你。
还有2019-2020BAT 面试真题解析,里面内容很多也很系统,包含了很多内容:Android 基础、Java 基础、Android 源码相关分析、常见的一些原理性问题等等,可以很好地帮助我们深刻理解Android相关知识点的原理以及面试相关知识。
《960全网最全Android开发笔记》
《379页Android开发面试宝典》
《507页Android开发相关源码解析》
资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图;
需要的朋友,直接转发+点赞+私信回复【资料】一键领取!!!
相关推荐
- 台积电提出SRAM存内计算新方法,能效比可达89TOPS/W
-
芯东西(公众号:aichip001)编译|高歌编辑|云鹏芯东西3月16日消息,近期,台积电的研究人员在ISSCC2021会议上公布了一种改良的SRAM存储器阵列,该SRAM阵列采用22nm工...
- Golang中如何判断两个slice是否相等?
-
在Golang中,要判断两个slice是否相等是不能直接使用==运算符的(==只能说明两个slice是否指向同一个底层数组)。如果两个slice的底层数组相同,但长度或容量不同...
- JS入门基础知识(js基础知识总结笔记)
-
JS对象操作对象增删改查创建对象letobj={}新增属性obj.a=1修改属性obj.a='a'...
- 趣谈JS二进制:File、Blob、FileReader、ArrayBuffer、Base64
-
大家好,我是Echa。好久没跟粉丝们细聊JavaScript那点事了。做一名全栈工程师,JS基础还是要打牢,这样的话不管底层业务逻辑以及第三方框架怎么变化,都离不开基础。本文文章属于基础篇,阅读有点...
- 告别 substr() 和 substring()?更可靠的 JavaScript 字符串截取方法
-
JavaScript提供了三个主要的字符串截取方法:...
- golang第九天,切片(slice)介绍(golang 切片作为参数)
-
什么是切片golang切片是对数组的抽象。go的数组长度不可改变,在特定场景中这样的集合就不太适用,go中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追...
- Go语言零到一:数组(go struct数组)
-
引言...
- 你说你熟悉Slice,这道slice题你能答对吗?
-
每当你花费大量时间使用某种特定工具时,深入了解它并了解如何高效地使用它是很值得的。...
- Python 3.14七大新特性总结:从t-string模板到GIL并发优化
-
Python3.14已进入测试阶段,根据PEP745发布计划,该版本已停止引入新功能,也就是说新特征就应该已经固定下来了。所以本文基于当前最新的beta2版本,深入分析了Python3.14中...
- Python 幕后:Python导入import的工作原理
-
更多互联网精彩资讯、工作效率提升关注【飞鱼在浪屿】(日更新)Python最容易被误解的方面其中之一是import。...
- Python元类实现自动化编程的正确姿势
-
元类是Python中用于创建类的类。通过元类机制,开发者可在运行时动态创建和修改类,为框架开发、设计模式实现和高级架构设计提供核心支持。在Python语言的高级特性中,元类占据着独特而重要的地位。作...
- Python字符串详解与示例(python字符串类型及操作)
-
艾瑞巴蒂字符串的干货来了,字符串是程序中最常见的数据类型之一,用来表示数据文本,下面就来介绍下字符串的特性,操作和方法,和一些示例来吧道友:1.字符串的创建在python中字符串可以永单引号(...
- 恕我直言!你对Python里的import一无所知
-
文章来源:https://mp.weixin.qq.com/s/4WAOU_Lzy651IE-2zZSFfQ原文作者:写代码的明哥...
- Python基础:字符串操作(python字符串的用法)
-
字符串是Python中最常用的数据类型之一,用于表示文本数据。我们将学习如何对字符串进行常见的操作,包括创建、访问、修改和处理字符串。通过掌握这些技巧,您将能够更好地处理和操作文本数据。让我们开始吧!...
- Python 中 字符串处理的高效方法,不允许你还不知道
-
以下是Python中字符串处理的高效方法...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
面试官:git pull是哪两个指令的组合?
-
git 执行pull错误如何撤销 git pull fail
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 之后本地代码被覆盖 解决方案
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git命令之pull git.pull
-
- 最近发表
-
- 台积电提出SRAM存内计算新方法,能效比可达89TOPS/W
- Golang中如何判断两个slice是否相等?
- JS入门基础知识(js基础知识总结笔记)
- 趣谈JS二进制:File、Blob、FileReader、ArrayBuffer、Base64
- 告别 substr() 和 substring()?更可靠的 JavaScript 字符串截取方法
- golang第九天,切片(slice)介绍(golang 切片作为参数)
- Go语言零到一:数组(go struct数组)
- 你说你熟悉Slice,这道slice题你能答对吗?
- Python 3.14七大新特性总结:从t-string模板到GIL并发优化
- Python 幕后:Python导入import的工作原理
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)