请列举至少5个Dart语法糖,并说明其用法和作用
- 操作符与运算符
?.
:条件访问成员,用作条件判空
??
:空感知运算符,用于条件判空
??=
:空感知赋值运算符,判断对象是否为空决定是否要给对象赋值
..
:级联运算符
?..
:空感知级联运算符
...
:扩展操作符
...?
:空感知扩展操作符 - 循环
for-in
- 箭头函数
=>
:语法糖补全了return,同时因为是单行函数,所以不加{} -
await for
:用于处理流(Stream)
// 操作符:?.
void main() {
String? name;
var len = name?.length;
print(len); // len is null
}
void main() {
String? name = 'flutter';
var len = name?.length;
print(len); // len is 7
}
// 运算符:??
void main() {
String? name;
var result = name ?? 'Dart';
print(result); // result is Dart
}
void main() {
String? name = 'flutter';
var result = name ?? 'Dart';
print(result); // result is flutter
}
// 运算符:??=
void main() {
String? name;
name ??= 'Dart';
print(name); // name is Dart
}
void main() {
String? name = 'flutter';
name ??= 'Dart';
print(name); // len is flutter
}
请说明一下两种变量声明的区别
(1)late String name;
(2)String name;
- 延迟初始化:late 变量允许在首次访问时才初始化,而非 late 变量需在声明或构造函数中立即初始化。
- 编译时检查与运行时检查:late 变量仅在运行时检查是否已初始化,非 late 变量在编译时进行空安全检查。
- 默认值:late 变量无默认值,非 late 变量(在 null 安全环境下)通常要求在声明时提供非 null 初始值。
const 和 final 的区别
- const关键字
- 声明编译时常量: 这意味着变量的值在编译时就已经确定,并且在程序运行期间不可更改
- 变量声明的左侧: 此时它要求变量在声明时就必须赋值,并且赋值必须是编译时常量。例如,数值、字符串、其他的const变量或者某些表达式
- 变量声明的右侧: 此时它修饰的是值,表示该值在编译时就已经确定,并且整个对象是不可变的
- 修饰的集合或对象: 要求集合的元素必须是递归的编译时常数。
- 修饰的类的构造函数: 要求该类的所有成员都必须是final的
- final关键字
- 声明不可变的变量: 并不要求变量的值在编译时就已知
- 赋值时机: 可以在声明时赋值,也可以在构造函数中赋值
- 初始化: 第一次使用时初始化,如果是在声明时赋值,那么赋值可以是任意的,不一定是编译时常量
- 修饰的对象: 内部的可变成员仍然可以被修改,除非这些成员也被声明为final
- 总结:
const比final更严格
要求变量的值在编译时就已经确定,并且在运行时不可变
而final只要求变量在初始化后不可变,但初始化时的值不必在编译时已知
请依次写出Widget的生命周期函数并说明其使用场景
1713573967574.jpg- createState:创建 State 的方法。
- initState:初始化变量,接口请求操作。
- didChangeDependencies ,当State对象的依赖发生变化时会被调用。
- build ,主要是返回需要渲染的 Widget
- reassemble:在 debug 模式下,每次热重载都会调用该函数。
- didUpdateWidget :在widget重新构建时调用。
- deactivate:在组件被移除节点后会被调用。
- dispose:永久移除组件,并释放组件资源。
ListView和ListView.builder有什么区别?
- ListView在
初始化
时就把所有的children都创建
出来 - ListView.builder并不是初始化时把所有的children都创建出来,而是等用户
滚动
到了要创建
的位置才会创建出来。(这个构造函数适用于拥有大量(或者无限)子元素的列表视图,因为它只会为那些实际可见的子元素调用构建器。这样做可以提高性能,避免不必要的构建操作)
InheritedWidget是什么?有什么作用?
InheritedWidget是一种无界面的组件,它提供了自顶向下为子组件共享数据的功能。
StreamBuilder和FutureBuilder有什么区别?各自使用场景是什么?
都是用于根据异步操作的状态动态构建UI的Widget。
- 数据源的区别
StreamBuilder用于处理连续的异步数据流,可以有多个数据事件
。
FutureBuilder用于处理一次性的异步操作,只有一个结果
。 - 构建次数的区别
StreamBuilder可能多次
调用其build函数,每当新的数据事件到达时。
FutureBuilder只调用一次
其build函数,当Future完成时。
Provider与其他状态管理解决方案有什么不同?
https://blog.51cto.com/u_16175630/7445520
设计理念与简洁性
-
Provider设计目标是
简化状态管理
,通过继承自InheritedWidget
的ChangeNotifierProvider、ValueListenableProvider等类,将状态以树状结构向下传递
给子组件。强调简单易用。 -
Bloc基于响应式编程模式,强调将
业务逻辑(BLoC)与 UI 完全解耦
。通过事件流(Event)驱动状态变更(State),并利用 Stream 和 BlocProvider 实现状态的分发。虽然结构相对复杂,但非常适合大型项目和复杂的业务逻辑,因为它鼓励模块化和逻辑复用。 -
Redux:遵循严格的
单向数据流和不可变状态原则
。它包括 Actions、Reducers 和 Store,形成一个中心化的状态管理机制。Redux 提供清晰的调试工具和高度可预测的行为,适用于大型团队协作和有严格状态管理需求的应用。 -
ScopedModel:较早的 Flutter 状态管理库,同样基于
InheritedWidget
。它通过定义 Model 类来封装状态,并通过 ScopedModel 和 ScopedModelDescendant Widget 分发和访问状态。相比于 Provider,ScopedModel 的功能较为基础,且对不同类型状态的处理不够灵活。
状态更新方式
-
Provider:依赖于
ChangeNotifier
类,状态对象通过调用notifyListeners()
方法触发状态更新通知。也可以使用ValueNotifier、Stream等其他可观察对象作为状态源。 -
Bloc:基于
Stream
,状态更新通过处理事件流并在其中产生新的状态来实现。开发者编写事件处理器(event handler),将事件转换为新的状态。 -
Redux:状态更新通过
Reducer
函数完成。当 Actions 发出后,Store 调用相应的 Reducer 函数处理这些动作,并返回一个新的状态对象。新旧状态的差异会触发 UI 更新。 -
ScopedModel:状态更新
直接修改 Model 对象的属性
,然后调用notifyListeners()
方法通知依赖组件。
在Flutter中如何优化性能?
- 尽量减少消耗资源的操作 (异步)
- 控制 build() 方法的耗时
- 谨慎使用 saveLayer()
- 尽量减少使用不透明度和裁剪
- 使用ListView.builder()
- 尽量减少由内部操作引起的布局传递
- 使用FutureBuilder和StreamBuilder组件
- 合理的刷新范围RepaintBoundary
- 避免UI线程耗时操作
- 减少Widget重建
https://flutter.cn/docs/perf/best-practices
main函数和runApp的区别?
main函数是dart的程序运行入口函数
runApp函数是渲染根widget树的函数
热重载是如何实现的?
将更新的源代码文件注入到正在运行的 Dart 虚拟机(VM)来实现热重载。在虚拟机使用新的字段和函数更新类之后, Flutter 框架会自动重新构建 widget 树,以便可以快速查看更改的效果。
补充:
热重载 会将代码更改转入 VM,重建 widget 树并保持应用的状态,整个过程不会重新运行 main() 或者 initState()。
热重启 会将代码更改转入 VM,重启 Flutter 应用,不保留应用状态。
热重载流程:扫描更新、增量编译、推送更新、代码合并、widget重建。
Flutter web 目前仅支持热重启,不支持热重载。
说说flutter里async和await?
await的出现会把await之前和之后的代码分为两部分,await并不是程序运行到这里就阻塞了,而是立刻结束当前函数的执行并返回一个Future,函数内剩余代码通过调度异步执行。
async是和await搭配使用的,await只在async函数中出现。在async 函数里可以没有await或者有多个await。
provider 原理
Provider主要利用了InheritedWidget机制,结合Listenable监听,在数据变化时触发创建InheritedWidget,引起InheritedElement的update,从而让关联的子孙更新。在Provider中封装了InheritedWidget,持有了model,view通过provider获取到model,然后操作model。当model发生变化时,通过notify通知Provider更新view。
Provider中消费者Consumer 和 Selector的区别?
Selector类和Consumer类似,只是对build调用Widget方法时提供更精细的控制。支持部分状态数据更新,才会更新该组件。
四种消费者
Provider.of
Consumer
Selector
InheritedContext:read、watch、selector
八种提供者
1.Provider
2.ChangeNotifierProvider
3.FutureProvider
4.StreamProvider
5.MultiProvider
6.ProxyProvider
7.ChangeNotifierProxyProvider
8.ListenableProxyProvider
getx 原理
Getx 是一个轻量级的状态管理库,它是一个框架级别的解决方案,旨在提高性能和开发效率。它提供了多种便利功能,例如依赖注入、路由管理、持久化存储等,使得开发过程更加高效,结构更加清晰。
- Getx 的核心原理
是使用响应式编程模式。在该模式下,数据模型是可观察的,并且任何对模型的更改都会自动反映在视图中。当使用 Getx 时,我们定义数据变量作为 Reactive 类型,然后将其添加到相应的依赖列表中。当模型更改时,依赖列表中的所有组件会被自动通知,以更新它们自己的状态。 - Getx 依赖注入机制
通过该机制,我们可以将一个对象或函数注入到其他组件中,以节省内存和代码复杂性。我们可以将实例作为单例或原型进行注册。在编写组件时,我们只需在构造函数中添加 Get 类型,然后注入所需的依赖项即可。 - Getx 路由管理功能
可以轻松地实现页面间的导航和传递参数。它使用命名路由技术,使得链接和视图之间的映射更加清晰。我们可以使用路由中间件将路由与页面动画和其他自定义功能捆绑在一起。 - Getx 持久化存储功能,该功能可在应用程序关闭后保存状态,以便在下次启动时进行恢复。它使用本地存储机制,如 SharedPreferences 和 SQLite,以提供跨设备和跨浏览器的一致性。
- 总的来说,Getx 的原理是基于响应式编程模式,并提供了依赖注入、路由管理、持久化存储等多种实用性功能。它通过聚焦于基础应用程序构建块来提高性能和可维护性,使得开发人员可以更加专注于应用程序逻辑和用户体验
DevTools
- Flutter widget inspector:了解现有布局、诊断布局问题
flutter WebView加载内容过多导致app崩溃
flutter项目中遇到什么影响比较大的问题?
apk瘦身
- 清除无用资源
- 图片压缩
- 混淆
- 分架构构建apk
- 静态资源动态下发
上传的图片特别大,在朋友圈显示图片时如何处理性能问题?
- 根据图片显示大小裁剪。设置Image组件的
cacheWidth
和cacheHeight
属性,可以让引擎以指定的大小解析图片,减少内存的消耗 - 使用cached_network_image插件或者ImageCache类做图片缓存,避免频繁请加载图片资源。
- 上传图片时进行适当压缩
- 使用占位图组件FadeInImage和图片预加载precacheImage来提高用户体验。
- Element复用优化
短视频切换如何保证流畅播放?
预加载、队列
apk安全
- 代码混淆
- 图片音视频资源使用资源加密技术对资源进行加密,运行时动态解密
- 加固保签名
图片性能优化
问题1:APP 页面偶现卡顿,上下滑动不流畅,影响体验
问题2:devtools查看内存,内存占用高
优化方向:降内存、降刷新
高内存定位:
通过 Xcode 性能调试窗口可知,iOS 端智家 APP 的运行内存 1.36G 左右;
通过 Android Studio 的 profile 窗口可知,Android 端智家 APP 的运行内存在 1.6G 左右;
图片过载检测:debug模式下, Flutter Inspector,点击 Highlight Oversized Images。查看过载检测日志,很多图片存在过载,需内存优化。红色日志会打印某一张图的图片尺寸。
内存检测:profile构建模式下,在devtools上查看RSS(resident set size)内存占用情况,启动起来高达480M左右,在滑动列表时,列表会白屏,内存瞬间达到712M。performance工具查看UI线程绘制时间最高达40ms。
方案:
- 网络图片压缩
图片存放在阿里云 oss 服务器,可以使用 oss 图片压缩参数,采用指定宽高缩放的方式,阿里云还支持百分比缩放。再次检测 APP 的运行时内存占用,内存下降到206M。 - 本地图片压缩
根据官方文档说明,以及图片过载检测控制台优化提示,给本地图片增加 cacheHight 或 cacheWidth;或使用 ResizeImage 组件包裹。
cacheWidth/cacheHight = width/height * 像素密度
降低页面刷新:局部 UI 刷新 导致重绘;大量逻辑计算驱动页面刷新
定位:Flutter Inspector - Highlight Repaints,UI 组件周边会出现彩色边框,UI 重绘时彩色边框会动态变色。
方案:
- 组件重会
根据官方性能优化指南,出现组件重绘,使用RepaintBoundary包裹组件,缺点是会增加一定的内存消耗。我们采用了Provider来实现局部刷新。 - 订阅关系的优化
页面在前台显示时,才通知状态变化去更新UI;不在前台时,此时不去通知状态的变化
推送
极光推送、支持iOS
flutter app保活
- 使用前台服务:flutter_foreground_plugin
- 设置闹钟,进程被杀,闹钟也会触发:android_alarm_manager_plus
- 后台任务: workmanager
- 厂商推送:firebase_messaging
接口加密
使用RSA非对称加密,服务器生成公钥和私钥,客户端拿到公钥对接口参数加密,然后发起请求,服务器使用私钥对加密报文进行解密,获得原始请求报文。服务器处理请求,并将响应数据进行加密返回给客户端,客户端使用相同的公钥进行解密,获取结果。
内存优化
图片
释放不需要的对象
GooglePlay上架流程
https://blog.csdn.net/cuckoosusu/article/details/137172929
https://blog.csdn.net/u012378566/article/details/129749017
Google可能会对以下这几个方面进行检查。
① 内容审查:Google的审核系统会检测是否含有违反版权、成人内容、暴力内容等违禁元素。
② 权限隐私合理性审查:审查应用请求的权限,例如,一个手电筒应用若要求访问你的通讯录,这显然是不合理的。如果应用请求的权限超出其功能所必需的范围,可能会被拒审。
③ 代码安全审查:对应用程序进行分析,检查应用的源代码、二进制文件和资源文件,是否存在恶意代码、安全漏洞或其他安全风险,并进行标记。
④ 模拟应用操作行为:通过模拟操作应用的网络行为、API调用行为、日志筛选等一系列操作,提取关键信息进行标记后,与谷歌庞大的数据库进行检索匹配,以判断是否存在与已被标记的不合规产品高度相似的情况,从而判断是否存在违规行为并决定是否能够通过审核。
⑤ 应用界面规则
谷歌应用商店上架应用程序需要符合以下界面规则:
(1)应用界面需要简洁、清晰、易于使用。
(2)应用界面需要符合 Android 设备的屏幕大小和分辨率。
(3)应用界面需要符合 Android 设备的操作方式和交互习惯。
⑥应用广告规则
谷歌应用商店上架应用程序需要符合以下广告规则:
(1)应用广告需要符合谷歌广告政策,不得包含欺诈、虚假、误导等内容。
(2)应用广告需要标明广告的来源和性质,不得误导用户。
(3)应用广告需要保证广告内容与应用内容相关联。
IOS应用上架AppStore的流程
https://blog.csdn.net/hepingdev/article/details/125522207
自动化打包、渠道打包
https://blog.csdn.net/sugoods/article/details/123508277
利用Flutter 命令工具自定义参数的功能 --dart-define。
项目中创建脚本shell/xx.sh
网友评论