嘿,朋友!看到你想学Flutter,我简直太开心了。真的,别被那些所谓的“高门槛”吓跑,Flutter其实是现在最友好的跨平台开发工具之一。我当年第一次上手的时候,也是对着屏幕发呆,不知道Dart和Java、Kotlin有啥区别。但只要你跟着我的步骤走,一杯咖啡的时间,你就能让自己的App在手机屏幕上跑起来。
咱们不整那些虚头巴脑的理论,直接干。我会把整个流程拆得碎碎的,就像我小时候搭积木一样,一块一块来,最后你会惊讶地发现:哇,我竟然真的做出了一个App!
第一步:在开始之前,先看看你的“工具箱”
在动手写代码之前,你得确保你的电脑里有这些家伙。这一步很关键,很多人卡在这里是因为少装了一个东西。
1. 硬件要求
首先,你的电脑得够劲。
- Windows: Intel Core i5 或更高,8GB 内存(建议16GB),SSD硬盘。
- Mac: macOS 10.15 或更高,建议8GB以上内存,Apple Silicon或Intel芯片都可以。
- Linux: Ubuntu 18.04 或更高,其他发行版也行,但Ubuntu是Flutter官方最推荐的。
2. 下载 Flutter SDK
这是核心。你去 Flutter 官网下载最新版本的 SDK。下载完后,解压到一个你觉得顺眼的地方,比如 C:\src\flutter 或者 ~/Documents/flutter。注意:路径里不要有空格或中文,这是很多新手踩的坑,我见过太多人因为路径问题编译失败而崩溃。
3. 安装编辑器
推荐 Visual Studio Code 或者 Android Studio。如果你已经是老手,IntelliJ IDEA 也没问题。我强烈推荐 VS Code,轻量、插件多,而且专门为 Flutter 做了优化。装上之后,记得装两个插件:Flutter 和 Dart。没有这两个插件,VS Code 就只是个普通的文本编辑器,帮不了你智能提示和调试。
4. 移动端开发环境
既然要写跨平台 App,你至少得装一个平台的开发环境。
- 想做 Android:安装 Android Studio,并安装 Android SDK。在 Android Studio 里,去 SDK Manager 里下载
Android SDK Platform 33(或者更高版本)和Android SDK Build-Tools。 - 想做 iOS:你得有一台 Mac,安装 Xcode。然后在终端运行
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer来配置命令行工具。
5. 验证安装(这一步很重要,别跳过!)
打开你的终端(Windows 用 CMD 或 PowerShell,Mac 用 Terminal),把 Flutter 的 bin 目录加到环境变量里。
Windows:
# 假设你把 Flutter 解压到了 C:\src\flutter
set PATH=%PATH%;C:\src\flutter\bin
flutter --version
Mac/Linux:
export PATH="$PATH:`pwd`/flutter/bin"
# 或者把它加到 ~/.bashrc 或 ~/.zshrc 里,这样重启终端也不用重新设置
flutter --version
运行 flutter doctor,这是 Flutter 的体检报告。你会看到一堆检查项:
- ✅ Android toolchain - 正常
- ✅ Chrome/Edge - 正常
- ❌ Xcode - 缺失
如果看到红色叉叉,就照着提示去下载安装。比如 Xcode 缺失,就去 App Store 下载 Xcode。全部变绿才能开始写代码,不然你跑起来会报错,那时你就不知道该怪环境还是怪代码了。
第二步:创建你的第一个 Flutter 项目
环境好了,咱们开始写代码。在终端输入:
flutter create my_first_app
这会在当前目录下创建一个叫 my_first_app 的文件夹。里面的结构看起来有点复杂,但其实核心就这几个:
lib/: 你的所有代码都放在这里。pubspec.yaml: 依赖管理文件,就像 Node.js 的 package.json,用来管理第三方库。android/和ios/: 平台特定的代码,你一般不用动它们。test/: 单元测试。
双击打开 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 App',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true, // 启用 Material Design 3
),
home: const MyHomePage(title: 'Flutter 入门示例'),
);
}
}
看懂了吗?
main()是入口,就像 C 语言的main函数。runApp()把一个 widget 树渲染到屏幕上。MyApp继承自StatelessWidget,意思是它是一个无状态组件,一旦渲染完成,界面就不会变。build()方法返回你定义的 UI 结构。在 Flutter 里,一切皆 Widget,按钮是 Widget,文字是 Widget,甚至整个 App 也是一个 Widget。
第三步:理解核心概念——Widget 和 State
这是 Flutter 最难跨越,也是最神奇的一步。我打个比方:你盖房子。
- Widget 就是砖头、木头、玻璃。你拿着这些材料,可以搭出墙、屋顶、窗户。
- State 就是房子里住的人。如果这个人今天开心,房子看起来就亮堂;明天他悲伤,房子就阴暗。状态改变了,房子(UI)跟着变。
Flutter 有两种 Widget:
- StatelessWidget:无状态组件。它不关心自己是谁,只关心别人给它什么。比如一个纯展示的文字标签,它的内容不会变。
- StatefulWidget:有状态组件。它能记住自己的变化。比如一个计数器,点击按钮后数字会变。
咱们来写一个真正的计数器 App,这是 Flutter 的“Hello World”。
把 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 Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// 这一行代码是关键!
// 告诉 Flutter:“嘿,我的状态变了,请重新渲染界面!”
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
重点解析:
MyHomePage继承自StatefulWidget。_MyHomePageState是它的具体状态管理类。注意前面的下划线_,在 Dart 里这表示私有,外部不能直接访问。setState(() { _counter++; })是灵魂!当你修改了数据,必须调用setState,Flutter 才会知道要更新 UI。如果你不调用,数字虽然加了,但屏幕上显示的还是旧数字。
现在,你可以运行了。连接你的手机(开启开发者模式和 USB 调试),或者启动 Android 模拟器/iOS 模拟器。在 VS Code 里按 F5,或者在终端运行:
flutter run
看着 App 在屏幕上跑起来,点击右下角的加号,数字增加。那一刻的成就感,相信我,值得你熬几个夜。
第四步:美化界面——布局和样式
现在的界面太简陋了。咱们加点料。Flutter 的布局有点像 CSS 的 Flexbox,但更直观。
主要布局 Widget:
- Row: 水平排列。
- Column: 垂直排列。
- Stack: 层叠布局,像 Photoshop 的图层。
- Container: 容器,可以加边距、填充、背景色、边框。
- Padding: 内边距。
- SizedBox: 强制大小,常用于制造间距。
举个例子,做一个简单的登录界面:
Scaffold(
appBar: AppBar(title: const Text('登录')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 头像
const CircleAvatar(
radius: 50,
backgroundImage: NetworkImage('https://example.com/avatar.jpg'),
),
const SizedBox(height: 20),
// 用户名输入框
TextField(
decoration: const InputDecoration(
labelText: '用户名',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.person),
),
),
const SizedBox(height: 10),
// 密码输入框
TextField(
obscureText: true, // 密码隐藏
decoration: const InputDecoration(
labelText: '密码',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.lock),
),
),
const SizedBox(height: 20),
// 登录按钮
ElevatedButton(
onPressed: () {
// 处理登录逻辑
print('点击登录');
},
style: ElevatedButton.styleFrom(
minimumSize: const Size(double.infinity, 50), // 全宽按钮
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: const Text('登录', style: TextStyle(fontSize: 18)),
),
],
),
),
)
你看,这就是 Flutter 的魅力。代码即 UI,结构清晰,嵌套直观。多练几次,你就能随手写出漂亮的界面。
第五步:处理数据和网络请求
App 不能是死的,得能联网拿数据。比如,做一个天气 App,显示实时天气。
在 pubspec.yaml 里添加依赖:
dependencies:
flutter:
sdk: flutter
http: ^1.1.0 # 用于网络请求
provider: ^6.0.0 # 用于状态管理(后面会讲)
然后运行 flutter pub get 拉取依赖。
创建一个简单的模型和数据获取方法:
import 'dart:convert';
import 'package:http/http.dart' as http;
class WeatherData {
final String city;
final double temp;
final String description;
WeatherData({required this.city, required this.temp, required this.description});
factory WeatherData.fromJson(Map<String, dynamic> json) {
return WeatherData(
city: json['name'],
temp: json['main']['temp'].toDouble(),
description: json['weather'][0]['description'],
);
}
}
class WeatherService {
static const String apiKey = 'YOUR_API_KEY'; // 去 openweathermap.org 注册拿一个
static const String baseUrl = 'https://api.openweathermap.org/data/2.5/weather';
Future<WeatherData> fetchWeather(String city) async {
final response = await http.get(
Uri.parse('$baseUrl?q=$city&appid=$apiKey&units=metric&lang=zh_cn'),
);
if (response.statusCode == 200) {
return WeatherData.fromJson(jsonDecode(response.body));
} else {
throw Exception('获取天气失败');
}
}
}
然后在 UI 里调用它:
FutureBuilder<WeatherData>(
future: WeatherService().fetchWeather('Beijing'),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: [
Text(snapshot.data!.city, style: const TextStyle(fontSize: 24)),
Text('${snapshot.data!.temp}°C', style: const TextStyle(fontSize: 48)),
Text(snapshot.data!.description, style: const TextStyle(fontSize: 18)),
],
);
} else if (snapshot.hasError) {
return Text('错误: ${snapshot.error}');
}
return const CircularProgressIndicator(); // 加载中动画
},
)
FutureBuilder 是 Flutter 处理异步数据的利器。它在等待数据时显示加载动画,数据回来后显示内容,出错了显示错误信息。
第六步:状态管理——从新手到进阶
当你的 App 变复杂,几十个页面,数据到处传,你会疯的。这时候需要状态管理。
推荐新手从 Provider 开始。它是 Flutter 官方推荐的入门级状态管理方案,简单易懂。
假设你要做一个用户登录状态管理。
- 定义 Model (
models/user_model.dart):
class User {
final String name;
final String token;
User({required this.name, required this.token});
}
- 定义 Provider (
providers/user_provider.dart):
import 'package:flutter/foundation.dart';
import '../models/user_model.dart';
class UserProvider with ChangeNotifier {
User? _user;
User? get user => _user;
bool get isLoggedIn => _user != null;
Future<void> login(String name, String token) async {
// 模拟网络请求
await Future.delayed(const Duration(seconds: 1));
_user = User(name: name, token: token);
notifyListeners(); // 通知所有订阅者,UI 要刷新
}
void logout() {
_user = null;
notifyListeners();
}
}
- 在
main.dart中包裹MultiProvider:
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => UserProvider()),
],
child: const MyApp(),
),
);
}
- 在页面中使用:
final userProvider = context.watch<UserProvider>(); // 监听变化
// 或者
final userProvider = context.read<UserProvider>(); // 只读取,不监听
if (userProvider.isLoggedIn) {
return Text('欢迎, ${userProvider.user?.name}');
} else {
return const Text('请登录');
}
Provider 让你把业务逻辑和 UI 分离,代码更整洁,也更易测试。等你熟练了,可以再尝试 Riverpod、Bloc 或 GetX,但那都是后话了。
第七步:调试和优化
写代码不可能一次对。学会调试是必修课。
- 热重载 (Hot Reload): 按
r或者点击 VS Code 的刷新按钮。它不会重启 App,而是注入新代码,保留当前状态。修改 UI 时超好用。 - 热重启 (Hot Restart): 按
R。重启 App,重置状态。修改了main.dart或配置时常用。 - Debug Console: 看
print()输出,或者用debugPrint()。 - DevTools: 运行
flutter pub global activate devtools,然后flutter pub global run devtools。可以查看 Widget 树、内存泄漏、网络请求等。
性能优化小贴士:
- 避免在
build()方法里做耗时操作,比如网络请求。 - 使用
const关键字。能加const的地方尽量加,Flutter 会优化掉不必要的重建。 - 列表使用
ListView.builder,而不是ListView,它只渲染可见的 Item。
第八步:打包和发布上架
终于到了这一步!你的 App 写完了,怎么让别人用上?
Android 发布到 Google Play
- 生成签名的 APK/AAB:
创建 keystore 文件:
然后在keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias uploadandroid/key.properties里配置路径和密钥。
运行打包命令:
flutter build appbundle --release
生成的文件在 build/app/outputs/bundle/release/app-release.aab。Google Play 要求 AAB 格式,不要搞错。
创建 Google Play Console 账号:需要支付 25 美元注册费。
上传 AAB:在控制台创建应用,填写信息,上传 AAB,提交审核。审核一般 1-3 天。
iOS 发布到 App Store
配置 Xcode: 打开
ios/Runner.xcworkspace,设置 Bundle Identifier,选择你的 Team(免费账号或付费账号)。构建 Archive: 在 Xcode 里选择
Generic iOS Device,然后点击Product -> Archive。上传到 App Store Connect: Archive 完成后
