算是flutter最完整题库了

13,093 阅读36分钟

来自某校试题,所以前面的会比较简单,后面的会比较难,主要想测出学生的真实水平,只是部分,一共153题;

多选题

1、下面哪些组件具备居中功能?(ABC)

A、Container B、Center C、Align D、Card

2、Textfield需要设置哪些属性才能实现无限换行和扩展高度?(AD)

A、expands B、maxLength C、textAlign D、maxLines

3、如何让图片重复堆叠容器?(C)

A、fit: BoxFit.fill B、fit: BoxFit.cover C、repeat: ImageRepeat.repeat

4、Text组件有哪些属性不能为空?(A)

A、data B、style C、overflow D、maxLines

5、哪些组件可以使用属性控制child的显隐(CD)?

A、Card B、Center C、Offstage D、Visibility

6、下面对StatefulWidget中createState方法描述正确的是?(ABC)

A、在树中的给定位置为此小部件创建可变状态;

B、框架可以在整个生命周期中多次调用此方法;

C、如果将小部件从树中删除,并且稍后再次插入树中,框架将调用createState再次创建一个新的State对象,从而简化了State对象;

D、用来看的,没啥实际作用;

7、下面哪些是正确调用setstate的姿势?(AC)

A、在点击某按钮时触发进行刷新类实例变量;

B、在点击某按钮时触发进行刷新方法实例变量;

C、使用前先判断mounted是否为true

D、dispose生命周期中调用;

8、让AppBar的标题居中的方式有(AB)

A、centerTitle属性设置为true

B、Center组件包裹title

C、elevation设置为0

D、primary设置为false

9、controller是一个动画控制器,如果controller声明的时候为空了,使用controller?.forward()播放动画时会发生什么?(CD)

A、一片红的报错信息;

B、报错,但不影响布局;

C、不会报错;

D、因为是?.的调用,左边为空了则不会去调用播放方法;

10、在有状态类中编写一个按钮调用初始化生命周期(initState)方法,会发生什么?(BD)

A、一片红的报错信息;

B、报错,但不影响布局;

C、不会报错;

D、会报生命周期创建错误;

11、写布局时用ListView然后children并没有写间隔之类的,结果出现了上下有间距,为什么?(AB)

A、可能是ListView外层写了间隔之类的;

B、可能是ListView自带Padding

C、肯定是官方的问题;

D、人的问题,换个人来运行可能就好了;

12、属于滑动组件的有(AB)

A、ListView B、GridView C、Container D、Center

13、属于Sliver系列的组件有(ABC)

A、SliverToBoxAdapter

B、SliverAppBar

C、SliverAnimatedOpacity

D、Image

14、属于不可滑动的组件有(ACD)

A、Text B、SingleChildScrollView C、Card D、FlatButton

15、能操作对齐方式(Alignment)的组件有(CD)

A、FlatButton B、RaisedButton C、Align D、Container

16、当我给变量赋值的时候想为空的时候才赋值应该怎么操作(ABC)

A、使用??=来指定当左边为空时才执行等于;

B、使用三元运算符判断是否等于空才执行赋值;

C、使用if判断语句,为空时才等于;

D、直接使用等于来赋值;

17、去掉按钮水波纹效果的方式有(ABC)

A、全局设置水波纹颜色为透明;

B、局部设置水波纹颜色为透明然后包裹需要透明的组件;

C、使用按钮的水波纹颜色属性设置为透明;

D、发呆就对了,阿巴阿吧阿吧;

18、在什么情况下类可以混入TickerProviderStateMixin?(CD)

A、用手写代码的情况下;

B、在不摸鱼的情况下;

C、混入TickerProviderStateMixin必须是继承State的情况下;

D、具备有状态类的生命周期下;

19、在Android和IOS情况下ListView的滑动表现描述正确的是(AC)

A、每个平台都有判断,效果会出现不同点;

B、Android和IOS怎么滑效果都一样;

C、Android滑动到无法滑动之后会出现焦点特殊动画,而IOS是回弹效果;

D、我又没滑过,我懒个晓得咯;

20、容器设置了约束盒子最小宽度为200,但内部的宽度设置为了50,则会发生(A)

A、以约束盒子的200为准;

B、以内部设置的50为准;

C、会造成混乱,导致无法显示;

D、有时候会200,有时候会50

赋值操作符

1、AA ?? “999”表示:AA如果为空,则显示999

2、AA ??= “999”表示:AA如果为空,则给值设置为999

3、AA ~/999表示:AA对于999整除

4、A?.a表示:A如果为空则不调用,如果不为空则调用a值

5、A?.a??”999”表示:A如果为空则不调用,如果不为空则调用a值,然后a值为空则显示999

判断题

