百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

一位安卓程序员入坑Flutter后整理出一份超详细的学习笔记

wptr33 2024-12-05 17:00 26 浏览

作为忠实与较资深的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开发相关源码解析》

资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图;

需要的朋友,直接转发+点赞+私信回复【资料】一键领取!!!

相关推荐

MySQL进阶五之自动读写分离mysql-proxy

自动读写分离目前,大量现网用户的业务场景中存在读多写少、业务负载无法预测等情况,在有大量读请求的应用场景下,单个实例可能无法承受读取压力,甚至会对业务产生影响。为了实现读取能力的弹性扩展,分担数据库压...

Postgres vs MySQL_vs2022连接mysql数据库

...

3分钟短文 | Laravel SQL筛选两个日期之间的记录,怎么写?

引言今天说一个细分的需求,在模型中,或者使用laravel提供的EloquentORM功能,构造查询语句时,返回位于两个指定的日期之间的条目。应该怎么写?本文通过几个例子,为大家梳理一下。学习时...

一文由浅入深带你完全掌握MySQL的锁机制原理与应用

本文将跟大家聊聊InnoDB的锁。本文比较长,包括一条SQL是如何加锁的,一些加锁规则、如何分析和解决死锁问题等内容,建议耐心读完,肯定对大家有帮助的。为什么需要加锁呢?...

验证Mysql中联合索引的最左匹配原则

后端面试中一定是必问mysql的,在以往的面试中好几个面试官都反馈我Mysql基础不行,今天来着重复习一下自己的弱点知识。在Mysql调优中索引优化又是非常重要的方法,不管公司的大小只要后端项目中用到...

MySQL索引解析(联合索引/最左前缀/覆盖索引/索引下推)

目录1.索引基础...

你会看 MySQL 的执行计划(EXPLAIN)吗?

SQL执行太慢怎么办?我们通常会使用EXPLAIN命令来查看SQL的执行计划,然后根据执行计划找出问题所在并进行优化。用法简介...

MySQL 从入门到精通(四)之索引结构

索引概述索引(index),是帮助MySQL高效获取数据的数据结构(有序),在数据之外,数据库系统还维护者满足特定查询算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构...

mysql总结——面试中最常问到的知识点

mysql作为开源数据库中的榜一大哥,一直是面试官们考察的重中之重。今天,我们来总结一下mysql的知识点,供大家复习参照,看完这些知识点,再加上一些边角细节,基本上能够应付大多mysql相关面试了(...

mysql总结——面试中最常问到的知识点(2)

首先我们回顾一下上篇内容,主要复习了索引,事务,锁,以及SQL优化的工具。本篇文章接着写后面的内容。性能优化索引优化,SQL中索引的相关优化主要有以下几个方面:最好是全匹配。如果是联合索引的话,遵循最...

MySQL基础全知全解!超详细无废话!轻松上手~

本期内容提醒:全篇2300+字,篇幅较长,可搭配饭菜一同“食”用,全篇无废话(除了这句),干货满满,可收藏供后期反复观看。注:MySQL中语法不区分大小写,本篇中...

深入剖析 MySQL 中的锁机制原理_mysql 锁详解

在互联网软件开发领域,MySQL作为一款广泛应用的关系型数据库管理系统,其锁机制在保障数据一致性和实现并发控制方面扮演着举足轻重的角色。对于互联网软件开发人员而言,深入理解MySQL的锁机制原理...

Java 与 MySQL 性能优化:MySQL分区表设计与性能优化全解析

引言在数据库管理领域,随着数据量的不断增长,如何高效地管理和操作数据成为了一个关键问题。MySQL分区表作为一种有效的数据管理技术,能够将大型表划分为多个更小、更易管理的分区,从而提升数据库的性能和可...

MySQL基础篇:DQL数据查询操作_mysql 查

一、基础查询DQL基础查询语法SELECT字段列表FROM表名列表WHERE条件列表GROUPBY分组字段列表HAVING分组后条件列表ORDERBY排序字段列表LIMIT...

MySql:索引的基本使用_mysql索引的使用和原理

一、索引基础概念1.什么是索引?索引是数据库表的特殊数据结构(通常是B+树),用于...