flutter一些简单的认识

本文详细介绍了Flutter开发中的关键概念,包括Dart语言的生产力特性、单线程模型、Future与Isolate的区别、Stream与Future的关系,以及本地数据存储方式如文件、SharedPreferences和SQLite。此外,文章还探讨了Flutter与原生应用的通信、键盘弹出高度问题、ListView错误解决以及错误处理策略。同时,讲解了Widget、Element和RenderObject的关系、Navigator的用途和状态管理。最后,讨论了Dart的异步处理、线程管理和热重载技术,以及Flutter的性能优化和优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.Fluttter的跨平台原理

Dart同时支持JIT(Just In Time,即时编译)和AOT(Ahead of Time,运行前编译)两种编译模式。
JIT 在运行时即时编译,在开发周期中使用,可以动态下发和执行代码,开发测试效率高,但运行速度和执行性能则会因为运行时即时编译受到影响。
AOT 即提前编译,可以生成被直接执行的二进制代码,运行速度快、执行性能表现好,但每次执行前都需要提前编译,开发测试效率低。

Flutter在开发期使用JIT编译,可以缩短产品的开发周期,例如热重载就是基于该特性。

在发布期使用AOT,具有运行速度快、执行性能好的特点。

2.Dart 的单线程模型是如何运行的?

Dart是单线程模型,不存在资源竞争和状态同步的问题。这就意味着,一旦某个函数开始执行,就将执行到这个函数结束,而不会被其他Dart代码打断。

Dart中并没有线程,只有Isolate(隔离区)。Isolates之前不会共享内存,就像几个运行在不同进程中的worker,通过事件循环(Event Looper)在事件队列(Event Queue)上传递消息通信。

3.Future和Isolate有什么区别?

future是异步编程,调用本身立即返回,并在稍后的某个时候执行完成时再获得返回结果。在普通代码中可以使用await 等待一个异步调用结束。

isolate是并发编程,Dartm有并发时的共享状态,所有Dart代码都在isolate中运行,包括最初的main()。每个isolate都有它自己的堆内存,意味着其中所有内存数据,包括全局数据,都仅对该isolate可见,它们之间的通信只能通过传递消息的机制完成,消息则通过端口(port)收发。isolate只是一个概念,具体取决于如何实现,比如在Dart VM中一个isolate可能会是一个线程,在Web中可能会是一个Web Worker。

4.Stream 与 Future是什么关系?

Stream 和 Future 是 Dart 异步处理的核心 API。

区别:

  • Future 只能表示一次异步获得的数据。而 Stream 表示多次异步获得的数据。
  • Future将返回一个值,而Stream将返回多次值。
  • Dart 中统一使用 Stream 处理异步事件流,用Future 表示稍后获得的一个数据。

5.本地的数据存储方式

1.文件存储

使用:在pubspec.yaml引入path_provider

2.SharePreferences

主要用于缓存少量的键值对信息,SharedPreferences 会以原生平台相关的机制,为简单的键值对数据提供持久化存储,即在 iOS 上使用 NSUserDefaults,在 Android 使用 SharedPreferences。需要注意的是,以键值对的方式只能存储基本类型的数据,比如 int、double、bool 和 string。

使用:在pubspec.yaml引入shared_preferences

3.Sqlite数据库

主要用于持久化大量格式化后的数据,并且这些数据还会以较高的频率更新。

与文件和 SharedPreferences 相比,数据库在数据读写上可以提供更快、更灵活。

使用:在pubspec.yaml引入sqlfite

6.与原生通讯方式

Flutter定义了三种不同类型的Channel,它们分别是:

BasicMessageChannel:用于传递字符串和半结构化的信息。(双向有返回值)

MethodChannel:用于传递方法调用(method invocation)通常用来调用 native 中某个方法,(双向有返回值)

EventChannel: 用于数据流(event streams)的通信。有监听功能,比如电量变化之后直接推送数据给flutter端。(仅支持数据单向传递,无返回值)

iOS原生和Flutter通信的三种方式,消息传递是异步的。

使用总结:

1.Platform Channel 的代码在Flutter app侧的代码运行在UI Task Runner 线程中,而在android 和ios平台上运行在主线程上。因此 ,不应该在Platform端的Handler 中处理耗时操作。

2.Platform Channel 并非线程安全的,因此在将Platform端的消息处理结果回传到Flutter端时,需要确保回调函数是在Platform Thread(就是Android 和IOS 的主线程)中执行。