1、WidgetsFlutterBinding.ensureInitialized在runApp之后调用(错);
2、MaterialApp中router内有“/”可以和home属性共存(错);
3、构造方法中“Key key”必写(错);
4、“=>”操作符是大括号的简写方式(对);
5、使用overlay写全局提示框不需要上下文(错);
6、MaterialApp组件中home属性有时可以为空(对);
7、setState必须是在有状态类中调用(对);
8、final修饰的变量无法被setState刷新(对);
9、方法内声明的变量可以被setState刷新(错);
10、NestedScrollView的内控制器可以从树上找(对);
11、约束盒子最小宽度为10,包裹容器设置宽度为5,结果为5(错);
12、ListView的children写容器可以直接设置容器宽度并生效(错);
13、在initState生命周期可以拿到上下文干和build生命周期一样的事(错);
14、Scaffold的左右抽屉可以并存(对);
15、整AppBar高度是由状态栏高度加kToolbarHeight组成的(对);
16、媒体查询是获取上下文宽高数值的方法(错);
17、SizedBox.expand内宽和高都是无限值(对);
18、Container使用了decoration属性外面还可以设置color属性值(错);
19、一个App项目存在多个MaterialApp组件是合理的(错);
20、Platform.isAndroid可以在web平台中判断当前是什么系统(错);
21、showDialog是一个组件(错);
22、CupertinoAlertDialog只能在CupertinoApp情况下使用(错);
23、Divider组件其实就是SizedBox+Center+Container组合出来的(对);
24、ScrollConfiguration组件可以自定义behavior然后去掉滑动的焦点(对);
25、PageView有自动轮播功能(错);
26、debugShowCheckedModeBanner属性false可以去掉右上DEBUG标签(对);
27、AnimatedList中key是必填的(错);
28、AnimatedSwitcher中child属性key可以不填但可能没效果(对);
29、Spacer组件可以放在任何可以放组件的地方(错);
30、容器宽高未知的情况下可以使用Expanded组件(错);
31、BottomNavigationBar具备保存页面状态功能(错);
32、Card组件不会默认带阴影效果(错);
32、Center组件可以让child位于容器下方(错);
33、LinearProgressIndicator可以直接设置Color类型的valueColor(错);
34、VerticalDivider和Divider用法一样,前者垂直分割线,后者水平分割线(对);
35、RaisedButton中onPressed为null或不设置时,按钮是禁用状态(对);
36、BackButton组件默认Android和IOS效果不一致(对);
37、CupertinoButton只能在IOS风格App下使用(错);
38、Hero飞行动画无需用到tag(错);
39、Row中放Textfield无需知道Textfield所占宽度(错);
40、TabBar只能放在AppBar的bottom属性(错);

问答题

1、说下Flutter的优缺点

优点:1、高性能,2、高保真,3、相对易学,4、热重载;

缺点:1、不支持热更新,2、生态需完善;

2、Flutter跨平台原理是怎么样的?

自写UI渲染引擎实现跨平台

3、说下移动端跨平台技术划分

1、web技术,2、原生渲染,3、自渲染技术;

4、简单的说下移动端跨平台技术演进

A:

5、蓝湖设计图有一张轮播图,宽度是 335 高度是 120,左右间隔是10, 如何使用屏幕算法适配全机型屏幕宽和高?

分析:
  • 左右间隔:设置margin然后左右10个间隔;
  • 宽度:整宽减2020就是左右的间隔;
  • 高度:(宽度) * 120 / 335;
代码:
new Container(
  height: (winWidth(context) - 20) * 120 / 335,
  width: winWidth(context) - 20,
  margin: EdgeInsets.symmetric(horizontal: 10.0),
  alignment: Alignment.center,
  decoration: BoxDecoration(
    borderRadius: BorderRadius.all(Radius.circular(4.0)),
    color: Colors.amber.withOpacity(0.5),
  ),
  child: new Text('模拟图片'),
),
效果

6、未知数据数量有规则的列表视图,要求一行显示5个,每个间隔为10(含上下),最外边距margin左右都为20,高度为50,多出的数据继续往下排并向左对齐,适配任何机型,怎么做?

分析:
  • 左右间隔:设置margin然后左右20个间隔;
  • 间隔和高:除最外边左右,内边都为10间隔,并包含上下,高度固定50
  • 对齐方式:对齐方式默认都为向左对齐;
  • 组件:推荐Wrap,动态数据,依次撑开;
代码:
class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Flutter高级进阶'),
      ),
      body: new Container(
        padding: EdgeInsets.symmetric(vertical: 20.0),// 为了保持美观给了上下价格20
        color: Colors.amber.withOpacity(0.2), // 为了验证动态撑开给了背景
        child: TestRoute(),// 主代码
      ),
    );
  }
}

class TestRoute extends StatefulWidget {
  @override
  _TestRouteState createState() => _TestRouteState();
}

class _TestRouteState extends State<TestRoute> {
  Widget buildItem(item) {
    return new Container(
      decoration: BoxDecoration(
        borderRadius: BorderRadius.all(Radius.circular(4.0)), // 圆角
        color: Colors.blueAccent.withOpacity(0.5),// item颜色
      ),
      height: 50.0, // 高度
      alignment: Alignment.center, // item文本剧中
      width: (winWidth(context) - 80) / 5, // 宽度
      child: new Text('模拟'),
    );
  }

  @override
  Widget build(BuildContext context) {
    return new Container(
      width: winWidth(context) - 40, // 宽度容器算法
      margin: EdgeInsets.symmetric(horizontal: 20.0),
      child: new Wrap(
        spacing: 10.0,
        runSpacing: 10.0,
        children: [0, 1, 2, 3, 4, 5].map(buildItem).toList(),
      ),
    );
  }
}
效果

