学完 Dart 之后,再看 Flutter 会顺很多:你已经认识了 main()、类、构造函数、命名参数、空安全和异步。Flutter 做的事,是用 Dart 把界面、交互、状态和平台能力组织成一个完整应用。
这篇文章不追求一次讲完 Flutter 的全部生态,而是带你在 VS Code 里跑通最重要的一条线:
- 装好 Flutter SDK、VS Code 插件和运行设备;
- 创建并启动第一个 Flutter 项目;
- 看懂
main.dart、Widget、布局和状态; - 学会热重载、调试、格式化、分析和依赖管理;
- 知道新手最容易卡在哪些地方。
版本提示:Flutter、Android SDK、Xcode 和 VS Code 插件都会更新。本文讲的是稳定工作流;具体安装入口、系统要求和平台配置请以 Flutter 官方文档为准。
总览
| 任务 | 常用命令或入口 | 作用 | 你要确认什么 |
|---|---|---|---|
| 检查工具链 | flutter doctor |
找出缺失组件 | Android/iOS/Web 至少有一个可运行 |
| 创建项目 | flutter create app_name |
生成标准工程 | 项目名用小写加下划线 |
| 打开项目 | code app_name |
用 VS Code 开发 | 安装 Flutter 与 Dart 插件 |
| 查看设备 | flutter devices |
列出模拟器、真机、浏览器 | VS Code 右下角能选设备 |
| 启动应用 | flutter run 或 F5 |
构建并运行 | 第一次会比较慢 |
| 热重载 | 保存文件或点击 Hot Reload | 快速刷新 UI | 不重启应用状态 |
| 热重启 | Hot Restart | 重启 Dart VM | 状态会重置 |
| 静态检查 | flutter analyze |
发现代码问题 | 提交前建议跑一次 |
| 格式化 | dart format . |
统一代码风格 | 团队不要手调缩进 |
| 管理依赖 | flutter pub add package |
添加第三方库 | 更新 pubspec.yaml |
1. Flutter 是什么
原理
Flutter 是一个跨平台 UI 框架。你用 Dart 写一套界面和业务逻辑,Flutter 负责渲染到 Android、iOS、Web、Windows、macOS、Linux 等平台。
它和传统原生开发最大的区别是:Flutter 不只是把原生控件简单包一层,而是自己管理一套 UI 渲染体系。你写的是 Widget 树,Flutter 根据状态变化重新构建界面,再把变化绘制到屏幕上。
可以先这样理解:
- Dart 是语言;
- Flutter SDK 是框架和工具链;
- Widget 是界面的最小描述单元;
State是驱动界面变化的数据;flutter run、热重载和 DevTools 是日常开发工具。
使用场景
Flutter 适合:
- 移动端 App;
- 管理后台、活动页、内部工具等 Web 应用;
- 桌面端工具;
- 多端一致体验要求比较高的业务;
- 快速验证产品原型。
它不一定适合所有场景。比如极重原生能力、极小安装包、需要深度系统集成的应用,仍然要认真评估原生方案或混合方案。
2. 开发环境:先把 VS Code 跑通
安装清单
最常见的 Flutter + VS Code 开发环境需要这些东西:
| 工具 | 作用 |
|---|---|
| Flutter SDK | 提供 Flutter 框架、Dart SDK 和命令行工具 |
| VS Code | 编写、运行、调试代码 |
| Flutter 插件 | VS Code 中创建、运行、热重载、调试 Flutter 项目 |
| Dart 插件 | 语法高亮、代码补全、格式化和分析 |
| Android Studio 或 Android SDK | 运行 Android 模拟器或真机 |
| Xcode | 在 macOS 上运行 iOS 模拟器或真机 |
| Chrome | 运行 Flutter Web |
Flutter 插件通常会自动带上 Dart 插件。安装后,VS Code 里应该能识别 .dart 文件,并在右下角显示可选设备。
检查环境
打开终端:
bash
flutter --version
flutter doctor
flutter doctor 会告诉你哪些组件已经可用,哪些还缺配置。刚开始不用追求所有平台都打勾,你至少先让一个平台能跑起来:
- Android 模拟器;
- Android 真机;
- iOS 模拟器;
- Chrome 浏览器。
如果你只想最快看到效果,Chrome 往往是阻力最小的目标设备。
3. 用 VS Code 创建第一个 Flutter 项目
方式一:命令行创建
bash
flutter create hello_flutter
cd hello_flutter
code .
项目名建议使用小写字母和下划线,例如 hello_flutter、todo_app、shop_demo。
方式二:VS Code 命令面板创建
在 VS Code 中:
- 打开命令面板:
Cmd + Shift + P或Ctrl + Shift + P。 - 输入
Flutter: New Project。 - 选择
Application。 - 选择项目保存目录。
- 输入项目名。
创建完成后,VS Code 会打开项目,并自动执行依赖获取。
项目结构
一个新 Flutter 项目大致长这样:
text
hello_flutter/
├── android/
├── ios/
├── lib/
│ └── main.dart
├── linux/
├── macos/
├── test/
├── web/
├── windows/
├── pubspec.yaml
└── analysis_options.yaml
最开始你重点关注 3 个地方:
lib/main.dart:应用入口和主要 Dart 代码;pubspec.yaml:项目配置、依赖、资源声明;test/:测试代码。
android/、ios/、web/ 等目录是平台工程。新手阶段不要频繁改它们,先把 lib/ 写熟。
4. 运行项目
在 VS Code 中运行
打开 lib/main.dart,然后:
- 右下角选择运行设备;
- 按
F5进入调试运行; - 或点击顶部 Run 按钮。
如果只是运行,不需要调试断点,也可以在终端执行:
bash
flutter run
如果有多个设备,可以指定设备:
bash
flutter devices
flutter run -d chrome
flutter run -d emulator-5554
第一次运行为什么慢
第一次运行会做很多事:
- 下载或检查平台依赖;
- 编译 Dart 代码;
- 编译 Android/iOS/Web 平台工程;
- 安装到模拟器或真机;
- 启动调试连接。
后续使用热重载会快很多,不要被第一次构建时间吓退。
5. 看懂 main.dart
新项目默认会生成一个计数器示例。你可以先把它简化成下面这样:
dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hello Flutter',
home: Scaffold(
appBar: AppBar(
title: const Text('Hello Flutter'),
),
body: const Center(
child: Text('你好,Flutter'),
),
),
);
}
}
这段代码里有几层关键关系:
| 代码 | 含义 |
|---|---|
main() |
Dart 程序入口 |
runApp() |
启动 Flutter 应用 |
MyApp |
你的根 Widget |
MaterialApp |
提供 Material 风格应用能力 |
Scaffold |
页面骨架,常放 AppBar、body、浮动按钮 |
Center |
把子 Widget 居中 |
Text |
显示文本 |
Flutter 代码读起来像一棵树。外层 Widget 包住内层 Widget,内层负责更具体的显示和交互。
6. Widget:Flutter 的界面单位
原理
Widget 是 Flutter 对界面的描述。按钮、文字、图片、列表、页面、主题,都是 Widget。
Widget 本身通常是不可变的。界面变化不是"修改旧按钮",而是"根据新状态重新描述界面"。Flutter 会高效比较并更新屏幕。
两类常用 Widget
| 类型 | 适合场景 | 例子 |
|---|---|---|
StatelessWidget |
自己不管理可变状态 | 标题、展示卡片、纯布局组件 |
StatefulWidget |
需要随着交互变化 | 计数器、表单、开关、加载状态 |
什么时候拆 Widget
刚开始不要把所有代码都塞进 MyApp。出现下面情况就可以拆:
- 一个 UI 区块超过 30 行;
- 同样的 UI 出现多次;
- 某一块有独立的输入参数;
- 某一块以后可能单独测试或复用。
示例:
dart
class CourseCard extends StatelessWidget {
const CourseCard({
super.key,
required this.title,
required this.subtitle,
});
final String title;
final String subtitle;
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
title: Text(title),
subtitle: Text(subtitle),
trailing: const Icon(Icons.chevron_right),
),
);
}
}
Dart 的命名参数、final 字段、const 构造函数,在 Flutter 里会高频出现。
7. 布局:先掌握 6 个组件
Flutter 布局刚开始看起来很碎,其实常用心智模型很简单:父组件给约束,子组件在约束内决定大小。
先掌握这些组件:
| Widget | 作用 | 常见用法 |
|---|---|---|
Container |
包装尺寸、背景、边距、装饰 | 做一个块 |
Padding |
内边距 | 给内容留呼吸空间 |
Center |
居中 | 空状态、按钮、提示 |
Column |
垂直排列 | 页面内容从上到下 |
Row |
水平排列 | 图标加文字、按钮组 |
Expanded |
占据剩余空间 | 让列表或内容撑满 |
一个简单页面:
dart
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('课程首页')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Flutter 快速上手',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
const Text('使用 VS Code 创建、运行与调试第一个 App。'),
const SizedBox(height: 24),
Expanded(
child: ListView(
children: const [
CourseCard(title: '环境搭建', subtitle: 'Flutter SDK 与 VS Code'),
CourseCard(title: 'Widget 基础', subtitle: '界面就是 Widget 树'),
CourseCard(title: '状态管理', subtitle: '用状态驱动界面变化'),
],
),
),
],
),
),
);
}
}
注意:Column 里直接放很长内容,容易溢出。需要滚动时,用 ListView、SingleChildScrollView 或把可滚动区域放进 Expanded。
8. 状态:从 setState 开始
原理
状态就是会变化的数据。比如计数、输入框内容、当前选中的 Tab、接口加载结果。
最基础的状态管理方式是 StatefulWidget + setState():
dart
class CounterPage extends StatefulWidget {
const CounterPage({super.key});
@override
State<CounterPage> createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int count = 0;
void increment() {
setState(() {
count++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('计数器')),
body: Center(
child: Text(
'count = $count',
style: const TextStyle(fontSize: 28),
),
),
floatingActionButton: FloatingActionButton(
onPressed: increment,
child: const Icon(Icons.add),
),
);
}
}
setState() 的意思不是"手动刷新屏幕",而是告诉 Flutter:状态变了,请用新的状态重新执行 build()。
新手原则
- 页面内的小状态,用
setState足够。 - 多页面共享状态,再考虑
Provider、Riverpod、Bloc等方案。 - 不要一开始就把状态管理框架当成入门门槛。
- 复杂逻辑尽量从 Widget 中拆出去,放到模型、服务或状态类里。
9. 热重载和热重启
Flutter 开发体验里最舒服的部分,是你改完代码后能快速看到效果。
| 操作 | 作用 | 状态是否保留 |
|---|---|---|
| Hot Reload | 把代码变化注入正在运行的应用 | 通常保留 |
| Hot Restart | 重启 Dart 应用 | 不保留 |
| Stop + Run | 重新构建并启动 | 不保留 |
VS Code 里保存文件通常会触发热重载,也可以使用调试工具栏中的热重载按钮。
什么时候热重载不够:
- 修改了
main(); - 修改了全局初始化;
- 改动了状态类字段初始化;
- 插件或平台工程发生变化;
- 热重载后界面状态明显不对。
这时用 Hot Restart 或重新运行。
10. 调试:VS Code 里先学这几招
断点调试
在 VS Code 左侧行号处点击打断点,然后按 F5 运行。触发代码后,程序会停在断点位置。
你可以查看:
- 当前变量值;
- 调用栈;
- 表达式求值;
- 单步执行结果。
日志输出
简单调试可以用:
dart
debugPrint('current count: $count');
相比 print(),debugPrint() 对长文本输出更友好。
Flutter Inspector
Flutter Inspector 可以帮你看 Widget 树、布局边界和当前选中的组件。遇到页面布局奇怪时,先看它,效率很高。
常见用途:
- 找到某个文本或按钮对应的 Widget;
- 检查
Padding、Row、Column的层级; - 查看为什么某个组件占满了空间;
- 打开布局边界辅助线。
11. 资源文件:图片和字体怎么放
Flutter 资源需要在 pubspec.yaml 里声明。
例如把图片放在:
text
assets/images/logo.png
然后配置:
yaml
flutter:
assets:
- assets/images/logo.png
使用图片:
dart
Image.asset('assets/images/logo.png')
如果是一整个目录:
yaml
flutter:
assets:
- assets/images/
注意 pubspec.yaml 对缩进很敏感。改完资源配置后,如果热重载不生效,执行:
bash
flutter pub get
或者直接重新运行应用。
12. 依赖管理:使用 pubspec.yaml
Flutter 依赖来自 pub.dev。添加依赖可以手动改 pubspec.yaml,也可以用命令:
bash
flutter pub add http
flutter pub add shared_preferences
flutter pub add dev:build_runner
依赖加完后会更新:
pubspec.yaml:声明你需要什么;pubspec.lock:锁定当前解析到的版本。
常用命令:
bash
flutter pub get
flutter pub upgrade
flutter pub outdated
建议:
- 应用项目提交
pubspec.lock; - 第三方库项目通常不提交锁定应用版本的思路;
- 不要一次加很多依赖,先确认每个依赖解决了真实问题。
13. 路由:从两个页面跳转开始
Flutter 里页面跳转最基础的是 Navigator:
dart
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const DetailPage(),
),
);
返回上一页:
dart
Navigator.of(context).pop();
一个详情页:
dart
class DetailPage extends StatelessWidget {
const DetailPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('详情')),
body: const Center(
child: Text('这里是详情页'),
),
);
}
}
刚入门时先用 Navigator + MaterialPageRoute。项目变大、有深链、Web URL 或复杂嵌套路由时,再学习 go_router。
14. 网络请求:先理解异步和模型
Flutter 网络请求通常会用 Future。如果你已经学过 Dart 的 async 和 await,这里就很自然。
添加依赖:
bash
flutter pub add http
示例:
dart
import 'dart:convert';
import 'package:http/http.dart' as http;
class Todo {
const Todo({
required this.id,
required this.title,
required this.completed,
});
final int id;
final String title;
final bool completed;
factory Todo.fromJson(Map<String, dynamic> json) {
return Todo(
id: json['id'] as int,
title: json['title'] as String,
completed: json['completed'] as bool,
);
}
}
Future<Todo> fetchTodo() async {
final response = await http.get(
Uri.parse('https://jsonplaceholder.typicode.com/todos/1'),
);
if (response.statusCode != 200) {
throw Exception('Failed to load todo');
}
final json = jsonDecode(response.body) as Map<String, dynamic>;
return Todo.fromJson(json);
}
页面里可以用 FutureBuilder 快速展示异步结果:
dart
FutureBuilder<Todo>(
future: fetchTodo(),
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return const CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text('加载失败:${snapshot.error}');
}
final todo = snapshot.data!;
return Text(todo.title);
},
)
真实项目中,不建议在 build() 里直接创建新的网络请求。可以在 initState() 保存一个 Future,或者把请求放到状态管理层。
15. 平台运行:Android、iOS、Web 怎么选
Android
Android 是多数 Flutter 初学者最常用的平台。你需要:
- Android SDK;
- Android 模拟器或真机;
- 接受 Android licenses;
- VS Code 能识别设备。
常见命令:
bash
flutter doctor --android-licenses
flutter devices
flutter run
真机调试时,需要打开手机的开发者选项和 USB 调试。
iOS
iOS 开发只能在 macOS 上完整进行。你需要:
- Xcode;
- iOS Simulator;
- CocoaPods;
- Apple 相关签名配置。
模拟器阶段相对简单,真机和发布会涉及证书、Bundle Identifier、Team 等配置。
Web
Web 适合快速预览:
bash
flutter run -d chrome
Web 运行很方便,但不要把 Web 预览等同于移动端体验。手势、尺寸、字体、性能和平台插件都可能不同。
16. 常用 VS Code 工作流
推荐设置
VS Code 可以开启保存时格式化:
json
{
"editor.formatOnSave": true,
"[dart]": {
"editor.defaultFormatter": "Dart-Code.dart-code"
}
}
Flutter 项目里建议养成这几个习惯:
- 每次提交前执行
flutter analyze; - 代码保存时自动格式化;
- 善用
Quick Fix修复导入、const提示和 lint; - 用
Extract Widget把复杂 UI 拆小; - 用
Flutter: Select Device切换运行目标。
常用快捷动作
| 动作 | VS Code 入口 |
|---|---|
| 新建项目 | Flutter: New Project |
| 选择设备 | Flutter: Select Device |
| 热重载 | 调试工具栏或保存文件 |
| 热重启 | 调试工具栏 |
| 打开 DevTools | Dart: Open DevTools |
| 抽取 Widget | 选中代码后使用灯泡菜单 |
17. 一个小练习:课程清单 App
把前面内容串起来,我们做一个简单课程清单页面。
dart
import 'package:flutter/material.dart';
void main() {
runApp(const LearningApp());
}
class LearningApp extends StatelessWidget {
const LearningApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Learning',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const CourseListPage(),
);
}
}
class CourseListPage extends StatefulWidget {
const CourseListPage({super.key});
@override
State<CourseListPage> createState() => _CourseListPageState();
}
class _CourseListPageState extends State<CourseListPage> {
final List<String> courses = const [
'安装 Flutter 环境',
'创建 Flutter 项目',
'认识 Widget',
'掌握布局组件',
'理解 setState',
];
int selectedIndex = 0;
@override
Widget build(BuildContext context) {
final selectedCourse = courses[selectedIndex];
return Scaffold(
appBar: AppBar(
title: const Text('Flutter 快速上手'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'当前学习:$selectedCourse',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 16),
Expanded(
child: ListView.separated(
itemCount: courses.length,
separatorBuilder: (context, index) => const SizedBox(height: 8),
itemBuilder: (context, index) {
final course = courses[index];
final selected = index == selectedIndex;
return ListTile(
selected: selected,
tileColor: selected ? Colors.blue.withValues(alpha: 0.08) : null,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
leading: CircleAvatar(
child: Text('${index + 1}'),
),
title: Text(course),
trailing: selected ? const Icon(Icons.check) : null,
onTap: () {
setState(() {
selectedIndex = index;
});
},
);
},
),
),
],
),
),
);
}
}
这个例子覆盖了入门阶段最核心的知识点:
runApp()启动应用;MaterialApp设置主题和首页;Scaffold搭页面骨架;Column、Expanded、ListView做布局;ListTile展示列表项;setState()处理选中状态;Theme.of(context)使用主题样式。
18. 新手常见问题
1. flutter doctor 没有全绿怎么办
不用一开始全绿。你先确定目标平台,比如只跑 Android 或 Chrome。和当前目标无关的问题可以以后再处理。
2. VS Code 找不到 Flutter SDK
检查:
- Flutter SDK 是否已下载;
flutter命令是否在终端可用;- VS Code Flutter 插件是否安装;
- 命令面板里是否可以执行
Flutter: Change SDK。
3. Android licenses 报错
执行:
bash
flutter doctor --android-licenses
按提示接受协议。若找不到 Android SDK,先确认 Android Studio 或命令行工具安装正确。
4. 模拟器很慢
可以先用 Chrome 跑通 Flutter 基础,再回头配置 Android 模拟器。模拟器慢时,检查硬件加速、镜像版本和电脑资源。
5. 页面出现黄色黑色溢出条
通常是布局超出了可用空间。常见修法:
- 给内容加滚动;
Column中的列表包一层Expanded;- 减少固定宽高;
- 用
Flexible或响应式布局处理小屏幕。
6. 改了代码但界面没变化
尝试:
- 保存文件触发热重载;
- 点击 Hot Restart;
- 停止应用后重新运行;
- 确认你修改的是当前运行项目;
- 确认设备没有跑旧应用。
7. pubspec.yaml 改完报错
大概率是缩进问题。YAML 对空格很敏感。flutter:、assets:、资源路径要保持正确层级。
19. 学习路线
第 1 阶段:跑通项目
目标:能在 VS Code 中创建、运行、热重载。
重点:
- Flutter SDK;
- VS Code 插件;
flutter doctor;flutter create;- 设备选择。
第 2 阶段:看懂页面
目标:看到 Widget 树不慌。
重点:
MaterialApp;Scaffold;StatelessWidget;StatefulWidget;- 常用布局组件。
第 3 阶段:做出交互
目标:能用状态驱动界面变化。
重点:
setState;- 表单;
- 列表;
- 页面跳转;
- 生命周期。
第 4 阶段:接入数据
目标:能做一个有真实数据的小 App。
重点:
http;- JSON 模型;
FutureBuilder;- 加载、成功、失败状态;
- 简单缓存。
第 5 阶段:工程化
目标:让代码可维护。
重点:
- 目录分层;
- 状态管理;
- 路由管理;
- 主题系统;
- 测试;
- 打包发布。
20. 面试或自我介绍时怎么说 Flutter
如果面试官问:"你怎么理解 Flutter?"
可以这样回答:
Flutter 是一个使用 Dart 编写的跨平台 UI 框架。它通过 Widget 描述界面,用状态驱动 UI 更新,并提供热重载、DevTools、统一渲染和多平台构建能力。日常开发里,我会关注 Widget 拆分、布局约束、状态管理、异步数据、路由、主题和平台适配。
如果继续追问:"StatelessWidget 和 StatefulWidget 有什么区别?"
可以回答:
StatelessWidget自身不管理可变状态,适合纯展示和可复用 UI;StatefulWidget有对应的State对象,可以通过setState()改变状态并触发界面重建。选择哪一个主要看这个组件是否需要自己保存会变化的数据。
如果继续追问:"热重载和热重启有什么区别?"
可以回答:
热重载会把代码变化注入正在运行的应用,通常保留当前状态;热重启会重启 Dart 应用,状态会重置。改 UI 样式时热重载很方便,改入口、初始化或状态结构时更适合热重启。
21. 总结
Flutter 入门的关键不是背组件,而是建立几个稳定模型:
- Flutter 应用从
main()和runApp()开始; - UI 是一棵 Widget 树;
- Widget 描述界面,状态驱动变化;
- 小状态先用
setState,复杂共享状态再引入框架; - VS Code 的 Flutter 插件能覆盖创建、运行、调试、热重载和 DevTools;
flutter doctor是环境问题的第一检查入口;pubspec.yaml管依赖和资源,缩进必须认真;- 真正变强的方法,是做一个能跑、能跳转、能请求数据的小 App。
如果你刚看完 Dart 快速上手,建议马上用 VS Code 新建一个 Flutter 项目,把本文的课程清单例子敲进去。能跑起来之后,再继续学习布局、路由、网络请求和状态管理,Flutter 的主线就基本顺了。