3.Platform Channel 是支持大内存数据块的传递。当需要传递大内存数据块时,需要使用BasicMessageChannel以及BinaryCodec。

7.键盘弹出高度超出解决

Scaffold(
    resizeToAvoidBottomPadding: false, //输入框抵住键盘
)

8.ListView报Vertical viewport was given unbounded height错误解决方法

错误解析:宽度或高度溢出,导致Widget不显示

解决方法:给ListView的shrinkWrap属性设置为true,即父视图的大小跟随子组件的内容大小

return Column(
        children: <Widget>[
          ListView.builder(
              itemCount: sourceDataComments.length,
              itemBuilder: _buildCommentsRow,
              shrinkWrap: true,
          ),
        ],
      );

9.Flutter报setState() or markNeedsBuild() called during build.错误解决办法

错误解析:
在运行中,原因是因为控件、响应事件还没有构建完毕,延时加载即可解决问题
解决方法:添加延时

void _addIndex() {
    Future.delayed(Duration(milliseconds: 200)).then((e) {
      setState(() {});
    });
  }
}

10.Flutter报setState() called after dispose()错误解决办法

错误解析:防止页面关闭执行setState()方法
解决方法:mounted

if(mounted){
setState(() {});
}

11.如何实现水波纹,图片也怎么添加

InkWell有的叫溅墨效果,有的叫水波纹效果。使用场景是给一些无点击事件的部件添加点击事件时使用(也支持长按、双击等事件),同时你也可以去修改它的颜色和形状。

12.Android和IOS控件区别

如:按钮、选择器、日历组件(Android的Material和iOS的Cupertino风格)

13.生命周期输出顺序

在 Flutter应用程序中,生命周期涉及两个:

一个是 Widget的生命周期,

一个是应用程序的生命周期。

一、Widget 的生命周期

Flutter 里的 Widget 分为 StatelessWidget 和 StatefulWidget 两种:

1.StatelessWidget

StatelessWidget 的生命周期只有一个也就是build ,build 是用来创建要显示的页面 的。

2.StatefulWidget

StatefulWidget 有一个createState 方法,StatefulWidget 的生命周期主要包含在创建的State里面。

第一部分:表示的是初始化显示页面的时候执行的生命周期,主要是:

  • initState
  • didChangeDependencies
  • build

第二部分:调用setState 刷新页面的时候执行的生命周期,主要是:

  • didUpdateWidget
  • build

第三部分:应的是关闭页面的时候执行的声明周期,主要是:

  • deactivate
  • dispose

二、应用程序 的生命周期

其实这里说成是应用程序 的生命周期是不完全正确的,对于整个App 完全是通过一个FlutterActivity 来完成的应用,这样说是对的,如果是混合开发或者是存在多个FlutterActivity的话,这里就不能视为应用程序的声明周期

通过WidgetsBindingObserver的didChangeAppLifecycleState 来获取。通过该接口可以获取是生命周期在AppLifecycleState类中。

class MyHomePageState extends State<TestPage> with WidgetsBindingObserver {
  @override
  Widget build(BuildContext context) {
 
  }
 
  @override
  void initState(){
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }
 
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    if (state == AppLifecycleState.paused) {
      // went to Background
    }
    if (state == AppLifecycleState.resumed) {
      // came back to Foreground
    }
    if (state == AppLifecycleState.inactive) {
    }
    if (state == AppLifecycleState.detached) {
 
    }
  }
 
}


AppLifecycleState.inactive 对应Activity的onPause 生命周期
AppLifecycleState.resumed对应Activity的onResume 生命周期
AppLifecycleState.paused对应Activity的onStop生命周期
AppLifecycleState.detached对应Activity的onDestroy生命周期

生命周期说明:

  • 初次打开widget时,不执行AppLifecycleState的回调;
  • 按home键或Power键, AppLifecycleState inactive---->AppLifecycleState pause
  • 从后台到前台:AppLifecycleState inactive--->ApplifecycleState resumed
  • back键退出应用: AppLifecycleState inactive--->AppLifecycleState paused

14.Flutter 线程管理模型

Dart是单线程 但是也有自己的进程或者线程机制,isolate。App的启动入口函数就是一个isolate。在dart中islolate无法直接共享内存,不同的isolate直接只能通过isolate Api 进行通讯。
isolate是Dart对actor并发模式的实现。 isolate是有自己的内存和单线程控制的运行实体。isolate本身的意思是“隔离”,因为isolate之间的内存在逻辑上是隔离的。isolate中的代码是按顺序执行的,任何Dart程序的并发都是运行多个isolate的结果。因为Dart没有共享内存的并发,没有竞争的可能性所以不需要锁,也就不用担心死锁的问题。