7、如何实现点击空白区域收起键盘?

触发代码:FocusScope.of(context).requestFocus(new FocusNode());

8、无需上下文进行路由跳转原理是怎么样的?

使用GlobalKey调用到NavigatorState的方法;

9、为什么我的容器无论设置宽高多少都是占整个的宽高?怎么解决?

1.被约束了;2.宽高无限了;

解决方式:

使用UnconstrainedBox包裹容器;

10、键盘弹出时底部溢出如何解决?

1.溢出部分增加滑动属性; 2.ScaffoldresizeToAvoidBottomPadding: false让其遮挡布局;

11、Container 设置 borderRadius 不生效怎么解决?如何导致的?

原因:Container 设置 borderRadius只对当前盒子生效;

解决方式:使用裁剪方式,如:ClipRRect组件;

12、GestureDetector 设置 onTap 不生效怎么解决?

使用GestureDetectorbehavior: HitTestBehavior.translucent

13、如何实现动态更改 TabBar 长度?

1.控制器重新赋值;(使用Ticker不能是Single的)

2.使用DefaultTabController

14、为何写多个动画时动画控制器使用了Ticker,类也混入了Ticker运行报错了?

有可能使用了单一的Ticker(SingleTickerProviderStateMixin),只能使用一次就失效了;

15、如何实现键盘弹出后遮住布局,而不是顶起布局?

Scaffold属性resizeToAvoidBottomPadding: false让其遮挡布局;

16、为何输入框输入内容之后返回到桌面,再进入app时内容被清空了?怎么解决?

可能是输入中没有做保存处理,可以使用生命周期判断,在程序暂停前让输入框取消焦点即可实现自动保存;

17、为何本地资源图片刚进入的时候切换到另一张出现白屏?怎么解决?

原因:切换之后才开始解析本地资源图片;

解决方案:在初始化的时候就加载指定asset图片,而不是在需要展示的时候才开始加载。

代码示例:

@override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) async {
     // _imageUrls 就是数组的Asset图片地址
      _imageUrls.forEach((image) {
        precacheImage(AssetImage(image), context);
      });
    });
  }

18、如何拦截 App 返回事件,用什么组件?

WillPopScope组件,用返回的bool来操作是否允许返回;

19、如何监听 App 暂停运行或不可见状态事件?

使用WidgetsBindingObserver观察生命周期状态;

具体:book.flutterj.com/chapter1/li…

20、Text 的 TextOverflow.ellipsis 不生效如何解决?

Text组件的所占宽度可知;

21、如何获取控件的大小和位置?

1、使用Key拿到上下文取得findRenderObject拿内容的尺寸数据;

2、使用context取得findRenderObject拿内容的尺寸数据;

22、类构造方法后面加个super表示什么意思?

调用父类的属性,可进行赋值传输;

23、assert( data != null, ‘no data’)是什么意思?

assert:断言;

data != null:data不能为空,否则触发断言错误;

no data:如果触发断言则提示的内容;

24、const修饰构造函数和放声明数值前分别有什么作用?

构造函数前:构造函数会在编译期和常量一起被编译;

声明数值前:一个不可变的常量,编译期就被初始化;

25、描述下getter setter和重写

Dart 中所有的基础类型、类等都继承 Object ,默认值是 NULL, 自带 gettersetter ,而如果是 final 或者 const 的话,那么它只有一个 getter 方法,Object 都支持 gettersetter 重写

26、Assert(断言)有什么作用?什么时候有效?

debug的时候提示出断言错误让开发者知悉,只在debug有效;

Dart相关

1、Dart 当中的 「..」表示什么意思?

级连操作符

“..” 和 “.” 不同:调用..后返回的相当于是this,而.返回的则是该方法返回的值;

2、Dart 的作用域是怎么样的?

Dart没有publicprivate等关键词,默认就是公开的,私有变量使用下划线开头;

4、dart是多线程还是单线程执行?

单线程执行,多线程是使用异步来执行的;

5、阻塞式调用和非阻塞式调用是怎么样的?

阻塞:调用结果之前,当前线程会被挂起,调用线程只有在得到结果之后才会继续执行;

非阻塞:调用执行之后,当前线程不会停止运行,只需要过一段时间来检查有没有结果返回即可;

6、事件循环是什么?

将需要处理的一系列事件,放在一个事件队列(Event Queue)中,不断从事件队列中取出事件,并执行需要执行的代码块,直到事件被清空。

7、dart是值传递还是引用传递?

dart是值传递。我们每次调用函数,传递过去的都是对象的内存地址,而不是这个对象的复制。

8、Dart 语言有哪些重要的特性?

  • Productive(生产力高,Dart的语法清晰明了,工具简单但功能强大)

  • Fast(执行速度快,Dart提供提前优化编译,以在移动设备和Web上获得可预测的高性能和快速启动。)

  • Portable(易于移植,Dart可编译成ARM和X86代码,这样Dart移动应用程序可以在iOS、Android和其他地方运行)

  • Approachable(容易上手,充分吸收了高级语言特性,如果你已经知道C++,C语言,或者Java,你可以在短短几天内用Dart来开发)

  • Reactive(响应式编程)

