Flutter 是 Google 开源的用户界面(UI)工具包,它允许开发者通过一套代码库高效构建适用于移动、Web、桌面和嵌入式平台的应用。Flutter 使用 Dart 语言,并借助 Skia 绘图引擎直接通过 CPU 和 GPU 进行绘制,无需依赖原生控件,从而在性能上相比 React Native 等框架更具优势。目前,Flutter 的混合栈技术已相当成熟,基础设施完善,在百度贴吧、网盘、地图等多个产品中广泛应用,实现了双端代码复用,显著提升了开发效率。
环境配置
下载 Flutter SDK
通过 Git 克隆 Flutter 仓库到本地:
git clone https://github.com/flutter/flutter.git
配置环境变量
编辑 ~/.bash_profile 文件(如果使用 zsh,则编辑 ~/.zshrc),在文件末尾添加以下环境变量,其中 FLUTTER_HOME 替换为你的 Flutter 文件夹路径:
export FLUTTER_HOME=/Users/.../flutter
export PATH=$PATH:$FLUTTER_HOME/bin
export PATH=$PATH:$FLUTTER_HOME/bin/cache/dart-sdk/bin
添加后,执行 source ~/.bash_profile 或 source ~/.zshrc 刷新环境变量。
开发工具
推荐使用 Xcode 配合 Android Studio 进行开发。在 Android Studio 中,需安装 Flutter 和 Dart 插件,路径为:Preferences → Plugins → Marketplace。此外,还需安装最新版的 Android SDK Command-line Tools,位于 Preferences → System Settings → Android SDK → SDK Tools 中。
配置完成后,运行 flutter doctor 检查环境状态。若遇到 “Unable to find bundled Java version” 错误,可进入 /Applications/Android Studio.app/Contents/jre 目录,执行以下命令创建符号链接后重新运行 flutter doctor:
ln -s ../jre jdk
ln -s "/Library/Internet Plug-Ins/JavaAppletPlugin.plugin" jdk
工程创建
创建 Flutter 项目
使用命令 flutter create xxx 创建一个标准的 Flutter 应用项目。
创建 Flutter 模块
若需将 Flutter 集成到原生应用中,可使用 flutter create --template module xxx 创建一个 Flutter 模块。
工程结构
新建的 Flutter 项目包含以下主要目录和文件:
android:Android 平台相关代码。ios:iOS 平台相关代码。lib:存放 Flutter 主代码(Dart 文件)。web:Web 平台相关代码。pubspec.yaml:项目配置文件,用于管理依赖(类似 CocoaPods 或 Gradle)。
编程范式
Flutter 采用声明式编程范式构建用户界面,这与传统的命令式编程形成对比。命令式编程关注“如何做”,需要详细步骤来操作界面元素;而声明式编程关注“做什么”,开发者只需描述界面的目标状态,框架会自动处理更新。自 2009 年以来,Vue、React、SwiftUI 和 Flutter 等框架推动了声明式编程在前端领域的普及。
为了理解两者的区别,考虑一个点击按钮更新文本的例子。在命令式编程中(如 Objective-C 或 Java),需要直接获取文本控件并调用更新方法;而在声明式编程中(如 Flutter 或 SwiftUI),只需定义一个状态变量,并在状态改变时重建界面,框架会自动将新状态应用到对应的文本控件上。这种方式使代码更结构化,并减少了手动管理界面更新的复杂度。
基础架构
Flutter 采用分层架构,从下到上分为 Embedder、Engine 和 Framework 三层,各层职责明确且可独立替换。
Embedder 是平台适配层,负责处理渲染表面设置、线程管理等与操作系统相关的任务。
Engine 层是 Flutter 的核心,使用 C++ 实现,提供了图形渲染(通过 Skia)、文本布局、文件网络 I/O、插件架构以及 Dart 运行时环境。当需要绘制新帧时,引擎负责栅格化场景。
Framework 层是用 Dart 编写的 UI SDK,为开发者提供了丰富的组件和抽象。它自底向上包括:
- 基础库(Foundation):提供动画、绘图和手势识别等基础服务。
- 渲染层(Rendering):负责布局计算,维护可渲染对象树。
- Widget 层:引入了响应式编程模型,每个渲染对象对应一个 Widget,允许通过组合构建复杂界面。
- Material/Cupertino 库:实现了 Material Design 和 iOS 设计规范的全套组件。
视图渲染
在 Flutter 中,一切界面元素都是 Widget,它是界面配置和数据的抽象描述。Widget 分为无状态的 StatelessWidget 和有状态的 StatefulWidget。
Flutter 的渲染过程并不直接操作 Widget 树,因为 Widget 频繁重建会导致性能开销。相反,Widget 会创建对应的 Element 和 RenderObject。Element 管理 Widget 的生命周期,而 RenderObject 则负责实际的布局和绘制。并非所有 Widget 都会生成 RenderObject;例如,Text 继承自 StatelessWidget,而 Column 等布局 Widget 会创建 RenderFlex 等 RenderObject 子类来执行具体渲染。
渲染流程可概括为:Widget 树 → Element 树 → RenderObject 树,Flutter 引擎最终渲染的是 RenderObject 树,这确保了高效稳定的界面更新。
混合开发
Flutter 调用原生方法
Flutter 与原生代码交互主要有三种方式:Platform Channels、Pigeon 以及第三方开源包。以 Platform Channels 为例,实现 Flutter 调用原生获取设备 UDID 的功能:
在 Flutter 端,创建 MethodChannel 并调用方法:
static const platform = const MethodChannel("leo.com/getudid");
void getUDID() async {
final result = await platform.invokeMethod("nativeGetUDID");
setState(() { _udid = result; });
}
在 iOS 端,于 FlutterViewController 中设置方法处理器:
let channel = FlutterMethodChannel(name: "leo.com/getudid", binaryMessenger: controller.binaryMessenger)
channel.setMethodCallHandler { (call, result) in
guard call.method == "nativeGetUDID" else {
result(FlutterMethodNotImplemented)
return
}
let udid = "xxxx-xxxx-xxxx-xxxx"
result(udid)
}
在 Android 端,于 FlutterEngine 中配置方法回调:
private val CHANNEL = "leo.com/getudid"
methodChannel.setMethodCallHandler { call, result ->
if (call.method == "nativeGetUDID") {
val udid = "xxxx-xxxx-xxxx-xxxx"
result.success(udid)
} else {
result.notImplemented()
}
}
原生集成 Flutter
首先,通过 flutter create --template module native_add_flutter 创建 Flutter 模块。
iOS 集成:在 Podfile 中添加 Flutter 模块路径,并初始化 FlutterEngine:
flutter_application_path = '../native_add_flutter'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
在代码中启动引擎并显示 Flutter 界面:
let flutterEngine = FlutterEngine(name: "leo")
flutterEngine.run()
let flutterVC = FlutterViewController(engine: engine, nibName: nil, bundle: nil)
present(flutterVC, animated: true)
Android 集成:在 settings.gradle 和 build.gradle 中配置 Flutter 模块依赖,并在 AndroidManifest.xml 中注册 FlutterActivity。随后,在 Activity 中启动 Flutter:
startActivity(FlutterActivity.createDefaultIntent(this))
案例讲解:计数器应用
通过 flutter create flutterdemo 创建一个新项目,主文件 main.dart 实现了一个简单的计数器应用。应用入口 main() 调用 runApp(MyApp()) 启动。
MyApp 是一个无状态 Widget,配置了应用主题和主页。主页 MyHomePage 是有状态 Widget,内部维护计数器 _counter。点击浮动按钮时,_incrementCounter 方法通过 setState 更新状态,触发界面重建,从而显示新的计数值。
界面使用 Scaffold 构建,包含导航栏、居中显示的文本列以及右下角的浮动按钮。这个例子展示了 Flutter 声明式编程的核心模式:状态改变驱动界面自动更新。