Flutter Engine层会创建一个Isolate,并且Dart代码默认就运行在这个主Isolate上。必要时可以使用spawnUri和spawn两种方式来创建新的Isolate,在Flutter中,新创建的Isolate由Flutter进行统一的管理。

事实上,Flutter Engine自己不创建和管理线程,Flutter Engine线程的创建和管理是Embeder负责的,Embeder指的是将引擎移植到平台的中间层代码。

Flutter 中存在的四大线程:分别为 UI Runner、GPU Runner、IO Runner, Platform Runner (原生主线程)

在 Flutter 中可以通过 isolate 或者 compute 执行真正的跨线程异步操作。

15. Flutter里的key?

key是Widgets,Elements和SemanticsNodes的标识符。
key有LocalKey 和 GlobalKey两种。通过灵活使用这些Key,我们可以更好地控制和管理Widget树,确保应用程序的正确运行和性能优化。

GlobalKey是全局唯一标识一个Widget的Key。它通常用于在整个应用程序中引用一个Widget。

GlobalKey实现类有LabeledGlobalKeyGlobalObjectKey。默认实现是LabeledGlobalKey。

LabeledGlobalKey

这是GlobalKey的默认实现,内部仅有一个debugLabel属性,其他的也没啥。

class LabeledGlobalKey<T extends State<StatefulWidget>> extends GlobalKey<T> {
  // ignore: prefer_const_constructors_in_immutables , never use const for this class
  LabeledGlobalKey(this._debugLabel) : super.constructor();
 
 
  final String? _debugLabel;
}

GlobalObjectKey

class GlobalObjectKey<T extends State<StatefulWidget>> extends GlobalKey<T> {
  const GlobalObjectKey(this.value) : super.constructor();
 
 
  final Object value;
 
 
  @override
  bool operator ==(Object other) {
    if (other.runtimeType != runtimeType)
      return false;
    return other is GlobalObjectKey<T>
        && identical(other.value, value);
  }
 
 
  @override
  int get hashCode => identityHashCode(value);

GlobalKey可以获取到对应的widget的state对象,GlobalKey 使用了一个静态常量 Map 来保存它对应的 Element。你可以通过 GlobalKey 找到持有该GlobalKey的 Widget,State 和 Element。需要注意的是:GlobalKey 是非常昂贵的,需要谨慎使用。

import 'package:flutter/material.dart';
 
class GlobalKeyDemoPage extends StatefulWidget {
  const GlobalKeyDemoPage({super.key});
 
  @override
  State<GlobalKeyDemoPage> createState() => _GlobalKeyDemoPageState();
}
 
class _GlobalKeyDemoPageState extends State<GlobalKeyDemoPage> {
  final GlobalKey<_GKDContentPageState> contentKey = GlobalKey();
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("123"),
      ),
      body: GKDContentPage(key: contentKey),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 获取其name 的值
          print("${contentKey.currentState!.widget.name}");
          // 获取其message 的值
          print("${contentKey.currentState!.message}");
          print("${contentKey.currentContext}");
        },
        child: Icon(Icons.add_a_photo),
      ),
    );
  }
}
 
 
class GKDContentPage extends StatefulWidget {
  final String name = "everything will be fine !!!";
  const GKDContentPage({super.key});
 
  @override
  State<GKDContentPage> createState() => _GKDContentPageState();
}
 
class _GKDContentPageState extends State<GKDContentPage> {
  final String message = "just do right things !!!";
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text("GKDContentPage"),
    );
  }
}

在这个例子中,我们创建了一个 GlobalKey<_GKDContentPageState>,通过该key的currentState获取到GlobalKeyDemoPage的具体widget。

LocalKey

LocalKey是一个抽象类,它是GlobalKey的基类。它主要用于在当前Widget树内唯一标识一个Widget。LocalKey的实现包括ValueKeyObjectKeyUniqueKey

1.ValueKey

ValueKey基于一个特定的值创建,用于标识Widget。例如,如果我们有一个需要根据某个数据模型来创建ListView的场景,可以使用ValueKey来确保每个列表项都有唯一的标识。

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(
      key: ValueKey(items[index].id),
      title: Text(items[index].title),
    );
  },
);

2.ObjectKey

ObjectKey是通过引用对象本身来创建的,用于标识Widget。通常用于确保在更新Widget时,新旧Widget可以正确匹配。

final Key myObjectKey = ObjectKey(myObject);