9、Dart 语言有哪些重要的概念?

  • 在Dart中,一切都是对象,所有的对象都是继承自Object

  • Dart是强类型语言,但可以用var或 dynamic来声明一个变量,Dart会自动推断其数据类型,dynamic类似c#

  • 没有赋初值的变量都会有默认值null

  • Dart支持顶层方法,如main方法,可以在方法内部创建方法

  • Dart支持顶层变量,也支持类变量或对象变量

  • Dart没有public protected private等关键字,如果某个变量以下划线(_)开头,代表这个变量在库中是私有的

10、Dart线程模型是如何执行的?

Dart 是单线程模型,运行的的流程如下图。

Dart 在单线程中是以消息循环机制来运行的,包含两个任务队列,一个是“微任务队列” microtask queue,另一个叫做“事件队列” event queue。 当Flutter应用启动后,消息循环机制便启动了。首先会按照先进先出的顺序逐个执行微任务队列中的任务,当所有微任务队列执行完后便开始执行事件队列中的任务,事件任务执行完毕后再去执行微任务,如此循环往复,生生不息。

11、Dart 是如何实现多任务并行的?

Dart 是单线程的,不存在多线程,那如何进行多任务并行的呢?其实,Dart的多线程和前端的多线程有很多的相似之处。Flutter的多线程主要依赖Dart的并发编程、异步和事件驱动机制。

简单的说,在Dart中,一个Isolate对象其实就是一个isolate执行环境的引用,一般来说我们都是通过当前的isolate去控制其他的isolate完成彼此之间的交互,而当我们想要创建一个新的Isolate可以使用Isolate.spawn方法获取返回的一个新的isolate对象,两个isolate之间使用SendPort相互发送消息,而isolate中也存在了一个与之对应的ReceivePort接受消息用来处理,但是我们需要注意的是,ReceivePortSendPort在每个isolate都有一对,只有同一个isolate中的ReceivePort才能接受到当前类的SendPort发送的消息并且处理。

12、await for 如何使用?

await for是不断获取stream流中的数据,然后执行循环体中的操作。它一般用在直到stream什么时候完成,并且必须等待传递完成之后才能使用,不然就会一直阻塞。

13、Stream 有哪两种订阅模式?分别是怎么调用的?

单订阅(single)和多订阅(broadcast)。

单订阅就是只能有一个订阅者,而广播是可以有多个订阅者。这就有点类似于消息服务(Message Service)的处理模式。单订阅类似于点对点,在订阅者出现之前会持有数据,在订阅者出现之后就才转交给它。而广播类似于发布订阅模式,可以同时有多个订阅者,当有数据时就会传递给所有的订阅者,而不管当前是否已有订阅者存在。 Stream 默认处于单订阅模式,所以同一个 stream 上的 listen 和其它大多数方法只能调用一次,调用第二次就会报错。但 Stream 可以通过 transform() 方法(返回另一个 Stream)进行连续调用。通过 Stream.asBroadcastStream() 可以将一个单订阅模式的 Stream 转换成一个多订阅模式的 StreamisBroadcast 属性可以判断当前 Stream 所处的模式。

14、dart中mixin机制是怎么样的?

mixin 是Dart 2.1 加入的特性,以前版本通常使用abstract class代替。简单来说,mixin是为了解决继承方面的问题而引入的机制,Dart为了支持多重继承,引入了mixin关键字,它最大的特殊处在于:mixin定义的类不能有构造方法,这样可以避免继承多个类而产生的父类构造方法冲突。 mixins的对象是类,mixins绝不是继承,也不是接口,而是一种全新的特性,可以mixins多个类,mixins的使用需要满足一定条件。

15、JIT 与 AOT分别是什么?

借助于先进的工具链和编译器,Dart 是少数同时支持 JIT(Just In Time,即时编译)和 AOT(Ahead of Time,运行前编译)的语言之一。那,到底什么是 JIT 和 AOT 呢?语言在运行之前通常都需要编译,JIT 和 AOT 则是最常见的两种编译模式。JIT 在运行时即时编译,在开发周期中使用,可以动态下发和执行代码,开发测试效率高,但运行速度和执行性能则会因为运行时即时编译受到影响。AOT 即提前编译,可以生成被直接执行的二进制代码,运行速度快、执行性能表现好,但每次执行前都需要提前编译,开发测试效率低。

16、Dart的内存分配与垃圾回收是怎么样的?

Dart VM 的内存分配策略比较简单,创建对象时只需要在堆上移动指针,内存增长始终是线性的,省去了查找可用内存的过程。在 Dart 中,并发是通过 Isolate 实现的。Isolate 是类似于线程但不共享内存,独立运行的 worker。这样的机制,就可以让 Dart 实现无锁的快速分配。Dart 的垃圾回收,则是采用了多生代算法。新生代在回收内存时采用“半空间”机制,触发垃圾回收时,Dart 会将当前半空间中的“活跃”对象拷贝到备用空间,然后整体释放当前空间的所有内存。回收过程中,Dart 只需要操作少量的“活跃”对象,没有引用的大量“死亡”对象则被忽略,这样的回收机制很适合 Flutter 框架中大量 Widget 销毁重建的场景。

