1. StatelessWidget 和 StatefulWidget在 Flutter 中有什么区别?
StatelessWidget
定义:
StatelessWidget 是一种不可变的 Widget,其状态在创建后不会改变。自己不能更新自己,它们只依赖于构造函数中的参数来构建 UI。
特点:
- 不维护任何内部状态。
- 适合用于显示静态内容或简单的 UI 结构。
- 在需要更新 UI 时,必须重新创建该 Widget 的实例。
StatefulWidget
定义:
StatefulWidget 是一种可变的 Widget,可以在其生命周期内维护状态。它可以响应用户输入或其他事件并更新 UI。
特点:
- 具有一个可变的状态(通过
State类管理)。 - 当状态发生变化时,通过调用
setState()方法来通知 Flutter 更新 UI。 - 适合用于需要动态更新的内容,如表单、动画等。
状态管理
-
StatelessWidget:不维护任何状态,适合静态内容。 -
StatefulWidget:可以维护内部状态,适合动态内容。
更新机制
-
StatelessWidget:需要重新创建实例来更新 UI。 -
StatefulWidget:通过setState()方法来更新 UI。
根据应用的需求,开发者可以选择使用 StatelessWidget 或 StatefulWidget 来构建相应的界面。
2. 解释 Stateful Widget Lifecycle 生命周期
Stateful Widget 生命周期阶段
State 生命周期(State 类)
State 对象有以下几个生命周期方法,按调用顺序如下:
-
createState():当 Stateful Widget 被插入到 Widget 树中时,会调用createState()方法。这个方法必须返回一个 State 对象。 -
mounted:在createState()之后,State 对象与BuildContext关联,此时mounted属性被设置为true。这是一个重要的标志,表示 State 对象当前是否在树中。在调用setState时,必须确保mounted为true,否则会抛出异常。 -
initState():这是 State 对象的初始化方法,在 State 对象被创建后调用,且仅调用一次。通常用于初始化依赖、监听器或接收父 Widget 传递的数据。 -
didChangeDependencies():在initState()之后立即调用,也会在依赖的InheritedWidget发生变化时调用。可以在这个方法中执行一些依赖于上下文或InheritedWidget的操作。 -
build():这个方法用于构建 Widget 树。在以下情况会被调用:- after
initState() - after
didChangeDependencies() - after
didUpdateWidget() - after
setState()
- after
-
didUpdateWidget(oldWidget):当父 Widget 重建并传递新的 Widget 配置时,会调用这个方法。旧 Widget 会被作为参数传入。可以在此处比较新旧 Widget,并决定是否需要重新初始化一些状态。 -
setState():这是一个通知框架状态已变化的方法,调用后会重新构建 Widget。它本身不是生命周期方法,但会触发build。 -
deactivate():当 State 对象从树中移除时调用,可能会被重新插入到树的另一个位置(比如使用GlobalKey时)。 -
dispose():当 State 对象从树中永久移除时调用,用于释放资源,如取消计时器、停止动画、清理控制器等。调用后mounted属性变为false。 -
reassemble():仅在调试模式下调用,用于热重载。在 release 模式下不会调用。
3. 什么是 Flutter tree shaking
Tree Shaking 是一种优化技术,用于减少最终应用的体积。具体来说,它的作用是:
- 去除未使用的代码:在构建应用时,
Tree Shaking会分析代码中的依赖关系,并自动移除那些在应用中未被引用或使用的代码。这包括未使用的库、类、函数等。
- 去除未使用的代码:在构建应用时,
- 提高性能:通过减小应用的体积,
Tree Shaking帮助提高应用的加载速度和运行性能,因为较少的代码意味着更少的资源消耗。
- 提高性能:通过减小应用的体积,
- 编译时优化:Flutter 在编译过程中会进行
Tree Shaking,确保在最终的构建产物中只包含必要的代码。
- 编译时优化:Flutter 在编译过程中会进行
4. Spacer 小部件是什么?
- 需要在组件之间添加弹性空白,或者将组件推到主轴起点、中间或末尾(如居右、均匀分隔),优先考虑
Spacer。 - 需要让某个子组件填满剩余空间,或者多个子组件按特定比例分配剩余空间,应使用
Expanded。
总结:Spacer 用于“创造空白”,Expanded 用于“扩展内容”。
5. hot restart 和 hot reload 之间的区别是什么?
定义:
hot restart:是一种重启应用的方式,它会重新加载整个应用及其状态。会清除当前状态,并重新启动应用,因此所有的状态、数据和输入都会丢失。hot reload:是一种快速更新代码的方式,它可以在不重启应用的情况下,立即反映对代码的更改。会保留应用的当前状态,包括用户输入、动画等。这样可以在不丢失进度的情况下查看更改,主要用于更新 UI 方面的代码,如 Widget 的布局或样式。
6. InheritedWidget 是什么?(因海瑞提的·维基特)
- 用于在 Widget 树中向下传递数据。它允许子 Widget 访问其祖先 Widget 中提供的数据,从而实现状态管理和数据共享。
- InheritedWidget 的工作原理是:当在 Widget 树中插入一个 InheritedWidget 时,其子组件可以通过
context.dependOnInheritedWidgetOfExactType()来注册依赖关系。当 InheritedWidget 的数据发生变化并重建时,所有依赖它的子组件都会重新构建。
实际应用场景
主题数据:Flutter 内置的 Theme 就是一个 InheritedWidget
用户信息:在应用多个地方共享用户数据
配置信息:应用配置、语言设置等
状态管理:作为更复杂状态管理方案(如 Provider)的基础
注意事项
性能考虑:只在需要时使用,避免过度使用导致不必要的重建
数据类型:适合共享不变或较少变化的数据
测试:使用 InheritedWidget 的组件更容易测试,因为它们不直接依赖于具体实现。
7. 为什么构建(build)方法在 State 上而不是在 StatefulWidget 上?
构建方法放在 State 类中是为了更好地管理和反映状态变化,使得 StatefulWidget 能够动态响应用户交互和其他条件的变化。这种设计使得 Flutter 的状态管理更加高效和灵活。
从设计逻辑看
State 类不仅负责构建 UI,还包含了管理状态的逻辑。将构建方法放在 State 中,可以更清晰地分离出 Widget 的外观(StatefulWidget)和其行为(State),将构建方法放在 State 中,能够确保 UI 始终反映最新的状态变化
从生命周期看
在 StatefulWidget 中,状态信息可能会发生变化,这些变化需要在构建方法中反映出来。通过将构建方法放在 State 类中,Flutter 能够在状态变化时仅重建相关的部分,提高性能。
8. pubspec 文件在 Flutter 中是什么?
主要用于管理项目的依赖项、元数据和其他设置。以下是该文件的主要功能和构成:
1.可以列出项目所需的外部库(packages),这些库可以通过 Dart 的包管理器 pub 来自动下载和管理
2.项目名称和版本:可以在文件中定义项目的名称、版本、描述等信息。
3.环境配置:可以指定使用的 Flutter SDK 的版本。
9. Flutter 是如何实现原生性能和体验的?
1.渲染引擎
Skia:Flutter 使用 Skia 作为其渲染引擎。Skia 是一个高性能的 2D 图形库,可以直接与底层操作系统的图形 API 进行交互(如 OpenGL 和 Vulkan),从而实现高效的图形渲染。
2.直接访问原生 API
Platform Channels:Flutter 通过平台通道(Platform Channels)与原生代码进行通信。开发者可以在 Flutter 中调用原生 Android 或 iOS 的 API,实现对硬件功能(如相机、GPS 等)的访问。
3.Widget 树
自绘 Widget:Flutter 的 UI 是完全由 Widgets 构成的,Flutter 不依赖于原生 UI 组件,而是通过绘制其自己的组件来实现。从而确保了在不同平台上具有一致的外观和行为。
4.高效的性能
AOT 编译:Flutter 使用 Ahead-of-Time (AOT) 编译,将 Dart 代码编译为原生机器码,从而提高应用的启动速度和运行性能。
5.热重载
开发体验:Flutter 提供热重载功能,使得开发者在进行 UI 修改时可以立即查看效果,而无需重新启动应用,这大大提高了开发效率。
Flutter 渲染原理:
在Flutter渲染的流程中,有三棵树,Flutter引擎渲染是针对Render树中的对象进行渲染。
1.Widget 树 所有Widget都会 调用 creatElement 方法 创建 Element 对象,将Element对象加入Element树中,后面都会调用 mount方法
但是Element分三种:
a.RanderElement 继承 RanderObjectElement ,主要是创建RenderObject,通过mount方法创建RenderObject。
b.StatefulElement 继承 ComponentElement,调用createState方法,创建State,然后将Widget赋值给State对象,最后调用state的buidl方法并且将自己(Element) 传出去
c.StatelessElement 继承 ComponentElement,主要调用build方法,并将自己回调出去。
2.Element 树 :所有继承 Widget的树 都有 Element树,他们属于 1对1 关系。在 Element树中都调用
mount 方法, 然后 mount 里面 不是继承 RenderObjectWidget 就结束,然后是的话,则调用CreatRenderObject 创建RenderObject 。
StatelessWidget的Element
1.StatelessWidget 会创建 Element
- 然后Element创建就会调用 mount方法
3.mount里面会调用 Widget 的build方法进行渲染,并且将自己Element 传递出去。
StatafulWidget
state和 Widget共用一个 element
3.Render 树 (渲染引擎去渲染的树)(只有继承 RenderObjectWidget的才会创建 RenderObject树,才会被渲染引擎渲染 )
10.Key的原理
1.Keys 是一个用于标识 Widget 的对象,帮助 Flutter 识别和管理 Widget 的状态。
1.GlobalKey:
GlobalKey是一个全局唯一的键,一种特殊的 Key。用于跨 Widget 树访问状态和方法。GlobalKey 允许你在不同的 Widget 之间共享状态,特别是在使用 StatefulWidget 时。它的主要用途是确保在 Widget 树重建时仍能保留和访问 Widget 的状态。当 Widget 被重建时,GlobalKey 保持对其状态的引用,因此可以在重建过程中保持状态。
示例:
1.在处理表单时,你可以使用 GlobalKey 来访问和验证表单的状态。
2.使用 GlobalKey 可以直接控制 Widget 的状态方法,比如在动画中调用
3.当你的 Widget 树结构复杂,且多个子 Widget 可能会被重建时,GlobalKey 可以帮助你保持子 Widget 的状态。
注意
谨慎使用:GlobalKey 会破坏 Widget 的封装性,应仅在必要时使用
性能考虑:频繁使用可能影响性能
生命周期:确保在 Widget 被销毁后不再访问其 Key
2.ValueKey:
根据给定的值来识别 Widget,通常用于列表中的元素。
示例:在列表中修改顺序时,确保正确更新每个元素的状态。
3.ObjectKey:
通过对象的引用来识别 Widget,适用于需要比较对象的情况。
4.UniqueKey:
每次创建时都会生成一个唯一的 Key,适合临时 Widget。
11.PageRoute
1.PageRoute 是一个用于管理应用中页面导航的抽象类
2.PageRoute 负责在不同页面(或屏幕)之间进行导航。它管理了页面的堆栈,使得用户可以前往新页面或返回到之前的页面。
3.有两种效果
MaterialPageRoute:用于 Material Design 风格的应用,提供从底部向上推入页面的动画。
CupertinoPageRoute:用于 iOS 风格的应用,提供从右向左推入页面的动画。
4.PageRoute 提供了一些生命周期方法,如 didChangeDependencies 和 dispose,用于在页面进入和退出时执行特定操作。
12.解释 async , await 和 Future
1.async、await 和 Future 是处理异步编程的关键概念。它们可以帮助你编写非阻塞的代码,使得应用能够在执行长时间运行的任务时保持响应。
2.Future 是一个表示可能在未来某个时间点完成的异步操作的对象。它可以用于表示一个延迟的值,通常用于处理异步任务的结果。
状态:
未完成(Pending):Future 仍在进行中。
已完成(Completed):Future 执行完毕,可以获取结果。
已失败(Error):Future 执行过程中出现错误。
3.async 是一个修饰符,用于声明一个异步函数。使用 async 修饰的函数会返回一个 Future,即使函数内部没有显式地返回 Future。在 async 函数中,你可以使用 await 关键字等待一个 Future 完成。
4.await 是一个关键字,用于在 async 函数中等待一个 Future 完成,
使用 await 会暂停 async 函数的执行,直到 Future 完成,并返回结果,让异步代码看起来像同步代码,从而提高代码的可读性。只能在 async 函数中使用。
总结:
Future:表示一个异步操作的结果,可以是未完成、已完成或已失败。
async:用于声明异步函数,返回一个 Future。
await:在 async 函数中等待一个 Future 完成,并获取其结果。
13.你如何动态更新列表视图?
1.利用 StatefulWidget 和 setState() 方法,可以在更新数据时重新构建列表。
2.使用 Provider 或其他状态管理。
3.使用 StreamBuilder
如果数据来自异步源(如网络请求或数据库),可以使用 StreamBuilder 来动态更新列表。
14.stream 是什么??
1.Stream 是一种用于处理异步数据流的机制。它允许你接收一系列异步事件,而不仅仅是单个值。Streams 非常适合处理动态数据源,例如用户输入、网络请求、文件读取等。
2.Stream 允许你以异步的方式接收数据,不会阻塞当前线程。这使得应用在处理数据时仍然能够保持响应。
Stream 的类型
单订阅 Stream:
只能有一个订阅者,适合处理一次性事件流,例如从文件读取数据。
广播 Stream:
可以有多个订阅者,适合处理需要广播给多个监听者的事件,例如用户输入或网络请求。
14. Ticker 、 Tween 和 AnimationController 是什么?
Ticker:用于生成时间片,通常与动画相关联。
Tween:定义动画的起始值和结束值,用于在值之间插值。
AnimationController:控制动画的播放,包括启动、停止和反转。它生成一个时间值(通常在 0.0 到 1.0 之间),用于与 Tween 结合使用。
15. ephemeral 状态是什么?
Ephemeral 状态是 Flutter 中一种局部且短期存在的状态,适合用于管理特定 Widget 的状态。它通过 StatefulWidget 管理,适用于用户输入、动画和 UI 状态等场景。与之相对的是 app 状态(应用状态),后者是需要在多个 Widget 之间共享的持久状态。