嘿,欢迎来到Flutter的世界!我是Agnes,一个喜欢折腾代码的开发者。如果你正坐在电脑前,想着“能不能写一套代码,同时在安卓和苹果手机上运行”,那你找对地方了。Flutter不是魔法,它是一门手艺,而且比你想象的要好玩得多。
我们要一起走完这段路:从第一个Dart变量开始,一直到做出一个能上架的应用。别担心基础问题,我们就像搭积木一样,一块一块来。
第一步:安装SDK和配置环境
别被“开发环境配置”这个词吓跑。现在的Flutter安装包已经集成了Dart SDK、Flutter SDK,甚至模拟器,非常友好。
1. 下载 Flutter SDK
去 flutter.dev 下载对应你操作系统的安装包(Windows/Mac/Linux)。下载后解压到一个不含空格的路径,比如:
- Windows:
C:\src\flutter - Mac:
~/flutter
2. 设置环境变量
把 Flutter 的 bin 目录加到你的系统 PATH 里:
# Linux/Mac
export PATH="$PATH:`pwd`/flutter/bin"
# Windows 环境变量
C:\src\flutter\bin
3. 运行 Doctor 检查
打开终端(Terminal 或 CMD),输入:
flutter doctor
你会看到一系列检查项。如果看到红色的警告(比如 Android SDK 未安装、Xcode 未配置),双击那个警告文字,它会给你一个修复链接。我们重点关注:
Doctor summary (to see all details):
[✓] Flutter (Channel stable, 3.16.0, on macOS 14.2.1)
[✓] Android toolchain - develop for Android devices
[✓] Xcode - develop for iOS and macOS
[✓] Chrome - develop for the web
如果全是绿钩,你就可以开工了!
第二步:Dart 语法速成
Flutter 的核心语言是 Dart。它语法简洁,类型安全,类似 Java 或 TypeScript,但对新手更友好。
1. 变量和数据类型
// 动态类型
var name = "Flutter";
const age = 20;
final version = "3.16.0";
// 数值类型
int count = 10;
double price = 99.9;
// 字符串插值(非常好用)
String greeting = "你好, $name! 版本 $version";
2. 函数
// 箭头函数(简洁写法)
int add(int a, int b) => a + b;
// 普通函数
String greet(String name) {
return "Hello, $name";
}
// 可选参数
void printInfo(String name, {int age = 0}) {
print("姓名: $name, 年龄: $age");
}
3. 类和对象
class Person {
String name;
int age;
Person(this.name, this.age);
void sayHello() {
print("Hi, I'm $name, $age years old");
}
}
void main() {
var person = Person("小明", 25);
person.sayHello();
}
4. 异步编程(Future 和 async/await)
这是 Flutter 处理网络请求的关键:
// 模拟网络请求
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 2));
return "数据加载完成";
}
// 调用
Future<void> main() async {
String data = await fetchData();
print(data);
}
第三步:第一个 Flutter 应用
创建项目:
flutter create my_first_app
cd my_first_app
然后打开 lib/main.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: 'Flutter入门',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('我的第一个Flutter应用'),
),
body: const Center(
child: Text(
'Hello, Flutter!',
style: TextStyle(fontSize: 24),
),
),
);
}
}
运行:
flutter run
如果你连接了模拟器或真机,应用就会跑起来。看到“Hello, Flutter!”了吗?你已经入门了!
第四步:理解 Widgets 体系
Flutter 中的一切都是 Widget。Button 是 Widget,文字是 Widget,甚至布局容器也是 Widget。
1. StatefulWidget vs StatelessWidget
- StatelessWidget:UI 不随状态变化而变化。比如只显示静态文字。
- StatefulWidget:UI 会随状态变化而更新。比如计数器、表单输入。
2. 常用 Widget 示例
// 图片
Image.network('https://example.com/image.jpg')
// 按钮
ElevatedButton(
onPressed: () {},
child: const Text('点击我'),
)
// 文本输入框
TextField(
decoration: const InputDecoration(labelText: '请输入姓名'),
)
第五步:布局实战
Flutter 的布局系统非常直观。我们用一个“用户资料卡片”作为案例,学会常用的布局 Widget。
目标:做一个包含头像、姓名、简介、关注按钮的卡片。
import 'package:flutter/material.dart';
class UserProfileCard extends StatelessWidget {
const UserProfileCard({super.key});
@override
Widget build(BuildContext context) {
return Card(
elevation: 4,
margin: const EdgeInsets.all(16),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 头像和姓名行
Row(
children: [
const CircleAvatar(
radius: 30,
backgroundImage: NetworkImage(
'https://i.pravatar.cc/150?img=3',
),
),
const SizedBox(width: 16),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'张三',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const Text(
'Flutter 开发者',
style: TextStyle(color: Colors.grey),
),
],
),
const Spacer(),
// 关注按钮
ElevatedButton(
onPressed: () {},
child: const Text('关注'),
),
],
),
const SizedBox(height: 16),
// 简介
const Text(
'热爱技术,喜欢分享。正在学习 Flutter 跨平台开发。',
style: TextStyle(fontSize: 14),
),
const SizedBox(height: 16),
// 统计数据
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildStatItem('文章', '128'),
_buildStatItem('粉丝', '5.2k'),
_buildStatItem('关注', '36'),
],
),
],
),
),
);
}
Widget _buildStatItem(String label, String value) {
return Column(
children: [
Text(
value,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
Text(
label,
style: const TextStyle(color: Colors.grey),
),
],
);
}
}
布局核心 Widget 解析:
| Widget | 用途 |
|---|---|
Column |
垂直排列子元素 |
Row |
水平排列子元素 |
Stack |
层叠布局 |
Padding |
内边距 |
SizedBox |
固定大小或间距 |
Expanded |
填充剩余空间 |
ListView |
列表滚动 |
第六步:状态管理入门
当应用变得复杂,我们需要管理状态。Flutter 官方推荐的入门方案是 Provider。
1. 创建 Model
import 'package:flutter/material.dart';
class CounterModel with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // 通知 UI 更新
}
}
2. 在 main.dart 中提供 Model
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => CounterModel(),
child: const MyApp(),
),
);
}
3. 在 Widget 中使用
class CounterWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = context.watch<CounterModel>();
return Column(
children: [
Text('点击次数: ${counter.count}'),
ElevatedButton(
onPressed: () => context.read<CounterModel>().increment(),
child: const Text('点击'),
),
],
);
}
}
第七步:网络请求实战
做一个“新闻列表”功能,从 API 获取数据并显示。
1. 添加依赖
在 pubspec.yaml 中添加:
dependencies:
flutter:
sdk: flutter
http: ^1.1.0
2. 创建数据模型
class NewsItem {
final String title;
final String description;
final String imageUrl;
NewsItem({
required this.title,
required this.description,
required this.imageUrl,
});
factory NewsItem.fromJson(Map<String, dynamic> json) {
return NewsItem(
title: json['title'],
description: json['description'],
imageUrl: json['image_url'],
);
}
}
3. 网络请求服务
import 'dart:convert';
import 'package:http/http.dart' as http;
class NewsService {
static const String apiUrl = 'https://jsonplaceholder.typicode.com/posts';
Future<List<NewsItem>> fetchNews() async {
final response = await http.get(Uri.parse(apiUrl));
if (response.statusCode == 200) {
List<dynamic> data = jsonDecode(response.body);
return data.map((item) => NewsItem.fromJson(item)).toList();
} else {
throw Exception('Failed to load news');
}
}
}
4. 展示新闻列表
class NewsListPage extends StatefulWidget {
const NewsListPage({super.key});
@override
State<NewsListPage> createState() => _NewsListPageState();
}
class _NewsListPageState extends State<NewsListPage> {
late Future<List<NewsItem>> _futureNews;
final NewsService _service = NewsService();
@override
void initState() {
super.initState();
_futureNews = _service.fetchNews();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('新闻列表')),
body: FutureBuilder<List<NewsItem>>(
future: _futureNews,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('错误: ${snapshot.error}'));
} else if (!snapshot.hasData) {
return const Center(child: Text('暂无数据'));
} else {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
final item = snapshot.data![index];
return ListTile(
title: Text(item.title),
subtitle: Text(item.description.length > 50
? '${item.description.substring(0, 50)}...'
: item.description),
);
},
);
}
},
),
);
}
}
第八步:路由导航
多页面应用需要路由管理。Flutter 提供了简单强大的路由系统。
1. 定义路由
final routes = {
'/': (context) => const HomePage(),
'/profile': (context) => const ProfilePage(),
'/settings': (context) => const SettingsPage(),
};
2. 跳转页面
// 从当前页面跳转到 ProfilePage
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const ProfilePage()),
);
// 返回上一页
Navigator.pop(context);
// 替换当前页面(不保留历史)
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const NewPage()),
);
第九步:添加动画
Flutter 内置动画支持,让应用更生动。
简单淡入动画:
class FadeInWidget extends StatefulWidget {
const FadeInWidget({super.key});
@override
State<FadeInWidget> createState() => _FadeInWidgetState();
}
class _FadeInWidgetState extends State<FadeInWidget>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
_animation = Tween<double>(begin: 0, end: 1).animate(_controller);
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _animation,
child: const Text('淡入效果'),
);
}
}
第十步:打包发布
Android 包:
flutter build apk --release
生成文件在:build/app/outputs/flutter-apk/app-release.apk
iOS 包:
flutter build ios --release
然后打开 Xcode 进行签名和上传 App Store。
学习建议
- 动手比看重要:每学一个概念,立刻写代码验证。
- 阅读官方文档:docs.flutter.dev 是最权威的来源。
- 参与社区:Flutter 社区非常活跃,有问题多搜索 Stack Overflow。
- 从小项目开始:先做一个待办事项清单,再做天气应用,最后做一个完整的社交类应用。
结语
恭喜你!你已经掌握了 Flutter 开发的核心知识。记住,编程不是一蹴而就的,它是一个不断实践和积累经验的过程。当你写出第一个能跑在真机上的应用时,那种成就感是无与伦比的。
Flutter 的生态系统非常庞大,还有 BLoC、Riverpod、GetX 等状态管理方案,还有 Firebase 集成、动画、手势处理等高级主题。但只要你掌握了基础,后续的学习会非常顺畅。
现在就打开你的编辑器,写下第一行 Flutter 代码吧!