17、使用mixins的条件是什么?

因为mixins使用的条件,随着Dart版本一直在变,这里讲的是Dart2.1中使用mixins的条件:

  • mixins类只能继承自object mixins类不能有构造函数

  • 一个类可以mixins多个mixins

  • 可以mixins多个类,不破坏Flutter的单继承

18、mixin 怎么指定异常类型?

on关键字可用于指定异常类型。 on只能用于被mixins标记的类,例如mixins X on A,意思是要mixins X的话,得先接口实现或者继承A。这里A可以是类,也可以是接口,但是在mixins的时候用法有区别.

on 一个类:

on 的是一个接口: 得首先实现这个接口,然后再用mix

19、main future mirotask 的执行顺序是怎样的?

普通代码都是同步执行的,结束后会开始检查microtask中是否有任务,若有则执行,执行完继续检查microtask,直到microtask列队为空。最后会去执行event队列(future)。

20、Future和Isolate有什么区别?

future是异步编程,调用本身立即返回,并在稍后的某个时候执行完成时再获得返回结果。在普通代码中可以使用await 等待一个异步调用结束。 isolate是并发编程,Dart有并发时的共享状态,所有Dart代码都在isolate中运行,包括最初的main()。每个isolate都有它自己的堆内存,意味着其中所有内存数据,包括全局数据,都仅对该isolate可见,它们之间的通信只能通过传递消息的机制完成,消息则通过端口(port)收发。isolate只是一个概念,具体取决于如何实现,比如在Dart VM中一个isolate可能会是一个线程,在Web中可能会是一个Web Worker

21、Stream 与 Future是什么关系?

StreamFuture 是 Dart 异步处理的核心 API。Future 表示稍后获得的一个数据,所有异步的操作的返回值都用 Future 来表示。但是 Future 只能表示一次异步获得的数据。而 Stream 表示多次异步获得的数据。比如界面上的按钮可能会被用户点击多次,所以按钮上的点击事件(onClick)就是一个 Stream 。简单地说,Future将返回一个值,而Stream将返回多次值。Dart 中统一使用 Stream 处理异步事件流。Stream 和一般的集合类似,都是一组数据,只不过一个是异步推送,一个是同步拉取。

Flutter

1、介绍下Flutter的FrameWork层和Engine层,以及它们的作用

Flutter的FrameWork层是用Dart编写的框架(SDK),它实现了一套基础库,包含Material(Android风格UI)和Cupertino(iOS风格)的UI界面,下面是通用的Widgets(组件),之后是一些动画、绘制、渲染、手势库等。这个纯 Dart实现的 SDK被封装为了一个叫作 dart:ui的 Dart库。我们在使用 Flutter写 App的时候,直接导入这个库即可使用组件等功能。 Flutter的Engine层是Skia 2D的绘图引擎库,其前身是一个向量绘图软件,Chrome和 Android均采用 Skia作为绘图引擎。Skia提供了非常友好的 API,并且在图形转换、文字渲染、位图渲染方面都提供了友好、高效的表现。Skia是跨平台的,所以可以被嵌入到 Flutter的 iOS SDK中,而不用去研究 iOS闭源的 Core Graphics / Core Animation。Android自带了 Skia,所以 Flutter Android SDK要比 iOS SDK小很多。

2、介绍下Widget、State、Context 概念

  • Widget:在Flutter中,几乎所有东西都是Widget。将一个Widget想象为一个可视化的组件(或与应用可视化方面交互的组件),当你需要构建与布局直接或间接相关的任何内容时,你正在使用Widget
  • Widget树:Widget以树结构进行组织。包含其他Widgetwidget被称为父Widget(或widget容器)。包含在父widget中的widget被称为子Widget
  • Context:仅仅是已创建的所有Widget树结构中的某个Widget的位置引用。简而言之,将context作为widget树的一部分,其中context所对应的widget被添加到此树中。一个context只从属于一个widget,它和widget一样是链接在一起的,并且会形成一个context树。
  • State:定义了StatefulWidget实例的行为,它包含了用于”交互/干预“Widget信息的行为和布局。应用于State的任何更改都会强制重建Widget

3、介绍下StatelessWidget和StatefulWidget两种状态组件类

  • StatelessWidget: 一旦创建就不关心任何变化,在下次构建之前都不会改变。它们除了依赖于自身的配置信息(在父节点构建时提供)外不再依赖于任何其他信息。比如典型的TextRowColumnContainer等,都是StatelessWidget。它的生命周期相当简单:初始化、通过build()渲染。
  • StatefulWidget: 在生命周期内,该类Widget所持有的数据可能会发生变化,这样的数据被称为State,这些拥有动态内部数据的Widget被称为StatefulWidget。比如复选框、Button等。State会与Context相关联,并且此关联是永久性的,State对象将永远不会改变其Context,即使可以在树结构周围移动,也仍将与该context相关联。当statecontext关联时,state被视为已挂载。StatefulWidget由两部分组成,在初始化时必须要在createState()时初始化一个与之相关的State对象。

4、StatefulWidget 的生命周期是怎么样的?