return Container(
  key: myObjectKey,
  // Widget content
);

3.UniqueKey

UniqueKey是一个特殊的LocalKey,它总是创建一个唯一的Key。通常用于确保在动态生成Widget时,每个Widget都有一个唯一的标识。

return Container(
  key: UniqueKey(),
  // Widget content
);

总结

Flutter里的key分为两类:

  1. LocalKey,实现类有ValueKey、ObjectKey、UniqueKey;
  2. GlobalKey,实现类有LabeledGlobalKey、GlobalObjectKey。

LabeledGlobalKey其没有实现==和hashCode方法,也没有const修饰的构造函数,所以肯定能保证其全局唯一性。 

GlobalObjectKey是特殊的GlobalKey,内部维护了一个Object属性,并实现了== 和hashCode方法,通过判断runtimeType以及Object属性是否一致来判断两个GlobalObjectKey是否相等。
使用GlobalObjectKey时,为了保证GlobalObjectKey的全局唯一性,最佳实践是继承自GlobalObjectKey实现一个private的内部类,可以有效避免多人开发时可能造成的GlobalObjectKey冲突的问题。

16.Dart值传递还是引用传递?

dart是引用传递的,参数是把内存地址传过去了,如果对这个内存地址上的对象修改,那么其他位置的引用该内存地址的变量值也会修改。千万要记住dart中一切都是对象。

17.Widget 和 element 和 RenderObject 之间的关系?

既然 Widget 只是描述一个UI元素的配置信息,那么真正的布局、绘制是由谁来完成的呢?Flutter 框架的处理流程是这样的:

1.根据 Widget 树生成一个 Element 树,Element 树中的节点都继承自 Element 类。

2.根据 Element 树生成 Render 树(渲染树),渲染树中的节点都继承自RenderObject 类。

3.根据渲染树生成 Layer 树,然后上屏显示,Layer 树中的节点都继承自 Layer 类。

Flutter  里面有三棵树:

  • WidgetTree:,用户配置用户显示的页面,存放渲染内容、它只是一个配置数据结构,创建是非常轻量的,在页面刷新的过程中随时会重建。
  • Elment树,一般一个Widget对应一个Element,Element 主要是维持父子关系,例如节点的增删改查。同时持有Widget和RenderObject,存放上下文信息,通过它来遍历视图树,支撑UI结构。
  • RenderObject 树,RenderObject 树负责执行布局(layout)和绘制(paint)。

真正的布局和渲染逻辑在 Render 树中,Element 是 Widget 和 RenderObject 的粘合剂,可以理解为一个中间代理,Element会持有renderObject和widget的实例

Widget会被inflate(填充)到Element,并由Element管理底层渲染树。

Widget并不会直接管理状态及渲染,而是通过State这个对象来管理状态

Widget 只是一个配置,RenderObject 负责管理布局、绘制等操作。

18.什么是Navigator? MaterialApp做了什么?

Navigator是在Flutter中负责管理维护页面堆栈的导航器。MaterialApp在需要的时候,会自动为我们创建Navigator。Navigator.of(context),会使用context来向上遍历Element树,找到MaterialApp提供的_NavigatorState再调用其push/pop方法完成导航操作。

19.Stream 两种订阅模式?

Stream有两种订阅模式:单订阅(single) 和 多订阅(broadcast)

单订阅就是只能有一个订阅者,而广播是可以有多个订阅者。Stream 默认处于单订阅模式,所以同一个 stream 上的 listen 和其它大多数方法只能调用一次,调用第二次就会报错。但 Stream 可以通过 transform() 方法(返回另一个 Stream)进行连续调用。通过 Stream.asBroadcastStream() 可以将一个单订阅模式的 Stream 转换成一个多订阅模式的 Stream,isBroadcast 属性可以判断当前 Stream 所处的模式。

20.mixin、 extends、 implement 之间的关系?

继承(关键字 extends)、混入 mixins (关键字 with)、接口实现(关键字 implements)。

这三者可以同时存在,前后顺序是extends -> mixins -> implements。

Flutter中的继承是单继承,子类重写超类的方法要用@Override,子类调用超类的方法要用super。

在Flutter中,Mixins是一种在多个类层次结构中复用类代码的方法。mixins的对象是类,mixins绝不是继承,也不是接口,而是一种全新的特性,可以mixins多个类,mixins的使用需要满足一定条件。

使用mixins的条件:

mixins类只能继承自object

mixins类不能有构造函数

一个类可以mixins多个mixins类

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

21.Dart 中的级联操作符

