目标:先跑起来,再改代码,再故意犯错,最后看答案。
一、先跑起来:最小 Flutter 程序
创建项目:
bash
flutter create my_first_app
cd my_first_app
flutter run -d chrome
把 lib/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(
home: Scaffold(
body: Center(
child: Text('Hello Flutter'),
),
),
);
}
}
在终端按 r 触发热重载,把文字改成 你好,Flutter,界面会立刻变化。
核心概念一句话:
· Widget:界面元素。
· Widget 树:Widget 层层嵌套。
· StatelessWidget:不可变,不能自己改自己。
· StatefulWidget:有状态,可通过 setState() 刷新界面。
· 热重载:改代码后按 r,不重启应用就能看到变化。
二、多元化习题
练习0:环境自检
题目要求
运行 flutter doctor,记录所有带 ✗ 的项目,逐个解决。再次运行,直到全部变成 ✓。
答案
常用命令:
bash
flutter doctor
flutter doctor --android-licenses
常见问题:
· Android SDK 没装:安装 Android Studio。
· Chrome 没装:安装 Chrome。
· PATH 没配:把 Flutter 的 bin 目录加入环境变量。
· 许可证没接受:运行 flutter doctor --android-licenses。
答案解读
flutter doctor 是环境体检工具。它不自动修环境,但会告诉你缺什么。环境问题解决一次,后面开发会顺畅很多。
练习1:选择题
题目要求
在 Flutter 中,哪个 Widget 用于垂直排列子元素?
A. Row
B. Column
C. Stack
D. Center
答案
B。
答案解读
Row 是水平排列,Column 是垂直排列,Stack 是层叠布局,Center 是居中。做名片、列表、计数器时,最常用的就是 Column。
练习2:判断题
题目要求
判断对错,并说明理由:
- Flutter 中"一切皆 Widget"。
- StatelessWidget 可以通过 setState() 更新界面。
答案
- 对。
- 错。
答案解读
Flutter 的界面由 Widget 组成,文字、按钮、布局、间距都是 Widget。StatelessWidget 不可变,没有 setState();只有 StatefulWidget 的 State 中才能调用 setState() 触发重建。
练习3:连线题
题目要求
将左侧 Widget 与右侧作用连线:
左侧 右侧
Center 垂直排列
Column 显示文字
Text 居中
SizedBox 页面骨架
Scaffold 占位/间距
答案
· Center --- 居中
· Column --- 垂直排列
· Text --- 显示文字
· SizedBox --- 占位/间距
· Scaffold --- 页面骨架
答案解读
这些是入门最常用的 Widget。先记住作用,再通过组合搭界面。Flutter 开发就是不断把小组件拼成大组件。
练习4:排序题
题目要求
将以下 Widget 按"从外到内"的嵌套顺序排列:
Text、Scaffold、MaterialApp、Center
答案
MaterialApp → Scaffold → Center → Text
答案解读
MaterialApp 是应用外壳,Scaffold 是页面骨架,Center 让内容居中,Text 是最里面显示的文字。理解嵌套顺序,就理解了 Widget 树。
练习5:填空题
题目要求
补全代码,使文字居中显示"你好,Flutter":
dart
body: ______(
child: ______('你好,Flutter'),
),
答案
dart
body: Center(
child: Text('你好,Flutter'),
),
答案解读
Center 负责居中,Text 负责显示文字。body 是 Scaffold 的主体区域。如果直接写 body: Text(...),文字会顶到左上角。
练习6:代码预测题
题目要求
阅读代码,预测点击按钮 3 次后屏幕显示什么:
dart
class Demo extends StatefulWidget {
const Demo({super.key});
@override
State<Demo> createState() => _DemoState();
}
class _DemoState extends State<Demo> {
int count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$count'),
ElevatedButton(
onPressed: () => setState(() => count++),
child: const Text('+1'),
),
],
),
),
);
}
}
答案
显示 3。
答案解读
初始 count = 0。每次点击执行 setState(() => count++),count 加 1,并通知 Flutter 重新构建界面。点 3 次后,count 变成 3,所以 Text('$count') 显示 3。
练习7:改错题
题目要求
下面代码有错误,找出来并修正:
dart
void main() {
runApp(MyApp);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Text('练习7'),
),
);
}
}
答案
修正后:
dart
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: const Center(
child: Text('练习7'),
),
),
);
}
}
答案解读
runApp(MyApp) 传入的是类名,不是 Widget 实例,应该写成 runApp(MyApp()) 或 runApp(const MyApp())。使用 const MyApp() 时,需要给 MyApp 添加 const MyApp({super.key}); 构造函数。另外,Text 直接放 body 会顶到左上角,通常包一层 Center。
练习8:补全代码题
题目要求
补全下面代码,实现计数器:点击按钮,数字加 1。
dart
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => ______;
}
class _CounterState extends State<Counter> {
int count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$count', style: const TextStyle(fontSize: 48)),
ElevatedButton(
onPressed: () {
______(() {
______;
});
},
child: const Text('+1'),
),
],
),
),
);
}
}
答案
dart
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$count', style: const TextStyle(fontSize: 48)),
ElevatedButton(
onPressed: () {
setState(() {
count++;
});
},
child: const Text('+1'),
),
],
),
),
);
}
}
答案解读
createState() 返回 _CounterState()。按钮点击时用 setState() 包裹状态修改,count++ 让数字加 1。setState() 会触发 build 重新执行,界面更新。
练习9:动手编码题:个人名片
题目要求
用 Column、Text、SizedBox 做一个个人名片,显示三行信息:姓名、职业、城市。要求垂直排列,每行之间有间距。
答案
dart
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Text(
'张三',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text('Flutter 开发者'),
SizedBox(height: 8),
Text('青岛'),
],
),
),
答案解读
Column 垂直排列,Center 整体居中,SizedBox(height: 8) 制造间距。TextStyle 让姓名更突出。这个练习训练的是 Widget 组合能力。
练习10:动手编码题:点赞计数器
题目要求
做一个"收到 N 个赞"的页面,点击按钮 N 加 1。
答案
dart
class LikeApp extends StatefulWidget {
const LikeApp({super.key});
@override
State<LikeApp> createState() => _LikeAppState();
}
class _LikeAppState extends State<LikeApp> {
int likes = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('收到 $likes 个赞', style: const TextStyle(fontSize: 24)),
const SizedBox(height: 16),
ElevatedButton.icon(
onPressed: () {
setState(() {
likes++;
});
},
icon: const Icon(Icons.thumb_up),
label: const Text('点赞'),
),
],
),
),
);
}
}
答案解读
likes 是可变状态,放在 State 中。点击按钮时 setState() 让 likes 加 1,界面中的 $likes 自动刷新。这个练习把"展示数据"和"修改数据"结合了起来。
练习11:综合挑战:名片 + 点赞
题目要求
把练习9和练习10合并:上面显示个人名片,下面显示"收到 N 个赞",点击按钮 N 加 1。
答案
dart
class ProfileCard extends StatefulWidget {
const ProfileCard({super.key});
@override
State<ProfileCard> createState() => _ProfileCardState();
}
class _ProfileCardState extends State<ProfileCard> {
int likes = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'张三',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
const Text('Flutter 开发者 · 青岛'),
const SizedBox(height: 24),
Text('收到 $likes 个赞', style: const TextStyle(fontSize: 18)),
const SizedBox(height: 12),
ElevatedButton.icon(
onPressed: () {
setState(() {
likes++;
});
},
icon: const Icon(Icons.thumb_up),
label: const Text('点赞'),
),
],
),
),
);
}
}
答案解读
静态信息直接写死,动态点赞数用 likes 保存。整个页面是 StatefulWidget,因为有点赞交互。setState() 只修改 likes,Flutter 自动重建相关部分。
练习12:开放式挑战:今日待办
题目要求
用 Text、Column、Center、SizedBox 设计一个"今日待办"页面。至少包含 5 个 Widget 嵌套,至少一处使用 TextStyle 做样式区分。
答案示例
dart
body: Center(
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text(
'今日待办',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
SizedBox(height: 20),
Text('1. 学习 Flutter 基础', style: TextStyle(fontSize: 18)),
SizedBox(height: 10),
Text(
'2. 完成第1课习题',
style: TextStyle(fontSize: 18, color: Colors.green),
),
SizedBox(height: 10),
Text(
'3. 复习 Widget 树',
style: TextStyle(
fontSize: 18,
decoration: TextDecoration.lineThrough,
),
),
SizedBox(height: 30),
Text(
'加油,今天也要练中学!',
style: TextStyle(
fontSize: 16,
fontStyle: FontStyle.italic,
color: Colors.grey,
),
),
],
),
),
),
答案解读
这个页面包含 Center、Padding、Column、Text、SizedBox 多层嵌套,满足 Widget 树练习。TextStyle 分别做了标题加粗变蓝、完成项绿色、已完成项删除线、底部文字斜体灰色。开放题没有唯一答案,重点是能组合、能运行、能改样式。
练习13:错误观察挑战
题目要求
在任意一个能运行的例子中,故意删掉一个右括号,或把 Column 改成不存在的 Colum,运行后观察控制台错误。记录你看到的关键错误词。
答案
常见错误示例:
text
Error: Expected ')' before this.
或:
text
The method 'Colum' isn't defined for the type '_MyAppState'.
答案解读
Flutter 报错通常会给出文件名、行号和原因。新手最重要的能力之一,就是学会看错误关键词:Expected 表示缺少符号,isn't defined 表示名字写错,The named parameter ... isn't defined 表示参数名不对。故意改坏代码,再修好,是练中学非常有效的方法。
三、本课小结
这一课你完成了:
- 跑通第一个 Flutter 应用;
- 认识 Widget、Widget 树、StatelessWidget、StatefulWidget、setState、热重载;
- 做了 14 个练习,覆盖环境、选择、判断、连线、排序、填空、代码预测、改错、补全、动手编码、综合挑战、开放设计和错误观察。
下一课会进入布局系统:Row、Column、Container、Expanded、Padding、MainAxisAlignment,继续用"练中学"的方式搭出更复杂的界面。