Flutter的Widget分为StatelessWidgetStatefulWidget两种。其中,StatelessWidget是无状态的,StatefulWidget是有状态的,因此实际使用时,更多的是StatefulWidgetStatefulWidget的生命周期如下图

  • initState()Widget 初始化当前 State,在当前方法中是不能获取到 Context 的,如想获取,可以试试 Future.delayed()
  • didChangeDependencies():在 initState() 后调用,State对象依赖关系发生变化的时候也会调用。
  • deactivate():当 State 被暂时从视图树中移除时会调用这个方法,页面切换时也会调用该方法,和Android里的 onPause 差不多。
  • dispose()Widget 销毁时调用。 didUpdateWidgetWidget 状态发生变化的时候调用。

5、说下Widgets、RenderObjects 和 Elements的关系

首先看一下这几个对象的含义及作用。

  • Widget :仅用于存储渲染所需要的信息。
  • RenderObject :负责管理布局、绘制等操作。
  • Element :才是这颗巨大的控件树上的实体。

Widget会被inflate(填充)Element,并由Element管理底层渲染树。Widget并不会直接管理状态及渲染,而是通过State这个对象来管理状态。Flutter创建Element的可见树,相对于Widget来说,是可变的,通常界面开发中,我们不用直接操作Element,而是由框架层实现内部逻辑。就如一个UI视图树中,可能包含有多个TextWidget(Widget被使用多次),但是放在内部视图树的视角,这些TextWidget都是填充到一个个独立的Element中。Element会持有renderObjectwidget的实例。记住,Widget 只是一个配置,RenderObject 负责管理布局、绘制等操作。 在第一次创建 Widget 的时候,会对应创建一个 Element, 然后将该元素插入树中。如果之后 Widget 发生了变化,则将其与旧的 Widget 进行比较,并且相应地更新 Element。重要的是,Element 不会被重建,只是更新而已。

6、Flutter 是如何与原生Android、iOS进行通信的?

Flutter 通过 PlatformChannel 与原生进行交互,其中 PlatformChannel 分为三种:

  • BasicMessageChannel:用于传递字符串和半结构化的信息。
  • MethodChannel:用于传递方法调用。Flutter主动调用Native的方法,并获取相应的返回值。
  • EventChannel:用于数据流(event streams)的通信。

关于原理:www.jianshu.com/p/39575a90e…

7、简述下Flutter 的热重载

Flutter 的热重载是基于 JIT 编译模式的代码增量同步。由于 JIT 属于动态编译,能够将 Dart 代码编译成生成中间代码,让 Dart VM 在运行时解释执行,因此可以通过动态更新中间代码实现增量同步。

热重载的流程可以分为 5 步,包括:扫描工程改动、增量编译、推送更新、代码合并、Widget 重建。

Flutter 在接收到代码变更后,并不会让 App 重新启动执行,而只会触发 Widget 树的重新绘制,因此可以保持改动前的状态,大大缩短了从代码修改到看到修改产生的变化之间所需要的时间。

另一方面,由于涉及到状态的保存与恢复,涉及状态兼容与状态初始化的场景,热重载是无法支持的,如改动前后 Widget 状态无法兼容、全局变量与静态属性的更改、main 方法里的更改、initState 方法里的更改、枚举和泛型的更改等。

可以发现,热重载提高了调试 UI 的效率,非常适合写界面样式这样需要反复查看修改效果的场景。但由于其状态保存的机制所限,热重载本身也有一些无法支持的边界。

8、说下Flutter 和其他跨平台方案的本质区别

React Native 之类的框架,只是通过 JavaScript 虚拟机扩展调用系统组件,由 Android 和 iOS 系统进行组件的渲染;

Flutter 则是自己完成了组件渲染的闭环。那么,Flutter 是怎么完成组件渲染的呢?这需要从图像显示的基本原理说起。在计算机系统中,图像的显示需要 CPU、GPU 和显示器一起配合完成:CPU 负责图像数据计算,GPU 负责图像数据渲染,而显示器则负责最终图像显示。CPU 把计算好的、需要显示的内容交给 GPU,由 GPU 完成渲染后放入帧缓冲区,随后视频控制器根据垂直同步信号(VSync)以每秒 60 次的速度,从帧缓冲区读取帧数据交由显示器完成图像显示。操作系统在呈现图像时遵循了这种机制,而 Flutter 作为跨平台开发框架也采用了这种底层方案。下面有一张更为详尽的示意图来解释 Flutter 的绘制原理。

Flutter 绘制原理可以看到,Flutter 关注如何尽可能快地在两个硬件时钟的 VSync 信号之间计算并合成视图数据,然后通过 Skia 交给 GPU 渲染:UI 线程使用 Dart 来构建视图结构数据,这些数据会在 GPU 线程进行图层合成,随后交给 Skia 引擎加工成 GPU 数据,而这些数据会通过 OpenGL 最终提供给 GPU 渲染。

9、Widget 唯一标识Key有哪几种?

在flutter中,每个widget都是被唯一标识的。这个唯一标识在buildrendering阶段由框架定义。该标识对应于可选的Key参数,如果省略,Flutter将会自动生成一个。