Dart 当中的 「..」意思是 「级联操作符」,为了方便配置而使用。「..」和「.」不同的是 调用「..」后返回的相当于是 this,而「.」返回的则是该方法返回的值 。

22.await for 与 stream流

Stream<String> stream = new Stream<String>.fromIterable(['1', '2', '3', '4']);
main() async{
 print('start');
 await for(String s in stream){
   print(s);
 }
 print('end..');
}

输出:

start
1
2
3
4
end..

await for一般用在直到Stream什么时候完成,并且必须等待传递完成之后才能使用,不然就会一直阻塞。

23.Flutter是怎么完成组件渲染的?

在计算机系统中,图像的显示需要CPU、GPU和显示器一起配合完成CPU负责图像数据计算,GPU负责图像数据渲染,而显示器则负责最终图像显示。CPU把计算好的、需要显示的内容交给GPU,由GPU完成渲染后放入帧缓冲区,随后视频控制器根据垂直同步信号以每秒60次的速度,从帧缓冲区读取帧数据交由显示器完成图像显示。操作系统在呈现图像时遵循了这种机制。

而Flutter作为跨平台开发框架也采用了这种底层方案,UI线程使用Dart语言来构建视图结构数据,这些数据会在GPU线程进行图层合成,随后交给图像渲染引擎Skia加工成GPU数据,而这些数据会通过OpenGL最终提供给GPU渲染。

24.Flutter状态管理

Flutter的状态可以分为全局状态和局部状态两种。常用的状态管理有ScopedModel、BLoC、Redux / FishRedux和Provider。

状态管理基本都是基于InheritedWidget封装的用于Widget树的数据传递与共享的的一套框架

Provider是继承于InheritProvider,而InheritProvider本质上是一个InheritWidget,所以Provider本质上是依托于InheritProvider的机制来实现的widget树的状态共享。

25.isolate是怎么进行通信和实例化的?

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

借助:

import 'dart:io';
import 'dart:isolate';

26.Flutter中const关键字

Flutter中有两种定义常量的方式,一种是final一种是const,const声明的变量在编译时确定,永远也不会改变final变量在第一次使用时被初始化,即:final声明的变量也是不可以更改的,但是final声明的变量可以先声明后再赋值。赋值后不可改变。比如我们声明 final 类型的变量时,可以先不赋值,甚至可以通过构造函数来初始化。

final int a;
a = 1;

// 构造方法
class FinalDemo {
  final name;
  FinalDemo({required this.name});
}

const 意味着什么?

实际上,声明为 const 时就是告诉编译器,这个变量在整个代码的生命周期中都不会改变,因此只需要给这个变量创建一个副本,其他任何地方使用这个变量的时候都指向该副本,而不是创建一个新对象

