一 概述
1
| 本文列出 Flutter 常见面试/开发核心知识点关键词,适用于开发者面试准备、学习梳理
|
二 高频知识点
2.1. Flutter 安装
1-Flutter SDK 安装与环境配置
1 2 3 4 5 6 7 8 9
| Flutter SDK 安装与环境配置
1.下载 SDK: 访问 Flutter 官网下载对应操作系统的 SDK 压缩包。 2.解压 SDK: 将下载的压缩包解压到你希望安装 Flutter 的目录。 3.配置环境变量: 将 Flutter SDK 的 bin 目录添加到系统的环境变量 PATH 中,这样你就可以在命令行中直接使用 flutter 命令。 4.运行 flutter doctor: 在命令行中运行 flutter doctor 命令,它会检查你的环境并报告缺少哪些依赖。 5.安装依赖: 根据 flutter doctor 的提示,安装所需的 Android Studio、Xcode、Gradle 等开发工具和依赖。 6.验证安装: 再次运行 flutter doctor,确保所有必要的依赖都已安装并且没有报错。
|
2-flutter doctor 使用
1 2 3 4 5 6 7 8 9 10 11 12 13
| 1-概念 -flutter doctor 是 Flutter SDK 自带的一个命令行工具, 用于检查你的开发环境是否满足 Flutter 应用开发的要求。
2-当你运行 flutter doctor 命令后,它会: -检查 Flutter SDK 本身的安装是否完整。 -检查你电脑上安装的相关依赖,例如 Android Studio、Xcode、Gradle 等。 -报告任何缺失或配置不正确的依赖,并给出相应的建议或警告。
3-主要用途: -首次安装 Flutter 后,用于验证环境是否配置正确。 -遇到构建或运行问题时,用于诊断环境问题。 -更新 Flutter SDK 后,检查是否有新的依赖需要安装。
|
3-Android Studio / VSCode 插件配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 一、android Studio / VSCode 插件配置
为了更高效地开发 Flutter 应用,你需要在你的集成开发环境 (IDE) 中安装 Flutter 和 Dart 插件。
1.1 Android Studio: -打开设置 (Settings):通常在 "File" 菜单下。 -选择插件 (Plugins):在设置菜单中找到 "Plugins" 选项。 -搜索插件:在插件市场中搜索 "Flutter" 和 "Dart"。 -安装插件:点击 "Install" 按钮安装这两个插件。 -重启 IDE:安装完成后,通常需要重启 Android Studio 使插件生效。
1.2 VSCode: -打开扩展 (Extensions):点击侧边栏的方块图标或使用快捷键 (Ctrl+Shift+X 或 Cmd+Shift+X)。 -搜索插件:在搜索框中输入 "Flutter" 和 "Dart"。 -安装插件:点击每个插件旁边的 "Install" 按钮进行安装。 -无需重启:VSCode 通常在安装后立即启用插件。
二、主要功能:
安装这些插件后,你的 IDE 将支持 Flutter 项目的创建、 代码高亮、代码补全、语法检查、widget 预览、调试以及运行等功能,极大地提升开发体验。
|
4-国内镜像源配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 一、国内镜像源配置
由于国内网络环境的限制,访问 Flutter 官方的一些资源可能会比较慢或不稳定。 配置国内镜像源可以加速 Flutter SDK 的下载、依赖获取和插件更新等过程。
1.1 配置方法(以临时配置为例):
在命令行中执行以下命令来设置临时的环境变量: export PUB_HOSTED_URL=https://pub.flutter-io.cn export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
1.2 持久化配置(推荐):
为了避免每次使用都重新配置,可以将上述环境变量添加到你的 shell 配置文件中(例如 .bashrc、.zshrc 等)。
-打开你的 shell 配置文件。 -在文件末尾添加上述 export 命令。 -保存文件并执行 source ~/.bashrc 或 source ~/.zshrc (取决于你使用的 shell) 来使配置生效。
二、验证配置:
配置完成后,你可以尝试运行 flutter doctor 或创建一个新的 Flutter 项目,观察下载速度是否有所改善。
三、注意: -国内镜像源可能并非总是最新,但通常能满足日常开发需求。 -如果遇到问题,可以尝试切换不同的国内镜像源或者暂时取消镜像源配置,使用官方源。 -通过配置国内镜像源,可以显著提升在国内开发 Flutter 应用的效率。
|
1-StatelessWidget 生命周期
1 2 3 4 5 6 7 8 9 10 11 12
| StatelessWidget 是 Flutter 中表示不可变 UI 部分的组件,一旦创建,其外观和行为就不会改变。 因此,它的生命周期相对简单,只有一个核心阶段:
build(BuildContext context): 这是 StatelessWidget 唯一必须实现的方法。 Flutter 框架会调用这个方法来构建组件的 UI。 它接收一个 BuildContext 对象,用于在 widget 树中定位自身并访问其他 widget 或服务。
总结: StatelessWidget 的生命周期非常短暂,仅在构建时执行一次 build 方法。 之后,widget 保持不变,直到其父 widget 重新构建并创建新的 StatelessWidget 实例。 它没有状态,也没有可以响应生命周期事件的回调方法。
|
2-StatefulWidget 生命周期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| StatefulWidget 是 Flutter 中表示拥有可变状态的 UI 部分的组件。 它的生命周期比 StatelessWidget 复杂,涉及到其关联的 State 对象的生命周期。
一、核心阶段(State 对象的生命周期):
1.1 createState(): StatefulWidget 被创建时,框架会调用这个方法来创建与其关联的 State 对象。 这个方法只会被调用一次。
1.2 initState(): State 对象被创建后,这个方法会被立即调用一次。 通常在这里进行一些初始化操作,例如订阅数据流、初始化动画等。 不能在这里直接使用 BuildContext,因为 widget 尚未完全构建。
1.3 didChangeDependencies(): 在 initState 之后,以及当 widget 的依赖(例如 InheritedWidget)发生变化时,这个方法会被调用。 这是可以使用 BuildContext 的地方。
1.4 build(BuildContext context): 这个方法在 State 对象创建后(initState 或 didChangeDependencies 之后) 以及每次调用 setState() 触发 UI 更新时都会被调用。 它负责构建 widget 的 UI 结构。
1.5 didUpdateWidget(covariant StatefulWidget oldWidget): 当父 widget 重新构建并且传入了相同类型的新的 StatefulWidget 时,这个方法会被调用。 你可以在这里比较新旧 widget 的属性并进行相应的更新。
1.6 deactivate(): 当 State 对象从 widget 树中被移除时,这个方法会被调用。 这通常发生在 widget 被临时移除(例如在 Navigator 中切换页面)但未来可能会重新插入时。
1.7 dispose(): 当 State 对象被永久地从 widget 树中移除时,这个方法会被调用。 在这里进行资源的释放和清理工作,例如取消订阅、停止动画等。 这个方法之后 State 对象将不再可用。
二、总结:
StatefulWidget 的生命周期管理着其关联的 State 对象的创建、更新和销毁过程, 允许开发者在不同的阶段执行相应的逻辑,以实现动态变化的 UI。 核心方法是 createState(创建 State)、initState(初始化)、 build(构建 UI)、setState(触发 UI 更新)和 dispose(资源释放)。
|
3-生命周期调用顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 当一个 StatefulWidget 及其关联的 State 对象被创建、更新和销毁时, 其生命周期方法通常按以下顺序调用:
一、首次创建: 1.1 createState(): (在 StatefulWidget 中调用,创建 State 对象) 1.2 initState(): (在 State 对象中调用,仅一次) 1.3 didChangeDependencies(): (在 State 对象中调用,在 initState 之后) 1.4 build(BuildContext context): (在 State 对象中调用,首次构建 UI)
二、Widget 更新(父 Widget 重新构建并传入新的同类型 Widget): 2.1 didUpdateWidget(covariant StatefulWidget oldWidget): (在 State 对象中调用) 2.2 didChangeDependencies(): (在 State 对象中调用,如果依赖发生变化) 2.3 build(BuildContext context): (在 State 对象中调用,重新构建 UI)
三、Widget 移除(但 State 对象可能被保留): 3.1 deactivate(): (在 State 对象中调用,当 State 对象从 widget 树中临时移除)
四、Widget 销毁: dispose(): (在 State 对象中调用,当 State 对象从 widget 树中永久移除,仅一次)
|
1-Flutter 渲染三棵树:Widget Tree、Element Tree、RenderObject Tree
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 一、三棵树 Flutter 为了高效地渲染 UI,维护了三棵关键的树结构:
1.1 Widget Tree (Widget 树): -这是你编写 Flutter 代码时创建的树。 -它描述了 UI 的结构和配置,本质上是轻量级的不可变对象。 -当状态改变时,Widget 树可能会被完全重建。
1.2 Element Tree (元素树): -Element 树是 Widget 树的一个实例化的表示。 -每个 Widget 节点在 Element 树中都有一个对应的 Element 节点。 -Element 是可变的,负责管理 Widget 的生命周期,并决定是否需要更新底层的 RenderObject。 -Element 树在 Widget 树重建时会进行比较 (reconciliation),尽可能地复用已有的 Element 节点。
1.3 RenderObject Tree (渲染对象树): -RenderObject 树是真正负责 UI 布局和绘制的树。 -每个 Element 对象(通常)会关联一个 RenderObject 对象。 -RenderObject 知道如何计算自身的大小、布局子节点以及在屏幕上绘制内容。 -当 Element 树发生变化时,相应的 RenderObject 会被创建、更新或销毁,并触发实际的渲染过程。
二、总结: Widget 树是蓝图,Element 树是蓝图的实例化管理者,RenderObject 树是最终执行绘制的工人。 Flutter 通过这三棵树的分层管理和高效的比较机制,实现了高性能的 UI 渲染。 当你修改 Widget 时,Flutter 会智能地更新 Element 树和 RenderObject 树,而不是每次都完全重绘整个 UI。
|
2-build vs render
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 在 Flutter 中,build 和 render 是 UI 渲染过程中两个关键但不同的概念:
一、build (构建): -指的是 Flutter 框架根据 Widget 树创建或更新 Element 树的过程。 -当你编写Widget的build方法时,你描述了该Widget应该呈现什么样的UI结构(返回一个新的Widget子树)。 -build 方法可能会被多次调用(例如,当父 Widget 重建或调用 setState 时)。 -build 的目标是描述 UI 的逻辑结构,它本身不直接进行屏幕绘制。
二、render (渲染): -指的是 Flutter 框架根据 RenderObject 树在屏幕上绘制 UI 内容的过程。 -Element 树中的每个 Element(通常)会关联一个 RenderObject。 -RenderObject 负责计算布局、确定大小和实际绘制像素到屏幕上。 -渲染是一个底层过程,由 Flutter 渲染引擎(Skia)执行。 -只有当 RenderObject 的相关属性发生变化时,才会触发实际的重新渲染。
三、简单来说: -build 是“设计”或“描述”UI 的过程,生成 UI 的逻辑结构 (Widget 树和 Element 树)。 -render 是“执行”或“绘制”UI 的过程,将逻辑结构转化为屏幕上的像素 (RenderObject 树)。
|
3-Element 的作用与复用机制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| 一、Element 的作用
Element 在 Flutter 的渲染流程中扮演着核心的中间层角色,它的主要作用是:
-Widget 的实例化代表: Element 是 Widget 在渲染树中的一个动态实例。每个 Widget 都会对应一个 Element。 -管理 Widget 的生命周期: Element 负责处理 Widget 的创建、更新和销毁。 -连接Widget和RenderObject:Element持有与其关联的 Widget 和 RenderObject 的引用,是它们之间的桥梁。 -决定是否需要更新 RenderObject: 当 Widget 树发生变化时,Element 会比较新旧 Widget,并决定是否需要更新已有的 RenderObject 或创建新的。 -维护子 Element 列表: 对于拥有子 Widget 的 Widget,其对应的 Element 会管理子 Element 的列表。
二、Element 的复用机制 (Reconciliation)
Flutter 为了提高性能,在 Widget 树重建时会尝试复用已有的 Element,而不是每次都完全销毁并重新创建。 这个复用机制称为 reconciliation (协调)。
复用的基本原则是:
-Key 的重要性: 如果新旧 Widget 在相同的位置拥有相同的 key,Flutter 会认为它们代表相同的逻辑元素,并尝试更新已有的 Element,而不是创建新的。 -类型匹配: 如果没有key或者key不同,但新旧Widget的类型相同,Flutter 也可能会尝试更新已有的 Element,并更新其配置。 -类型不匹配: 如果新旧 Widget 的类型不同,Flutter 会认为它们是不同的元素,会销毁旧的 Element 及其子树,并为新的 Widget 创建新的 Element 及其子树。
三、总结:
Element 是 Widget 在渲染过程中的动态体现,负责管理 Widget 的生命周期并连接 Widget 和 RenderObject。 通过基于 key 和类型的复用机制,Flutter 能够高效地更新 UI, 避免不必要的对象创建和销毁,从而提升应用的性能。Key 是控制 Element 复用的重要手段。
|
4-Stateless vs Stateful 区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| StatelessWidget vs StatefulWidget 区别
主要区别在于它们是否拥有内部可变的状态:
一、StatelessWidget (无状态组件): -没有内部状态,一旦创建,其外观和行为不会改变。 -依赖于外部传入的信息(例如构造函数中的参数)来决定如何显示。 -通常用于显示静态信息或那些外观和行为在整个生命周期内保持不变的部分。 -生命周期相对简单,只有一个 build 方法。
二、StatefulWidget (有状态组件): -拥有内部状态,这些状态可以在组件的生命周期内发生改变。 -其外观和行为可以根据内部状态的变化而更新。 -通常用于实现动态交互的 UI 部分,例如按钮点击、表单输入、动画等。 -包含两个核心部分: StatefulWidget本身(不可变)和一个关联的State对象(可变),State对象负责管理组件的状态和构建UI。 生命周期更复杂。
三、简单来说: -StatelessWidget 是静态的,一旦创建就不会改变。 -StatefulWidget 是动态的,其内部状态可以改变,并触发 UI 的更新。
|
2.4. Flutter 增量渲染与 Key
1-Flutter 的增量更新机制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| Flutter 的增量更新机制,也称为 热重载 (Hot Reload) 和 热重启 (Hot Restart), 旨在在开发过程中快速预览代码更改,而无需完全重新编译和重新启动应用程序,从而显著提升开发效率。
一、热重载 (Hot Reload): -速度非常快,通常在几秒内完成。 -主要用于更新代码逻辑和 UI 结构。 -尝试保留应用程序的当前状态(例如,用户输入、滚动位置等)。 -通过将修改后的 Dart 代码注入到正在运行的应用程序的 Dart 虚拟机 (VM) 中实现。 -并非所有更改都能热重载成功。例如,对 initState 方法、静态变量的修改,以及一些底层架构的更改可能需要热重启。
二、热重启 (Hot Restart): -比热重载慢,但比完全重新启动应用程序要快。 -会重新启动 Dart VM,这意味着应用程序的所有状态都会丢失。 -但它不会完全重新编译原生代码,因此比完整构建要快得多。 -通常在热重载失败或进行了需要更深层次更新的更改时使用。
三、总结: Flutter 的增量更新机制通过 热重载快速预览 UI 和逻辑更改并尝试保留状态, 以及通过 热重启在必要时进行更深层次的更新但仍比完整构建快,极大地缩短了开发迭代周期,提高了开发效率。 开发者可以根据代码更改的性质选择使用热重载或热重启。
|
2-Widget Diff 算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 一、概念 Flutter 的 Widget Diff 算法是其高效 UI 更新机制的核心。 当 Widget 树发生变化(例如,由于 setState 或父 Widget 重建),Flutter 不会直接销毁并重建整个 UI 树, 而是会比较 (diff) 新旧两棵 Widget 树的结构,找出差异 (differences), 然后仅更新需要改变的部分,从而提高性能。
二、其基本原理和策略包括:
2.1 类型和 Key 的比较: 算法首先比较同一位置的新旧 Widget 的类型 (runtimeType) 和 Key。 -如果类型和 Key 都相同,Flutter 会认为这两个 Widget 代表相同的逻辑元素, 并尝试更新 (update) 对应的 Element,而不是创建新的。 -如果 Key 相同但类型不同,Flutter 会认为这是一个完全不同的元素,会销毁旧的 Element 并创建新的。 -如果 Key 不同,即使类型相同,Flutter 也可能将其视为不同的元素。
2.2 深度优先遍历: Diff 算法通常以深度优先的方式遍历 Widget 树。 2.3 同级比较: Flutter 主要进行同级 (sibling) 比较。它不会尝试将一个旧树深处的 Widget 移动到新树的浅层位置。 这意味着如果你改变了列表项的顺序但没有使用 Key,Flutter 可能会销毁并重新创建这些项,而不是移动它们。
2.4 Key 的重要性: Key 是控制 Widget 复用的关键。 通过为需要跨重建保持状态或身份的 Widget 提供唯一的 Key, 你可以帮助 Flutter 更准确地识别它们,并进行更高效的更新,例如在列表重排序时。
三、总结:
Flutter 的 Widget Diff 算法通过比较新旧 Widget 树的类型和 Key,以深度优先和同级比较的方式找出差异, 并仅更新发生变化的部分,从而实现了高效的 UI 增量更新。合理使用 Key 对于算法的优化至关重要。
|
3-Key 的作用与使用场景(GlobalKey / ValueKey)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| 一、Key 的作用
在 Flutter 中,Key 是用于唯一标识 Widget 树中的 Element 的对象。 它的主要作用是帮助 Flutter 的 Widget Diff 算法在重建 UI 树时更准确地识别和复用已有的 Element, 从而提高更新效率并维护组件的状态。
二、主要作用总结:
2.1 控制 Widget 的复用: 当 Widget 树发生变化时,拥有相同 Key 的相同类型 Widget 会被认为是同一个逻辑上的组件, Flutter 会尝试更新其对应的 Element,而不是销毁并重新创建。 2.2 维护组件状态: 通过复用 Element,Key 可以帮助跨重建保持组件的内部状态(例如,输入框的文本、滚动位置等)。 2.3 在 Widget 树中引用特定的 Widget/Element/RenderObject: GlobalKey 提供了访问特定 Widget、Element 或 RenderObject 的能力,即使它们在树中的位置发生了变化。
三、使用场景与 Key 类型:
3.1 ValueKey: 作用: 基于数据值来标识 Widget。当 Widget 所代表的数据在重建后仍然相同时, 即使其在 Widget 树中的位置可能发生变化,Flutter 也能识别并复用它。
使用场景: -列表项重排序: 当列表项的数据不变但顺序改变时,使用基于数据唯一标识的ValueKey可以让Flutter正确地移动Element并保持其状态。 -简单的同类型 Widget列表: 用于区分列表中具有相同 UI 但数据不同的 Widget。
3.2 GlobalKey: 作用: 提供了一个全局唯一的标识符,允许你在整个应用程序中访问特定的 Widget、Element 或 RenderObject。
使用场景: -访问子 Widget 的状态或方法: 例如,获取 Form 组件的状态来验证表单,或者调用 TextFormField 的 focusNode 来控制焦点。 -在不同的 Widget 树分支中引用同一个 Widget: 例如,在不同的页面或不同的父 Widget 中操作同一个底层的组件。 -控制动画: 访问 AnimationController 来启动或停止动画。 -获取 RenderObject 的信息: 例如,获取 Widget 的大小和位置。
四、简单总结: 使用 ValueKey 来基于数据标识和复用 Widget,常用于列表等场景。 使用 GlobalKey 来获取全局唯一的 Widget/Element/RenderObject 引用,用于跨树访问和操作特定的组件。
|
4-Key 与 Element 绑定原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| 一、概念 当 Flutter 构建 UI 树时,对于每一个 Widget 节点, 框架会尝试在现有的 Element 树中找到一个可以复用的 Element。 Key 在这个复用过程中起着至关重要的作用。
二、绑定原理可以概括为:
2.1 Widget 创建时携带 Key: 当你在代码中创建一个 Widget 并为其指定一个 Key (可以是 ValueKey 或 GlobalKey) 时, 这个 Key 就成为了该 Widget 配置信息的一部分。
2.2 Element 创建或更新时关联 Key: 当 Flutter 首次为这个 Widget 创建一个 Element 时,该 Widget 的 Key 会被存储在这个 Element 对象中。如果 Widget 树发生变化,需要更新已有的 Element,新的 Widget 也会携带一个 Key。
2.3 Diff 算法利用 Key 进行匹配: 在 Widget 树的协调 (reconciliation) 过程中,Flutter 的 Diff 算法会遍历新旧 Widget 树的同级节点。 在比较时,算法会优先检查新旧 Widget 是否拥有相同的 Key。
三、Key 匹配决定 Element 的复用:
3.1 如果新旧 Widget 在相同位置拥有相同的 Key 和相同的 Widget 类型, Flutter 会认为它们代表同一个逻辑上的 UI 元素,并复用旧的 Element,仅仅更新其配置(即新的 Widget)。 这样可以保留 Element 关联的 RenderObject 和可能存在的内部状态。
3.2 如果 Key 相同但 Widget 类型不同, Flutter 会认为这是一个不同的元素,会销毁旧的 Element 并为新的 Widget 创建一个新的 Element。
3.3 如果没有 Key 或 Key 不同,即使 Widget 类型相同, Flutter 也可能无法判断它们是否是同一个逻辑元素,可能会创建新的 Element。
四、GlobalKey 的特殊绑定: GlobalKey 的工作方式略有不同。 它在创建时会在全局的 Key 注册表中注册自身,并与创建它的 Element 建立关联。 这使得可以通过 GlobalKey 在任何地方直接访问到其关联的 Element (以及 Widget 和 RenderObject), 而不仅仅依赖于树的结构和比较。
五、总结:
Key 本身是 Widget 配置的一部分。 在 Element 的创建和更新过程中,Key 会被存储在 Element 对象中。 Diff 算法在比较新旧 Widget 树时会利用 Key 来判断哪些 Element 可以被复用。 拥有相同 Key 的同类型 Widget 更倾向于复用同一个 Element,从而维护状态和提高更新效率。 GlobalKey 则提供了更强的全局引用能力,通过全局注册表实现与特定 Element 的绑定。
|
2.5. Flutter 布局
1-布局原理:父传约束,子给大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 一、Flutter 的布局系统基于一个核心原则: 父 Widget 向其子 Widget 传递布局约束 (Constraints),子 Widget 根据这些约束确定自身的大小 (Size)。
2.1 父传约束 (Parent Imposes Constraints): -父 Widget 在布局其子 Widget 时,会定义子 Widget 的布局限制。 -这些约束通常包括: -最小宽度 (minWidth) -最大宽度 (maxWidth) -最小高度 (minHeight) -最大高度 (maxHeight)
-父 Widget 决定了子 Widget 可以有多大或多小,但不直接指定子 Widget 的具体尺寸。
2.2 子给大小 (Child Determines Size): -子 Widget 在接收到父 Widget 的约束后,会根据自身的逻辑和内容,在父 Widget 允许的范围内选择自己的大小。 -子 Widget 的 build 方法会返回描述其期望大小和布局的子 Widget 树。 -最终的大小必须满足父 Widget 传递的约束。 例如,子 Widget 的宽度不能超过 maxWidth,高度不能小于 minHeight。
三、简单来说: 父 Widget 说:“你可以在这个宽度和高度范围内选择你的大小。” 子 Widget 回答:“好的,我决定我的宽度是 X,高度是 Y。” 其中 X 和 Y 必须在父 Widget 给定的范围内。
这个单向的数据流(父到子传递约束,子到父反馈大小)使得 Flutter 的布局系统具有可预测性、灵活性和高效性。 不同的父 Widget 可以施加不同的约束,而子 Widget 可以根据这些约束和自身的需求进行布局。
|
2-多子布局(Column / Row / Stack)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 一、Column (垂直列): -作用: 将其子 Widget 垂直方向排列成一列。 -约束: 父 Widget 通常会提供水平方向的约束(宽度), Column 会尝试在垂直方向上尽可能地展开以适应所有子 Widget,除非受到父 Widget 的垂直约束。 -大小: Column 的宽度通常由其最宽的子 Widget 决定(可以配置 crossAxisAlignment 来调整水平对齐), 高度则取决于所有子 Widget 的总高度。 -特点: 适用于垂直方向上需要顺序排列多个元素的场景。
二、Row (水平行): -作用: 将其子 Widget 水平方向排列成一行。 -约束: 父 Widget 通常会提供垂直方向的约束(高度), Row 会尝试在水平方向上尽可能地展开以适应所有子Widget,除非受到父 Widget 的水平约束。 -大小: Row 的高度通常由其最高的子 Widget 决定(可以配置 verticalDirection 和 crossAxisAlignment 来调整垂直对齐),宽度则取决于所有子 Widget 的总宽度。 -特点: 适用于水平方向上需要并排显示多个元素的场景。
三、Stack (堆叠): -作用: 将其子 Widget 堆叠在一起,后面的子 Widget 会覆盖在前面的子 Widget 之上。 -约束: 父Widget通常会为 Stack 提供宽度和高度的约束, Stack会将这些约束传递给其非定位 (non-positioned) 的子 Widget。 -大小: Stack 的大小通常由其最大的非定位子 Widget 决定,或者可以根据 fit 属性来适应父 Widget 的约束。 -定位: 可以使用 Positioned Widget来显式指定子Widget在Stack中的位置 (例如,距离顶部、底部、左侧、右侧的距离)。 -特点: 适用于需要元素重叠显示的场景,例如添加水印、覆盖层、动画效果等。
四、总结: -Column 用于垂直排列子元素。 -Row 用于水平排列子元素。 -Stack 用于将子元素堆叠在一起,并允许使用 Positioned 进行精确定位。
这些是 Flutter 中最基础和常用的多子布局 Widget,通过组合和嵌套它们,可以构建出各种复杂的 UI 结构。
|
3-单子布局(Container / Align / Center)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| 一、Container (容器): -作用: 是一个功能丰富的单子 Widget,可以包装另一个 Widget,并为其添加各种视觉效果和布局特性。 功能: -尺寸控制: 可以设置 width 和 height 来显式指定其大小,或者根据子 Widget 和约束自适应。 -内外边距: 提供 padding(内边距)和 margin(外边距)来调整子 Widget 周围的空间。 -背景: 可以设置 color 作为背景色,或者使用 decoration 提供更复杂的背景(例如渐变、图片、边框)。 -对齐: 可以使用 alignment 属性来控制子 Widget 在 Container 内的对齐方式。 -变换: 支持 transform 属性进行矩阵变换(例如旋转、缩放)。 -特点: 非常灵活和常用,是构建 UI 时的基本 building block。
二、Align (对齐): -作用: 调整其子 Widget 在父 Widget 提供的空间内的对齐方式。 -对齐方式: 通过 alignment 属性控制,它接受一个 Alignment 对象,例如 Alignment.center、Alignment.topLeft、Alignment.bottomRight 等。 -尺寸: Align 本身的大小取决于其父 Widget 提供的约束以及子 Widget 的大小。 默认情况下,它会尽可能大地占据父 Widget 的空间,并根据 alignment 对齐其子 Widget。 可以通过 widthFactor 和 heightFactor 缩小 Align 本身的大小。 -特点: 专注于控制子 Widget 的位置,自身可以根据需要调整大小。
三、Center (居中): -作用: 将其子 Widget 在其父 Widget 提供的空间内居中显示。 简化版的 Align: 本质上是一个 alignment 设置为 Alignment.center 的 Align Widget。 -尺寸: 行为与 Align 类似,会尽可能大地占据父 Widget 的空间,然后将其子 Widget 居中。 -特点: 专门用于快速实现子 Widget 的居中对齐,是 Align 的一个常用快捷方式。
四、总结: -Container 是一个多功能容器,可以包装子 Widget 并添加尺寸、边距、背景、对齐等效果。 -Align 专注于控制子 Widget 在可用空间内的对齐方式。 -Center 是一个便捷的 Widget,用于将其子 Widget 居中显示。
|
4-布局流程:performLayout / constraints / size
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| Flutter 的布局流程主要围绕着 约束 (constraints) 和 大小 (size) 的传递与计算, 而 performLayout 是执行实际布局的核心方法。
一、Constraints (约束): -是父 Widget 传递给子 Widget 的布局限制条件。 -包括最小宽度、最大宽度、最小高度和最大高度。 -父 Widget 决定了子 Widget 可以有多大或多小,但不直接指定其大小。
二、performLayout (执行布局): -这是 RenderObject 类中的一个核心方法。 -每个负责布局的 RenderObject(例如,RenderBox)都必须实现这个方法。 -当一个 RenderObject 需要确定其子 RenderObject 的大小和位置时,框架会调用其 performLayout 方法。 -在 performLayout 内部,RenderObject 会: -接收来自父 RenderObject 的约束 (constraints)。 -根据自身的逻辑和子 RenderObject 的需求,将合适的约束传递给它的子 RenderObject。 -调用其子 RenderObject 的 performLayout 方法,让子节点确定它们的大小。 -根据子节点的大小和自身的布局规则,确定自身的大小 (size) 和子节点在自身坐标系中的位置 (offset)。 -将自身的大小存储在 size 属性中。
三、Size (大小): -是 RenderObject 在满足父 Widget 的约束后确定的自身尺寸(宽度和高度)。 -子 RenderObject 在 performLayout 中计算出自身的大小后,会将这个大小信息传递回父 RenderObject。 -父 RenderObject 根据子节点的大小和其他布局规则,最终确定自身的大小。
四、总结:
布局流程是一个自上而下传递约束 (constraints),再由下而上确定大小 (size) 的过程。 父 Widget 通过 constraints 告诉子 Widget 它们可以有多大的范围, 子 Widget 在其 performLayout 方法中根据这些约束计算出自己的 size, 并将大小信息传递回父 Widget,最终完成整个布局。 performLayout 是每个 RenderObject 执行实际布局计算的核心方法。
|
2.6. FlutterEngine
1-FlutterEngine 启动流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| FlutterEngine 启动流程简述:
1.初始化引擎环境 创建 FlutterEngine 实例,加载 Flutter Runtime 和 Dart VM。
2.设置 Dart 执行环境 加载 isolate、初始化 Dart 入口函数 main(),绑定 Dart 执行上下文。
3.资源加载 加载 Flutter 应用的 flutter_assets 资源(如 Dart 代码、图片、字体等)。
4.平台通道初始化 建立 PlatformChannel,用于 Dart 与原生通信(MethodChannel、EventChannel等)。
5.执行 Dart 代码 调用 run 启动 Dart main() 方法,进入 Flutter UI 渲染流程。
6.渲染准备 初始化 Skia 渲染引擎,连接渲染表面,准备绘制 Flutter UI。
FlutterEngine 启动后,Flutter UI 框架即可运行并与原生系统交互。
|
2-FlutterActivity 与 FlutterEngine 关系
1 2 3 4 5 6 7 8 9 10 11 12 13
| 一、FlutterActivity 与 FlutterEngine 的关系简述:
FlutterActivity 是 Android 中用于承载 Flutter 界面的原生 Activity, 而 FlutterEngine 是 Flutter 应用运行的引擎核心,负责执行 Dart 代码、管理插件、处理渲染等。
二、两者关系如下: -FlutterActivity 内部持有 FlutterEngine; -FlutterActivity 负责创建或绑定一个 FlutterEngine; -FlutterEngine 负责真正运行 Flutter 应用逻辑; -一个 FlutterEngine 可以被多个 FlutterActivity 复用(如预热引擎场景)。
三、简而言之: FlutterActivity 是容器,FlutterEngine 是引擎核心。
|
3-FlutterEngineGroup 多引擎场景
1 2 3 4 5 6 7 8 9 10 11
| 一、FlutterEngineGroup 多引擎场景简述:
FlutterEngineGroup 是 Flutter 提供的多引擎管理类, 用于在一个进程中高效创建多个 FlutterEngine 实例,适用于多 Flutter 页面或多 Flutter 模块共存的场景。
二、其优势和用途包括: -共享资源:多个引擎可复用同一套 Dart 代码、Flutter 资源、AOT 数据,提升初始化效率。 -快速创建引擎:比单独创建 FlutterEngine 更快,适合多页面快速切换。 -独立执行上下文:每个 FlutterEngine 都有自己的 Dart isolate,可运行不同 Dart 页面逻辑。
三、常用于:多 Flutter 页面(如多个业务模块)同时存在或切换的场景。
|
4-FlutterEngine 缓存与复用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 一、FlutterEngine 缓存与复用简述:
为了减少启动开销、加快页面加载,Flutter 提供了 FlutterEngine 缓存与复用机制,即:
-使用 FlutterEngineCache 将已初始化好的 FlutterEngine 缓存起来; -通过 FlutterActivity 或 FlutterFragment 绑定已有的引擎实例(而不是重新创建); -避免重复初始化 Dart VM、资源加载,提高性能,适合多 Flutter 页面或首屏优化。
二、常见使用场景: -App 启动时预热引擎; -多页面共享同一引擎; -快速跳转 Flutter 页面无黑屏。
三、简而言之: 通过缓存并复用 FlutterEngine,可以显著提升 Flutter 页面加载速度和用户体验。
|
2.7. Flutter_Channel(平台通信)
1-Platform Channel(基础)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 一、Platform Channel(基础)简述:
Platform Channel 是 Flutter 与原生(Android/iOS)进行通信的机制, 用于在 Dart 与原生代码之间传递数据和调用方法。
二、基本原理: -Dart 端 通过 MethodChannel、EventChannel 等发起请求; -原生端 实现对应的处理逻辑(如 Android 的 MethodCallHandler); -双方通过 消息编码机制(如 JSON、标准消息编码) 传递数据。
三、常见用途: -调用原生功能:如相机、定位、音视频播放; -获取原生数据:如系统版本、电池状态; -与原生 UI、SDK 交互。
四、 简而言之: Platform Channel 让 Dart 和原生代码互相调用,实现 Flutter 与系统的深度融合。
|
2-MethodChannel / EventChannel / BasicMessageChannel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 一、MethodChannel / EventChannel / BasicMessageChannel 简述:
1.1 MethodChannel 用于 单次请求与响应 的通信。Dart 端发送方法调用请求,原生端处理并返回结果。适用于同步或异步的请求响应场景。 典型用途:调用原生功能(如获取电池电量、打开相机)。
1.2 EventChannel 用于 持续的数据流 或 事件监听。 Dart 端通过 EventChannel 订阅原生端的事件流,原生端通过 sink 向 Dart 端发送事件流数据。 适合用于实时数据或通知。
典型用途:接收原生事件(如加速度计数据、传感器变化)。
1.3 BasicMessageChannel 用于 发送和接收消息,支持 异步和数据流 的双向通信。它不涉及方法调用,只是传递消息,常用于简单的数据交换。 典型用途:传输简单的消息数据(如 JSON、字符串等)。
二、简而言之: -MethodChannel 处理请求与响应; -EventChannel 处理事件流; -BasicMessageChannel 处理消息传递。
|
3-原理与序列化(StandardMethodCodec)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 一、原理与序列化(StandardMethodCodec)简述:
StandardMethodCodec 是 Flutter 中用于 序列化和反序列化数据 的编码解码器。 它用于 MethodChannel 和 EventChannel 中的数据传输,将 Dart 与原生平台之间的消息进行编码和解码。
二、原理: 2.1序列化(编码):将 Dart 端的数据(如对象、字符串、数字等)转换为原生平台可以理解的格式(如二进制流)。 2.2 反序列化(解码):将原生平台传来的二进制数据转换为 Dart 端可以操作的格式(如Dart对象、List、Map等)。
StandardMethodCodec 使用 标准的消息格式 来保证跨平台通信的兼容性和一致性, 确保 Dart 和原生端之间的数据正确传递。
三、典型用途: -Dart 调用原生方法时,使用 StandardMethodCodec 对方法参数和返回值进行编码解码; -适用于 基本数据类型 的传输,如整数、字符串、布尔值、列表等。
四、简而言之: StandardMethodCodec 负责在 Dart 与原生平台之间进行数据的序列化与反序列化,确保数据的正确传输。
|
4-Android / iOS 与 Dart 互调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 一、Android / iOS 与 Dart 互调简述:
Dart 与原生 Android / iOS 互调 是 Flutter 中实现跨平台功能的核心机制, 主要通过 Platform Channels 完成,允许 Dart 与原生代码进行双向通信。
1.1 Android 与 Dart 互调:
-使用 MethodChannel、EventChannel 等机制, Dart 端通过 Platform Channel 调用 Android 的原生功能(如相机、传感器等),并接收返回结果。
-Android 端通过实现 MethodChannel 的处理方法,响应 Dart 的请求并返回数据。
1.2 iOS 与 Dart 互调:
-类似于 Android,Dart 通过 Platform Channel 调用 iOS 原生代码,实现数据交互。 -iOS 端通过实现对应的 FlutterMethodChannel 来处理 Dart 端的调用并返回数据。
二、典型场景: -调用原生硬件功能(如相机、定位、传感器); -获取系统信息(如电池状态、网络状态); -实现 Flutter 与原生平台的交互。
三、简而言之: 通过 Platform Channel,Dart 与 Android / iOS 实现互调,允许 Flutter 应用调用原生功能并处理返回结果。
|
2.8. Flutter 混合开发
1-Boost / 原生与 Flutter 混合栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 一、Boost / 原生与 Flutter 混合栈简述:
Boost 是一个专门用于 提升 Flutter 和原生平台混合开发 的工具和方法, 旨在让 Flutter 和原生代码更高效地协同工作,尤其适用于现有原生应用需要逐步迁移或添加 Flutter 功能的场景。
二、原生与 Flutter 混合栈 的核心是通过 Flutter Engine 与原生平台的通信和协作来实现。 具体来说,原生和 Flutter 代码之间可以通过以下方式互操作:
2.1 原生应用嵌入 Flutter: -在现有的原生应用中,通过集成 FlutterEngine 或 FlutterActivity 来加载 Flutter 页面。 -原生代码可以嵌套 Flutter 页面,部分功能使用原生开发,部分使用 Flutter 实现。
2.2 Flutter 调用原生功能: -使用 Platform Channels 在 Dart 和原生代码之间进行通信,调用原生功能(如摄像头、GPS、蓝牙等)。 -MethodChannel、EventChannel 用于在 Dart 和原生之间传递数据和事件。
2.3 原生调用 Flutter 功能: 原生应用可以通过调用 FlutterEngine 实现 Flutter 页面、视图或者特定功能的展示, 甚至嵌入 Flutter Widget 到原生界面。
2.4 Boost 混合开发框架: Boost 提供的混合开发框架帮助原生和 Flutter 代码更好地协作,提升开发效率, 尤其在逐步迁移原生应用到 Flutter 时,提供了一些便捷的 API 和工具来简化集成过程。
三、典型场景: -在现有原生应用中嵌入 Flutter 页面进行快速开发; -逐步迁移原生应用到 Flutter; -原生功能与 Flutter 页面互通。
四、简而言之: Boost / 原生与 Flutter 混合栈帮助开发者在原生应用中逐步集成 Flutter,实现高效协同与功能扩展。
|
2-Flutter 模块集成到原生 App(Add to App)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| 一、Flutter 模块集成到原生 App(Add to App)简述:
Add to App 是 Flutter 提供的一种功能, 允许开发者将 Flutter 模块集成到现有的原生 Android 或 iOS 应用中。 通过这种方式,原生应用可以逐步引入 Flutter 页面,而无需完全重写原生应用。
二、集成步骤:
2.1 创建 Flutter 模块: 使用 Flutter 创建一个单独的 Flutter 模块,通过命令 flutter create --template module 创建。
2.2 集成 Flutter 模块到原生应用: -在 Android 项目中,通过 flutter.gradle 配置,引用 Flutter 模块并将其嵌入到现有的 Android 项目中; -在 iOS 项目中,通过 CocoaPods 配置,集成 Flutter 模块到现有的 iOS 项目中。
2.3 原生与 Flutter 交互: -原生应用可以通过 FlutterActivity 或 FlutterViewController 显示 Flutter 页面; -使用 Platform Channels 在 Dart 与原生代码之间进行数据交互。
2.4 运行与调试: Flutter 模块在原生应用中运行时,依然能够利用 Flutter 的热重载和调试功能,方便开发和测试。
三、典型场景: -在现有原生应用中逐步引入 Flutter 页面; -不需要重写整个应用,按需添加 Flutter 功能; -结合原生功能与 Flutter 高效开发界面。
四、简而言之: Add to App 让开发者将 Flutter 模块集成到现有原生应用中,快速实现跨平台功能,而无需完全重写原生应用。
|
3-混合栈生命周期管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 一、混合栈生命周期管理简述:
在 混合栈(Hybrid Stack)中,Flutter 与原生应用共存,其生命周期管理需要协调两者之间的状态和资源使用。 主要涉及如何管理 Flutter 页面 和 原生页面 的生命周期,确保两者的协同工作。
1.1 原生生命周期管理: -原生应用(如 Android 或 iOS)使用标准的生命周期方法(如 onCreate、onResume 等) 来管理原生页面和 Flutter 页面之间的切换。
-当进入 Flutter 页面时,原生生命周期暂停或停止,返回原生页面时,Flutter 生命周期需暂停或销毁。
1.2 Flutter 生命周期管理:
-Flutter 页面通过 WidgetsBindingObserver 监听原生生命周期状态(如 pause、resume), 确保 Flutter 页面在应用切换时适当响应。 -使用 StatefulWidget 和 StatelessWidget 来管理 Flutter 页面中的状态和资源。
1.3 混合栈中的资源管理: -需要特别关注原生与 Flutter 之间的 资源释放,如内存管理、线程管理等,防止资源泄漏。 -Flutter 页面与原生页面之间的数据传递和状态同步需要精确管理。
1.4 同步原生和 Flutter 状态: 使用 Platform Channels 在原生与 Flutter 之间传递生命周期事件和数据,确保两者的状态同步(如暂停、恢复)。
二、简而言之: 混合栈生命周期管理就是在原生和 Flutter 页面之间协调生命周期,确保两者的切换、资源管理与状态同步。
|
4-页面路由管理与通信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 页面路由管理与通信简述:
在 Flutter 中,页面路由管理与通信 主要涉及如何在应用中导航、管理不同页面之间的跳转和数据传递。 它确保页面之间的切换流畅并且能够有效地传递信息。
一、页面路由管理: -命名路由:通过 Navigator.pushNamed() 和 Navigator.pop() 等方法实现页面跳转。适用于简单的页面导航。 -匿名路由:通过 Navigator.push() 和 Navigator.pop() 传递页面的实例,适用于复杂的页面切换。 -路由参数传递:在路由跳转时,可以通过路由参数传递数据。 例如,在 Navigator.pushNamed() 中传递参数, 接收页面通过 ModalRoute.of(context)!.settings.arguments 获取。
二、页面间通信: -回调函数:在页面跳转时,传递回调函数,待目标页面完成任务后通过回调传递结果。 -InheritedWidget 和 Provider:通过全局状态管理的方式,在不同页面之间共享和传递数据。 -Stream 与 EventChannel:通过流和事件通道传递实时数据,适用于需要实时更新的场景。
三、路由与导航管理: -使用 Navigator 管理堆栈中的页面,控制页面的入栈与出栈。 -使用 Router 和 Route 对象进一步细化路由管理,支持动态路由、嵌套路由等。
四、简而言之: 页面路由管理与通信确保应用中不同页面的跳转和数据传递流畅,提供灵活的导航与状态共享机制。
|
2.9. Flutter 包与插件
1-pubspec.yaml 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| 一、pubspec.yaml 配置简述:
pubspec.yaml 是 Flutter 项目的配置文件,用于定义项目的依赖、资源和其他配置信息。 它是 Flutter 项目的核心文件之一,直接影响到项目的构建和运行。
二、主要配置项如下: -name:项目名称。 -description:项目描述。 -version:项目版本,格式为 major.minor.patch。 -dependencies:定义项目依赖的外部包(如第三方库)。可以指定版本号或使用最新版本。 -dev_dependencies:开发时依赖的包,通常用于测试、调试等工具。 -flutter:与 Flutter 相关的配置项,主要包括: -assets:项目中使用的静态资源,如图片、字体等。 -fonts:项目中使用的自定义字体。 -environment:指定 Dart 和 Flutter 的版本范围,确保兼容性。 -dependency_overrides:允许覆盖依赖版本,用于解决依赖冲突。
三、配置示例:
name: my_flutter_app description: A new Flutter project version: 1.0.0+1
dependencies: flutter: sdk: flutter cupertino_icons: ^0.1.3
flutter: assets: - assets/images/ - assets/icons/
dev_dependencies: flutter_test: sdk: flutter
三、简而言之: pubspec.yaml 配置文件用于管理 Flutter 项目的依赖、资源、版本信息和构建配置。
|
2-自定义 Dart 包 / 插件结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| 一、自定义 Dart 包 / 插件结构简述:
在 Flutter 或 Dart 中,自定义 Dart 包/插件 允许开发者封装功能并在多个项目中复用。 自定义包通常分为 Dart 包 和 Flutter 插件,两者结构略有不同。
1.1 Dart 包结构: 一个简单的 Dart 包包含以下基本文件和目录: -lib/:存放 Dart 代码的目录,所有公共接口和功能实现放在这里。 -pubspec.yaml:包的配置文件,定义包的名称、版本、依赖等信息。 -test/:存放单元测试代码的目录。
示例结构: my_package/ ├── lib/ │ └── my_package.dart ├── pubspec.yaml └── test/ └── my_package_test.dart 1.2. Flutter 插件结构: Flutter 插件不仅包括 Dart 代码,还涉及原生平台代码(如 Android 和 iOS 的原生实现)。插件结构通常包括:
-lib/:存放 Flutter 端的 Dart 代码。 -android/:存放 Android 原生实现代码。 -ios/:存放 iOS 原生实现代码。 -example/:包含一个示例应用,展示如何使用插件。
示例结构: my_plugin/ ├── lib/ │ └── my_plugin.dart ├── android/ │ └── src/main/java/com/example/myplugin/MyPlugin.java ├── ios/ │ └── Classes/MyPlugin.swift ├── example/ │ └── lib/main.dart └── pubspec.yaml
二、简而言之: 自定义 Dart 包/插件通过标准的目录结构组织代码,Dart 包仅包含 Dart 代码, 而 Flutter 插件还涉及原生平台代码,便于与原生功能集成。
|
3-插件开发(支持原生平台)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| 一、插件开发(支持原生平台)简述:
Flutter 插件 是为了让 Flutter 应用能够调用原生平台的功能(如 Android 和 iOS)而开发的包。 插件通常包括 Flutter 端的 Dart 代码以及对应的原生平台代码(Android 和 iOS), 通过 Platform Channels 实现 Flutter 与原生平台的通信。
二、插件开发步骤: 2.1 创建插件: -使用命令 flutter create --template=plugin 创建一个插件模板。 -插件目录结构会自动包含 Dart 代码(Flutter 端)和原生平台代码(Android 和 iOS 端)。
2.2 实现原生功能: -Android端:在android/src/main/java/ 中编写 Java 或 Kotlin 代码,使用原生 Android API 实现功能。 -iOS 端:在 ios/Classes/ 中编写 Swift 或 Objective-C 代码,调用原生 iOS API 实现功能。
2.3 Flutter 端调用原生功能: -在Dart代码中使用 MethodChannel 或 EventChannel 调用原生功能,通过消息传递参数并获取返回结果。 -插件会将 Flutter 端的调用转发到原生端,并将原生端的返回数据传递回 Flutter。
2.4 测试与发布: -在插件中创建示例应用,确保插件在 Android 和 iOS 上正常工作。 -发布插件到 pub.dev 供其他开发者使用。
三、示例插件结构: my_plugin/ ├── lib/ │ └── my_plugin.dart # Flutter 端的 Dart 代码 ├── android/ │ └── src/main/java/... # Android 端的 Java/Kotlin 代码 ├── ios/ │ └── Classes/... # iOS 端的 Swift/Objective-C 代码 ├── example/ │ └── lib/main.dart # 插件的示例应用 └── pubspec.yaml # 插件的配置文件
三、简而言之: 插件开发通过编写 Flutter 端的 Dart 代码和原生端的 Android/iOS 代码, 使用 Platform Channels 实现跨平台功能调用,使 Flutter 应用能够访问原生平台的功能。
|
4-发布插件到 pub.dev 流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| 发布插件到 pub.dev 流程简述:
发布 Flutter 插件到 pub.dev 的过程,允许其他开发者使用和依赖你的插件。
以下是简要的步骤:
1. 准备插件代码: 1.1 确保插件的代码已完成并通过测试。 1.2 插件应该包含: -lib/ 目录,存放插件的 Dart 代码。 -android/ 和 ios/ 目录,存放 Android 和 iOS 的原生实现。 -example/ 目录,提供示例应用来展示插件的使用。
2. 填写 pubspec.yaml 配置: -确保 pubspec.yaml 文件正确填写,包括: -插件的名称、描述、版本号等信息。 -依赖的 Flutter SDK 和其他包的版本。 -插件支持的平台(Android、iOS)。
示例: name: my_plugin description: A Flutter plugin example version: 1.0.0 dependencies: flutter: sdk: flutter 3. 验证插件是否符合要求: 使用 flutter pub publish --dry-run 命令来检查插件是否符合发布要求,确保没有遗漏重要信息或出现错误。
4. 提交插件到 pub.dev: -登录 pub.dev 网站,注册或使用现有账户。 -在 pub.dev 上选择 “Publish a package”,上传插件的源代码,填写必要信息。 -在插件页面点击 “Publish”,上传插件。
5. 插件发布与维护: -发布成功后,插件会出现在 pub.dev 上,其他开发者可以通过 flutter pub add my_plugin 来使用。 -定期维护插件,修复 bug,更新版本并发布。
简而言之: 发布插件到 pub.dev 包括准备代码、填写配置、验证插件、上传到 pub.dev,并通过维护插件来确保其长期可用。
|
2.10. 热重载与热更新
1-热重载(Hot Reload)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 一、热重载(Hot Reload)简述:
热重载 是 Flutter 提供的一项强大功能,它允许开发者在不重新启动应用的情况下,快速查看代码修改后的效果。 热重载能够保留应用的当前状态,更新 UI 或代码逻辑,而无需重新启动整个应用,从而提高开发效率。
二、工作原理: -代码更新:开发者修改 Dart 代码后,Flutter 会将修改的部分重新加载到正在运行的应用中。 -UI 刷新:修改后的代码会直接影响 UI,界面会即时更新,开发者可以快速查看变化。 -状态保持:应用的状态(如输入框内容、滚动位置等)通常会保持不变,这使得开发者可以在不丢失上下文的情况下进行多次修改和调试。
三、优势: -提高开发效率,减少等待时间。 -实时反馈,快速查看修改效果。 -保持应用状态,避免重复操作。
简而言之: 热重载通过快速更新代码并刷新 UI,使开发者能够高效调试和修改应用,而无需重启应用。
|
2-热重启(Hot Restart)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 一、热重启(Hot Restart)简述:
热重启 是 Flutter 提供的另一项功能,虽然它与 热重载 类似,但有所不同。 热重启会重新启动应用的整个状态,包括重置应用的所有状态和数据,而不需要完全关闭和重新启动应用。
二、工作原理: -代码更新:当开发者修改代码后,热重启会将应用从头开始重新启动。 -状态重置:与热重载不同,热重启会丢失应用的当前状态,所有的变量、UI 状态等都会被重置为初始状态。 -快速启动:尽管会丢失状态,热重启比完全关闭应用再重新启动要快得多。
三、优势: -可以确保所有代码修改(包括状态和逻辑的修改)都被应用。 -当热重载无法处理某些问题时,热重启可以提供一个全新的启动环境。
简而言之: 热重启会重新启动应用并重置状态,但相较于完全重启应用,它更加快速,适用于需要刷新整个应用状态的场景。
|
3-热更新(动态下发:代码 Push / JS Bridge)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 一、热更新(动态下发:代码 Push / JS Bridge)简述:
热更新 是指在应用发布后,能够通过网络将更新的代码或资源动态下发到客户端,而无需通过应用商店进行版本更新。 常用于修复 bug 或添加小的功能更新,提升用户体验。
二、主要方式: 2.1 代码 Push: -通过后台服务,将新的代码(如 JavaScript、Dart、Native)推送到客户端。 客户端在接收到更新后,自动或手动加载新的代码,应用可以即时生效。
-常见的 Flutter 热更新方案如 FlutterDevTools 或第三方库(如 flutter_hot_update)实现代码推送。
2.2 JS Bridge:
-对于使用 JavaScript 的应用,JS Bridge 是一种常见的方式。 通过 WebView 或 Native 组件,JavaScript 代码可以与原生代码进行交互, 更新的 JavaScript 代码可以通过动态下发的方式进行更新,而无需重新发布原生应用。
-在 React Native 中,JS Bridge 可以实现通过推送 JS 代码更新。
三、优势: -提高用户体验,避免频繁的应用商店更新。 -可以快速修复 bug 和推出小版本的功能更新。 -减少应用商店审核时间,支持即时发布。
简而言之: 热更新通过动态下发代码或资源,允许应用在不重新安装的情况下进行快速更新,常用于修复 bug 或更新小功能。
|
4-热重载机制 & VSCode / Android Studio 支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 一、热重载机制 & VSCode / Android Studio 支持简述:
热重载机制 是 Flutter 提供的一项功能,允许开发者在不重新启动应用的情况下,快速查看代码修改后的效果。 热重载能够保留应用的当前状态,并只更新被修改的部分,极大提升开发效率。
二、热重载机制: -快速反馈:修改代码后,Flutter 会将更新的代码重新注入到应用中,并立即反映在 UI 上。 -状态保持:应用的当前状态(如输入框内容、滚动位置等)通常会被保留,无需重新操作。 -效率提升:开发者可以快速看到代码修改的效果,无需等待应用重新启动,减少调试和开发的时间。
三、VSCode / Android Studio 支持: VSCode 和 Android Studio 都支持 Flutter 的热重载功能,并提供了易于使用的 UI 控件来触发热重载操作。
3.1 在VSCode 中,开发者可以通过快捷键 r 或点击界面上的“热重载”按钮来进行热重载。 3.2 在Android Studio中,开发者可以通过点击界面上的“热重载”按钮或者使用快捷键 Shift + F10 来进行热重载。
总结: 热重载机制 提供了即时的反馈和高效的开发体验, 而 VSCode 和 Android Studio 都是 Flutter 开发中支持热重载的主流 IDE, 提供便捷的操作界面来触发和管理热重载。
|
2.11. Flutter 资料推荐
- 官方文档(flutter.dev / api.flutter.dev)
- Awesome-Flutter Github 项目
- DartPad 在线运行
- 中文社区资源(掘金、Flutter中文网)
三 建议
1
| 每个模块建议结合源码理解,配合 Flutter Gallery、widgetbook、plugin 开发实践深化理解。
|