在flutter中,主要有4种类型的KeyGlobalKey(确保生成的Key在整个应用中唯一,是很昂贵的,允许element在树周围移动或变更父节点而不会丢失状态)、LocalKeyUniqueKeyObjectKey

10、什么是Navigator? MaterialApp做了什么?

Navigator是在Flutter中负责管理维护页面堆栈的导航器。

MaterialApp在需要的时候,会自动为我们创建Navigator

Navigator.of(context),会使用context来向上遍历Element树,找到MaterialApp提供的_NavigatorState再调用其push/pop方法完成导航操作。

11、Flutter动画类型有哪些?

  • 补间动画:给定初值与终值,系统自动补齐中间帧的动画
  • 物理动画:遵循物理学定律的动画,实现了弹簧、阻尼、重力三种物理效果

在应用使用过程中常见动画模式:

  • 动画列表或者网格:例如元素的添加或者删除操作;
  • 转场动画Shared element transition:例如从当前页面打开另一页面的过渡动画;
  • 交错动画Staggered animations:比如部分或者完全交错的动画。

12、Flutter是怎么完成组件渲染的?

A:

13、Flutter绘制流程是怎么样的?

A:

14、如何统一管理错误页面?

main方法修改ErrorWidget.builder 来自定义一个属于自己的Widget

如:

  /// 自定义报错页面
  ErrorWidget.builder = (FlutterErrorDetails flutterErrorDetails) {
    debugPrint(flutterErrorDetails.toString());
    return new Center(child: new Text("App错误,快去反馈给作者!"));
  };

15、Flutter 中存在哪四大线程?

Flutter 中存在四大线程,分别为 UI RunnerGPU RunnerIO RunnerPlatform Runner (原生主线程) ,同时在 Flutter 中可以通过 isolate 或者 compute 执行真正的跨线程异步操作。

16、PlatformView的作用有哪些?

Flutter 中通过 PlatformView 可以嵌套原生 ViewFlutter UI 中;

17、PlatformView使用了哪些东西来实现?

PresentationVirtualDisplaySurface 等;

18、PlatformView大致原理是怎么样的?

使用了类似副屏显示的技术,VirtualDisplay 类代表一个虚拟显示器,调用 DisplayManagercreateVirtualDisplay() 方法,将虚拟显示器的内容渲染在一个 Surface 控件上,然后将 Surfaceid 通知给 Dart,让 engine 绘制时,在内存中找到对应的 Surface 画面内存数据,然后绘制出来。 实时控件截图渲染显示技术。

19、Flutter 的 Debug 和 release 分别是在什么模式下运行的?

Flutter 的 Debug 下是 JIT 模式,release下是AOT模式。

20、Platform Channel有哪几种通信方式?分别是用于什么操作?

  • BasicMessageChannel :用于传递字符串和半结构化的信息。
  • MethodChannel :用于传递方法调用(method invocation)。
  • EventChannel: 用于数据流(event streams)的通信。

21、RenderObject布局相关方法调用顺序是怎么样的?

layout -> performResize -> performLayout -> markNeedsPaint , 但是用户一般不会直接调用 layout,而是通过 markNeedsLayout ,具体流程如下:

22、RenderObject如何使得页面重绘?流程是怎么样的?

RenderObjectattch/layout 之后会通过 markNeedsPaint(); 使得页面重绘,流程大概如下:

通过isRepaintBoundary 往上确定了更新区域,通过 requestVisualUpdate 方法触发更新往下绘制。

23、Flutter存在哪几棵树?他们有什么关系?

Flutter 中存在 WidgetElementRenderObjectLayer 四棵树,其中 WidgetElement 是一对多的关系 ,

24、简述下Flutter的线程管理模型

默认情况下,Flutter Engine层会创建一个Isolate,并且Dart代码默认就运行在这个主Isolate上。必要时可以使用spawnUrispawn两种方式来创建新的Isolate,在Flutter中,新创建的Isolate由Flutter进行统一的管理。 事实上,Flutter Engine自己不创建和管理线程,Flutter Engine线程的创建和管理是Embeder负责的,Embeder指的是将引擎移植到平台的中间层代码,Flutter Engine层的架构示意图如下图所示。

在Flutter的架构中,Embeder提供四个Task Runner,分别是Platform Task RunnerUI Task Runner ThreadGPU Task RunnerIO Task Runner,每个Task Runner负责不同的任务,Flutter Engine不在乎Task Runner运行在哪个线程,但是它需要线程在整个生命周期里面保持稳定

状态管理 【来自老友:i校长

1、状态管理是什么?

程序=算法+数据结构 数据是程序的中心。数据结构和算法两个概念间的逻辑关系贯穿了整个程序世界,首先二者表现为不可分割的关系。其实Flutter不就是一个程序吗,那我们面临的最底层的问题还是算法和数据结构,所以我们推导出

Flutter=算法+数据结构 那状态管理是什么?我也用公式来表达一下,如下:

Flutter状态管理=算法+数据结构+UI绑定

2、为什么需要状态管理?

用于解决状态更新问题,不需要WidgetState 被全局化,保证组件隐私,使得代码可扩展,易维护,可以动态替换UI而不影响算法逻辑,安全可靠,保持数据的稳定伸缩,性能佳,局部优化;