从缓存中取一个可复用的对象一样。这就是 const的真正价值!const声明就是告诉 Flutter 如果遇到了一个不可变的构造器(即const` 构造器)时,这就是一个不可变对象,可以进行复用。

对渲染的性能优化

const 修饰不仅仅是节省组件构建时的内存开销。当一个对象标记为常量的时候,Flutter 在需要重新构建组件的时候,由于这个组件是不应该改变的,重新构建没有任何意义,因此 Flutter 不会重建构建 const 组件。这对于一个应用的运行期间而言,将会是一个巨大的性能提升!由于 Flutter 知道哪些组件需要重新构建哪些不要,因此即便是在热重载阶段也能够加速热重载的速度。

原则:尽可能地将组件的属性声明为 final ,构造方法声明为const,并且使用 const 使用这样的组件对象。 

27.Flutter 的热重载

Flutter 的热重载是基于 JIT 编译模式的代码增量同步。

热重载的流程可以分为 5 步,包括:扫描工程改动、增量编译、推送更新、代码合并、Widget 重建。Flutter 在接收到代码变更后,并不会让 App 重新启动执行,而只会触发 Widget 树的重新绘制,因此可以保持改动前的状态,大大缩短了从代码修改到看到修改产生的变化之间所需要的时间。

另一方面,由于涉及到状态的保存与恢复,涉及状态兼容与状态初始化的场景,热重载是无法支持的,如改动前后 Widget 状态无法兼容、全局变量与静态属性的更改、main 方法里的更改、initState 方法里的更改、枚举和泛型的更改等。
可以发现,热重载提高了调试 UI 的效率,非常适合写界面样式这样需要反复查看修改效果的场景。但由于其状态保存的机制所限,热重载本身也有一些无法支持的边界。

28.讲一讲Flutter优势

Flutter,本质上一套跨平台的UI框架,通过OpenGL这种跨平台的绘制库(内部会调用操作系统API)实现了一套代码跨多端。Framework底层是Flutter引擎,引擎全部使用C++实现,强大的引擎能力,提供高效的图形和文字绘制。

1.Flutter的整体架构

Flutter 分为三大部分:由 Dart 语言负责的 Framework 层;Dart 语法执行器;impller(之前是Skia) 图像处理引擎。

通过Dart作为开发语言,并且提供Material和Cupertino两套视觉控件,视图或其他和视图相关的类,都以Widget的形式表现。Flutter有自己的渲染引擎,并不依赖原生平台的渲染。Flutter还包含一个用C++实现的Engine,渲染也是包含在其中的

 impller的新渲染引擎为何能替代Skia?

1. 性能

在性能方面,impller渲染引擎有着更高的性能表现。实际测试结果表明,在绘制图案或动画时,impller引擎的性能可以达到skia引擎的两倍。

2.可扩展性

传统的skia引擎采用单线程模型,而impller引擎采用多线程模型,impller更好地利用现代硬件的多核处理能力,从而实现更高的性能和更好的可扩展性以更好地应对不同的场景。

此外,impller引擎还可以实现动态代理,这意味着可以根据具体的场景选择合适的渲染器。例如,可以使用CPU渲染器来处理图形,也可以使用GPU渲染器来绘制3D图像。

3. 灵活性

在灵活性方面,impller渲染引擎可以更加灵活地应对不同的情况。比如,在处理不同的字体文件时,impller引擎可以更好地适应各种不同的字体格式,从而实现更好的字体渲染效果。

此外,impller引擎还可以更加智能地处理不同的场景和事件。比如,在处理鼠标点击等事件时,impller引擎可以根据具体情况进行处理,从而实现更加灵活的交互效果。

跨平台方案的比较:

NATIVE

原生应用程序在使用新功能时带来的困扰是最少的,大多数情况下,原生的应用将会比那些跨平台构建的应用性能要好一些。原生应用的一大优势是:当需要时,他们可以立即采用 Apple 和 Google 在测试版中开发的新技术而不用等待第三方的集成。构建原生应用的主要缺点是缺乏跨平台的代码复用,如果同时开发 iOS 和 Android 应用,那么开发成本可能会很高。

REACT NATIVE

React Native 允许原生应用使用 JavaScript 构建。应用中用到的控件实际上都是原生平台里的控件,所以用户使用起来感觉和原生应用一样。对于那些 React Native 没有提供的需要自定义的应用,仍然需要使用原生开发。当需要定制的模块比较多时,某些情况下,在 React Native 中开发不如使用原生开发更合适。

XAMARIN

当谈到 Xamarin 时,有两种不同的方法将会被提及。跨平台方法:Xamarin.Forms。该方法不同于 React Native,但是从概念上讲是相似的,因为它也是抽象原生控件。同样的,在定制方面它也有和 React Native 同样的缺点。第二种方法:Xamarin-classic。该方法分开使用 Xamarin 的 iOS 和 Android 产品来构建适用于特定平台的功能,就像直接使用 Apple/Android 原生功能一样,只不过在 Xamarin 中需要使用 C# 或 F# 。使用 Xamarin 的好处是可以共享非平台特定的代码,例如网络、数据访问、Web 服务等。

NATIVE+小程序

目前也有国内厂商推出了成熟的解决方案,之前有了解到 FinClip ,这个框架对标微信小程序的功能,相同的代码,既能在微信端跑,也能在自己的 App 里跑,效果是一样的,相当于把已经上架的微信小程序能够直接搬到自己的 App 能运行。开发一次就能够在包括 Linux、Windows、MacOS、麒麟等操作系统运行。这意味着,PC 端、车载设备、智能电视都能使用小程序了,实现了“一次开发,到处运行”。

Flutter

1.快速开发和迭代

Flutter自身具有热修复(热重载)的功能,尽管有使用的限制,但是它依然能够为开发过程提供更高的效率。另外,Flutter SDK还允许我们修复崩溃和继续从应用程序停止的地方进行调试。

2.页面流畅、样式美观

对于不同的平台(Android和iOS),Flutter提供了风格不同的控件,以满足不同平台的设计理念。

3.提供原生性能

Flutter提供了一种响应式视图,无须JavaScript做桥接;强大的API使得实现复杂的页面效果成为可能;高性能的渲染机制使得120FPS的高频率可以轻而易举的实现。当界面上的图片数量越来越多时,与React Native相比,Flutter的优势会越来越明显。

4.灵活的跨平台开发

Flutter可以单独作为开发框架完成整个App的开发,也可以与现有原生代码相结合实现Hybrid混合模式的开发。

29.Flutter热更新

利用原生框架更新,实际上就是更新Flutter框架相关的二进制。Flutter应用发布出来的产物主要包括 libflutter.solibapp.soflutterAssets,这样,就可以通过Android端原生平台网络请求,动态下发并加载这些产物,从而实现动态更新。

那么为什么这种方案不可取呢?主要原因在于iOS 的应用商店不允许动态下发和加载二进制产物,包括动态库之类的,故此方案只能在Android端实现。

flutter_lua_dardo 就是一个这样的扩展库。该库主要用于包装Flutter接口和控件,这使得Flutter能够在需要经常改变UI风格的地方使用远程脚本动态地更新和生成界面。

30.Flutter应用性能调试

Flutter Driver 库提供了对评分的支持。基于这套测试框架就可以生成以下几项的测试标准:

  • 卡顿

  • 下载大小

  • 电池性能

  • 启动时间

    Startup time

检视 widget 重建性能

在 Android Studio 中,找到 Flutter Performance (View > Tool Windows > Flutter Performance),就可以直接看到正在重建的 widget 数量。

代码优化工具 : PerformanceOverlay

A. 在 Flutter 项目中打开 PerformanceOverlay

1.首先打开 PerformanceOverlay 工具,在 AndroidStudio -> Preference -> Language&Frameworks -> Flutter 中打开相应开关,如图:

2.然后 run 我们的APP,就可以在AS的左边看到 Flutter Inspector的窗口(标记),如图

3.然后打开 PerformanceOverlay,点击Flutter Inspector 窗口左上角树状图按钮(标记2) ,即可在屏幕上显示当前UI帧率和GPU帧率的波图。

B. aar 模式混合开发的项目 打开 Flutter Inspector

只需要在Flutter 项目的根布局或者入口布局的 MaterialApp 的Widget中加入即可:

```

showPerformanceOverlay: true 

```

C. PerformanceOverlay 分析

打开PerformanceOverlay后,我们APP会增加两个浮层,如图:

31.createState 方法在什么时候调用?state 里面为啥可以直接获取到 widget 对象?

 答:Flutter 会在遍历 Widget 树时调用 Widget 里面的 createElement 方法去生成对应节点的 Element 对象,同时执行 StatefulWidget 里面的 createState 方法创建 state,并且赋值给 Element 里的 _state 属性,当前 widget 也同时赋值给了 state 里的_widget,state 里面有个 widget 的get 方法可以获取到 _widget 对象。

32.build 方法是在什么时候调用的?

答:Element 创建好以后 Flutter 框架会执行 mount 方法,对于非渲染的 ComponentElement 来说 mount 主要执行 widget 里的 build 方法。

33.BuildContext 是什么?

答:StatefulElement 执行 build 方法的时候是执行的 state 里面的 build 方法,并且将自身传入,也就是 常见的 BuildContext。简而言之 BuidContext 就是 当前Element对象

34.Widget 频繁更改创建是否会影响性能?复用和更新机制是什么样的?

答:不会影响性能,widget 只是简单的配置信息,并不直接涉及布局渲染相关。Element 层通过判断新旧 widget 的runtimeType 和 key 是否相同决定是否可以直接更新之前的配置信息,也就是替换之前的 widget,而不必每次都重新创建新的 Element。

35.创建 Widget 里面的 Key 到底是什么作用?

答:Key 作为 Widget 的标志,在widget 变更的时候通过判断 Element 里面之前的 widget 的 runtimeType 和 key来决定是否能够直接更新。

36.回调地狱(Callback Hell

答:如果代码中有大量异步逻辑,并且出现大量异步任务依赖其他异步任务的结果时,必然会出现Future.then回调中套回调情况。

1.回调地狱:

//先分别定义各个异步任务
Future<String> login(String userName, String pwd){
	...
    //用户登录
};
Future<String> getUserInfo(String id){
	...
    //获取用户信息 
};
Future saveUserInfo(String userInfo){
	...
	// 保存用户信息 
}; 

接下来,执行整个任务流:

login("alice","******").then((id){
 //登录成功后通过,id获取用户信息    
 getUserInfo(id).then((userInfo){
    //获取用户信息后保存 
    saveUserInfo(userInfo).then((){
       //保存用户信息,接下来执行其他操作
        ...
    });
  });
})

1.如何消除回调地狱:

一、使用Future消除Callback Hell

login("alice","******").then((id){
  	return getUserInfo(id);
}).then((userInfo){
    return saveUserInfo(userInfo);
}).then((e){
   //执行接下来的操作 
}).catchError((e){
  //错误处理  
  print(e);
});

二、使用 async/await 消除 callback hell

task() async {
   try{
    String id = await login("alice","******");
    String userInfo = await getUserInfo(id);
    await saveUserInfo(userInfo);
    //执行接下来的操作   
   } catch(e){
    //错误处理   
    print(e);   
   }  
}

36.State

一个 StatefulWidget 类会对应一个 State 类,State表示与其对应的 StatefulWidget 要维护的状态

State 中有两个常用属性:

1.widget,它表示与该 State 实例关联的 widget 实例,由Flutter 框架动态设置。

2.context。StatefulWidget对应的 BuildContext,作用同StatelessWidget 的BuildContex。

如何在 widget 树中获取State对象?

1.通过Context获取

2.通过GlobalKey

37.flutter 中Widget的分类

1、组合类:StatelessWidget和StatefulWidget

2、代理类:inheritedwidget、ParentDataWidget
inheritedwidget一般用于状态共享,如Theme 、Localizations 、 MediaQuery 等,都是通过它实现共享状态,这样我们可以通过 context 去获取共享的状态,比如 ThemeData theme = Theme.of(context);

3、绘制类:RenderObjectWidget
RenderObject 的布局相关方法调用顺序是 : layout -> performResize -> performLayout -> markNeedsPaint

38.isolate是怎么进行通信和实例化的?

1、isolate实际就是一个隔离的Dart执行的上下文环境(或者容器)
2、isolate是有自己的内存和单线程控制的事件循环
3、isolate之间的内存在逻辑上是隔离的,不像Java一样是共享内存的
4、任何Dart程序的并发都是运行多个isolate的结果。Dart没有共享内存的并发;

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

39.简述下Bloc模式是什么,以及它的工作原理。

BLoC全称(Business Logic Component)是谷歌提出的一种设计模式,利用流的方式实现界面的异步渲染和重绘,我们可以非常快速的通过BLoC实现业务与界面的分离。

BLoC 管理着事件 events 和状态 state,比如,它接受一系列事件流,并将它们转化为状态流作为输出。

在Flutter中实现BLoC设计模式需要借助 flutter_bloc 这个库来完成

原理:BLoC基于流(Stream)的概念,使用RxDart库中的StreamController和Stream来实现。BLoC将UI层(如widget)中的用户操作通过事件(Event)发送给业务逻辑层,并根据这些事件处理数据并生成新的状态(State),再将新状态传递回UI层以更新视图。

40.Flutter Engine

Flutter Engine 实现了 Flutter 的一系列核心库,包括动画和图像,文件还有网络 I/O,辅助功能支持,插件架构,还有用于开发、编译、运行 Flutter 应用的 Dart 运行时和工具链。

线程

Flutter 引擎本身是没有创建和管理自己的线程的操作。相反,是 embedder 负责为 Flutter 引擎创建和管理线程。embedder 使用 task runners 的概念为 Flutter 引擎提供线程管理。

Task Runner 配置

Flutter 引擎让 embedder 给它提供 4 个 Task Runner 的引用。对引擎来说,它不关心这些引用是否实际上就是同一个 task runner,或者这几个 task runner 是同一个线程来服务的 (相当于说 Flutter 引擎需要四个这样的接口,至于接口的具体实现它是不关心的)。但从最佳性能上来说,embedder 应该是要为每个 task runner 创建单独的线程。毕竟虽然引擎不关注这些,但它还是期望线程的配置在整个生命周期里应该是稳定的。

Platform Task Runner

embedder 把这个 task runner 所对应的线程当做主线程。

UI Task Runner

Flutter 引擎为 root isolate 执行的所有 Dart 代码就在 UI Task Runner 里。

Raster Task Runner

Raster Task Runner 也就是 GPU Task Runner。Raster 就是光栅化的意思,因此这个 task runner 就是用来执行设备上那些需要光栅化(通常是 GPU 来处理)的任务。

IO Task Runner

有一些对于光栅线程(raster thread)来说很必要同时也很耗时的工作,是在 IO Task Runner 上来做的。IO Task Runner 的主要作用是从 asset 里读取压缩图片,然后确保这些图片已经准备好给 Raster Task Runner 进行渲染。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值