3、说下状态管理基本分类

分为局部管理和全局管理;

  • 局部管理:短暂的状态,这种状态根本不需要做全局处理;
  • 全局管理:即应用状态,非短暂状态,您要在应用程序的许多部分之间共享,以及希望在用户会话之间保持的状态,就是我们所说的应用程序状态(有时也称为共享状态)

4、状态管理的底层逻辑一般是怎么样的?

  • State:如StatefulWidgetStreamBuilder状态管理方式;
  • InheritedWidget专门负责Widget树中数据共享的功能型Widget:如Providerscoped_model就是基于它开发;
  • Notification:与InheritedWidget正好相反,InheritedWidget是从上往下传递数据,Notification是从下往上,但两者都在自己的Widget树中传递,无法跨越树传递;
  • Stream 数据流 :如Blocflutter_reduxfish_redux等也都基于它来做实现;

5、状态管理的使用原则是怎么样的?

局部管理优于全局、保持数据安全性、考虑页面重新build带来的影响;

6、使用成熟状态管理库的弊端有哪些?

增加代码复杂性、框架bug修复需要时间等待、不理解框架原理导致使用方式不对,反而带来更多问题、选型错误导致不符合应用要求、与团队风格冲突不适用;

进阶

1、flutter run实际走了哪三个命令?分别用于什么操作?

  • flutter build apk:通过gradle来构建APK
  • adb install:安装APK
  • adb am start:启动应用

2、Flutter引擎启动过程中做了什么操作?

3、setState做了哪些工作?是如何更新UI的?

setState 其实是调用了 markNeedsBuild ,该方法内部标记此ElementDirty ,然后在下一帧 WidgetsBinding.drawFrame 才会被绘制, setState 并不是立即生效的。

4、Flutter应用启动runApp(MyApp)过程是怎么样的?

Flutter 中 runApp 启动入口其实是一个 WidgetsFlutterBinding ,它主要是通过 BindingBase 的子类 GestureBindingServicesBindingSchedulerBindingPaintingBindingSemanticsBindingRendererBindingWidgetsBinding 等,通过 mixins 的组合而成的。

5.Dart虚拟机如何管理的?怎么调用?如何跟Flutter引擎交互?

Dart虚拟机拥有自己的Isolate,完全由虚拟机自己管理的,Flutter引擎也无法直接访问。Dart的UI相关操作,是由Root Isolate通过Dart的C++调用,或者是发送消息通知的方式,将UI渲染相关的任务提交到UIRunner执行,这样就可以跟Flutter引擎相关模块进行交互。

6、Isolate组成部分有哪些?分别有什么作用?

  • isolate堆是运该isolate中代码分配的所有对象的GC管理的内存存储;
  • vm isolate是一个伪isolate,里面包含不可变对象,比如nulltruefalse
  • isolate堆能引用vm isolate堆中的对象,但vm isolate不能引用isolate堆;
  • isolate彼此之间不能相互引用 每个isolate都有一个执行dart代码的Mutator thread,一个处理虚拟机内部任务(比如GC, JIT等)的helper thread

7、线程和isolate的关系是什么?

1、同一个线程在同一时间只能进入一个isolate,当需要进入另一个isolate则必须先退出当前的isolate

2、一次只能有一个Mutator线程关联对应的isolateMutator线程是执行Dart代码并使用虚拟机的公共的C语言API的线程;

8、介绍下JIT运行模式中kernel service

是一个辅助类isolate,其核心工作就是CFE,将dart转为Kernel二进制,然后VM可直接使用Kernel二进制运行在主isolate里面运行。

9、介绍下Dart虚拟机中通过Snapshots运行

A:

10、介绍下Dart虚拟机中通过AppAOT Snapshots运行

A:

11、图片加载流程是怎么样的?

A:

12、简单的说下GestureDector底层实现

A:

13、setState在哪种场景下可能会失效?

1、刷新方法内声明的变量;

2、刷新被final修饰的变量;

14、isolate是怎么进行通信的?实例化过程是怎么样的?

isolate线程之间的通信主要通过port来进行,这个port消息传递过程是异步的。

实例化一个isolate的过程包括:

  • 1.实例化isolate结构体。
  • 2.在堆中分配线程内存。
  • 3.配置port等过程。

15、虚拟机如何运行Dart代码?

1.源码或者Kernel二进制(JIT)

2.snapshot :

  • AOT snapshot
  • AppJIT snapshot

16、JIT运行模式中debug运行原理是怎么样的?

将dart代码转换为kernel二进制和执行kernel二进制,这两个过程也可以分离开来,在两个不同的机器执行,比如host机器执行编译,移动设备执行kernel文件。

17.默认情况下debug和release会生成哪些架构的so库?

图解:

这个编译过程并不是flutter tools自身完成,而是交给另一个进程frontend_server来执行,它包括CFE和一些flutter专有的kernel转换器。 hot reload:热重载机制正是依赖这一点,frontend_server重用上一次编译中的CFE状态,只重新编译实际更改的部分。

结语

部分还在更新中,任何问题可以加入我们的Flutter微信交流群,加好友即可拉群;

Flutter交流QQ群:874592746