Flutter 完整开发指南
Flutter 基础
1.1 开发环境搭建
1.1.1 必要工具安装
-
Flutter SDK
# Windows # 1. 下载 Flutter SDK # 2. 解压到合适的目录,如 C:\flutter # 3. 添加 flutter\bin 到环境变量 Path # macOS brew install flutter # Linux sudo snap install flutter --classic
-
开发工具
- Android Studio
- VS Code
- Xcode (仅 macOS)
- Appuploader (iOS 开发助手,用于简化 iOS 应用的打包和上传流程)
-
环境检查
flutter doctor
1.1.2 IDE 配置
-
VS Code 插件
- Flutter
- Dart
- Flutter Widget Snippets
-
Android Studio 插件
- Flutter
- Dart
1.1.3 创建项目
-
使用命令行创建
# 创建新项目 flutter create my_app # 指定组织名称 flutter create --org com.example my_app # 指定使用特定平台 flutter create --platforms android,ios my_app # 创建包含示例代码的项目 flutter create --sample=material.AppBar.1 my_app
-
使用 VS Code 创建
- 打开 VS Code
- 按下
Ctrl+Shift+P
(Windows/Linux)或Cmd+Shift+P
(macOS) - 输入
Flutter: New Project
- 选择项目存储位置
- 输入项目名称(使用小写字母和下划线)
- 等待项目创建和依赖安装完成
-
项目结构说明
my_app/ ├── android/ # Android 平台相关代码 ├── ios/ # iOS 平台相关代码 ├── lib/ # Dart 源代码 │ └── main.dart # 应用入口文件 ├── test/ # 测试文件 ├── web/ # Web 平台相关代码 ├── pubspec.yaml # 项目配置文件 └── README.md # 项目说明文档
-
运行项目
# 命令行运行 cd my_app flutter run # VS Code 运行 # 1. 打开项目文件夹 # 2. 选择目标设备(模拟器或真机) # 3. 按 F5 或点击 "运行 > 启动调试"
-
常用 VS Code 快捷操作
Ctrl + F5
(Windows/Linux)或Cmd + F5
(macOS):无调试运行Ctrl + Shift + F5
:重新运行Shift + Alt + F
:格式化代码Ctrl + .
:快速修复/重构Ctrl + Space
:触发建议Ctrl + Shift + R
:重新加载
-
VS Code 常用插件推荐
- Flutter
- Dart
- Flutter Widget Snippets
- Awesome Flutter Snippets
- Flutter Tree
- pubspec Assist
- Error Lens
1.2 Dart 语言基础
1.2.1 变量与数据类型
// 变量声明
var name = 'Bob'; // 类型推断
String name = 'Bob'; // 显式声明
dynamic name = 'Bob'; // 动态类型
final name = 'Bob'; // 运行时常量
const pi = 3.14; // 编译时常量
// 内置数据类型
int age = 30; // 整数
double height = 1.75; // 浮点数
String name = 'Bob'; // 字符串
bool isStudent = true; // 布尔值
1.2.2 集合类型
// List(列表)
List<String> fruits = ['apple', 'banana', 'orange'];
var numbers = <int>[1, 2, 3, 4, 5];
// Set(集合)
Set<String> uniqueNames = {'John', 'Jane', 'Bob'};
// Map(字典)
Map<String, int> scores = {
'math': 95,
'english': 85,
'history': 90,
};
1.2.3 函数
// 基本函数
int add(int a, int b) {
return a + b;
}
// 箭头函数
int multiply(int a, int b) => a * b;
// 可选参数
String greet(String name, [String? title]) {
return title != null ? '$title $name' : 'Hello $name';
}
// 命名参数
void printPerson({
required String name,
int? age,
String? address,
}) {
print('Name: $name, Age: ${age ?? "Unknown"}, Address: ${address ?? "N/A"}');
}
// 函数作为参数
void processNumbers(List<int> numbers, int Function(int) processor) {
for (var number in numbers) {
print(processor(number));
}
}
1.2.4 类与对象
class Person {
// 属性
final String name;
int age;
// 构造函数
Person(this.name, this.age);
// 命名构造函数
Person.guest() : name = 'Guest', age = 0;
// 方法
void introduce() {
print('My name is $name and I am $age years old.');
}
// Getter
String get info => '$name ($age)';
// Setter
set setAge(int value) {
if (value >= 0) {
age = value;
}
}
}
// 继承
class Student extends Person {
String school;
Student(String name, int age, this.school) : super(name, age);
void introduce() {
super.introduce();
print('I study at $school.');
}
}
// Mixin
mixin Musical {
void playMusic() {
print('Playing music...');
}
}
class MusicStudent extends Student with Musical {
MusicStudent(String name, int age, String school)
: super(name, age, school);
}
1.3 Flutter 核心概念
1.3.1 Widget
Flutter 中一切都是 Widget,它们是用户界面的基本构建块。
-
StatelessWidget
class GreetingWidget extends StatelessWidget { final String name; const GreetingWidget({ Key? key, required this.name, }) : super(key: key); Widget build(BuildContext context) { return Text('Hello, $name!'); } }
-
StatefulWidget
class CounterWidget extends StatefulWidget { _CounterWidgetState createState() => _CounterWidgetState(); } class _CounterWidgetState extends State<CounterWidget> { int _counter = 0; void _increment() { setState(() { _counter++; }); } Widget build(BuildContext context) { return Column( children: [ Text('Count: $_counter'), ElevatedButton( onPressed: _increment, child: Text('Increment'), ), ], ); } }
1.3.2 生命周期
class LifecycleWidget extends StatefulWidget {
_LifecycleWidgetState createState() => _LifecycleWidgetState();
}
class _LifecycleWidgetState extends State<LifecycleWidget> {
void initState() {
super.initState();
print('1. initState - 组件初始化');
}
void didChangeDependencies() {
super.didChangeDependencies();
print('2. didChangeDependencies - 依赖变化');
}
void didUpdateWidget(LifecycleWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print('3. didUpdateWidget - 组件更新');
}
void setState(VoidCallback fn) {
super.setState(fn);
print('4. setState - 状态更新');
}
void deactivate() {
print('5. deactivate - 组件停用');
super.deactivate();
}
void dispose() {
print('6. dispose - 组件销毁');
super.dispose();
}
Widget build(BuildContext context) {
print('7. build - 构建UI');
return Container();
}
}
1.4 常用组件
1.4.1 基础组件
// 文本组件
Text(
'Hello World',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
)
// 图片组件
Image.network('https://example.com/image.jpg')
Image.asset('assets/images/logo.png')
// 按钮组件
ElevatedButton(
onPressed: () {},
child: Text('点击我'),
)
TextButton(
onPressed: () {},
child: Text('文本按钮'),
)
IconButton(
icon: Icon(Icons.add),
onPressed: () {},
)
// 输入框组件
TextField(
decoration: InputDecoration(
labelText: '用户名',
hintText: '请输入用户名',
border: OutlineInputBorder(),
),
onChanged: (value) {
print('输入内容: $value');
},
)
1.4.2 布局组件
// Container - 容器组件
Container(
width: 200,
height: 200,
padding: EdgeInsets.all(16),
margin: EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black26,
offset: Offset(0, 2),
blurRadius: 6,
),
],
),
child: Text('容器内容'),
)
// Row - 水平布局
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('左侧'),
Text('中间'),
Text('右侧'),
],
)
// Column - 垂直布局
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('顶部'),
Text('中部'),
Text('底部'),
],
)
// Stack - 层叠布局
Stack(
children: [
Image.asset('background.jpg'),
Positioned(
bottom: 16,
right: 16,
child: Text('覆盖文本'),
),
],
)
// Expanded & Flexible - 弹性布局
Row(
children: [
Expanded(
flex: 2,
child: Container(color: Colors.red),
),
Flexible(
flex: 1,
child: Container(color: Colors.blue),
),
],
)
1.5 布局与导航
1.5.1 基本页面布局
Scaffold(
appBar: AppBar(
title: Text('应用标题'),
actions: [
IconButton(
icon: Icon(Icons.settings),
onPressed: () {},
),
],
),
body: Center(
child: Text('页面内容'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
drawer: Drawer(
child: ListView(
children: [
DrawerHeader(
child: Text('侧边栏头部'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('菜单项 1'),
onTap: () {},
),
],
),
),
)
1.5.2 导航
// 基本导航
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(),
),
);
// 带参数导航
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(id: 123),
),
);
// 命名路由配置
MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => HomeScreen(),
'/detail': (context) => DetailScreen(),
'/settings': (context) => SettingsScreen(),
},
)
// 使用命名路由
Navigator.pushNamed(
context,
'/detail',
arguments: {'id': 123},
);
// 获取路由参数
class DetailScreen extends StatelessWidget {
Widget build(BuildContext context) {
final args = ModalRoute.of(context)!.settings.arguments as Map;
return Scaffold(
body: Center(
child: Text('详情页 ID: ${args['id']}'),
),
);
}
}
Flutter 进阶
2.1 状态管理
2.1.1 Provider
// 1. 定义数据模型
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
// 2. 提供状态
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => Counter(),
child: MyApp(),
),
);
}
// 3. 使用状态
class CounterWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
children: [
// 读取状态
Consumer<Counter>(
builder: (context, counter, child) {
return Text('Count: ${counter.count}');
},
),
// 修改状态
ElevatedButton(
onPressed: () {
context.read<Counter>().increment();
},
child: Text('增加'),
),
],
);
}
}
2.1.2 GetX
// 1. 定义控制器
class CounterController extends GetxController {
var count = 0.obs;
void increment() => count++;
}
// 2. 使用控制器
class CounterPage extends StatelessWidget {
final controller = Get.put(CounterController());
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
// 自动更新UI
Obx(() => Text('Count: ${controller.count}')),
ElevatedButton(
onPressed: controller.increment,
child: Text('增加'),
),
],
),
),
);
}
}
2.2 网络请求
2.2.1 Dio 网络请求
class ApiService {
final Dio _dio = Dio();
ApiService() {
_dio.options.baseUrl = 'https://api.example.com';
_dio.options.connectTimeout = 5000;
_dio.options.receiveTimeout = 3000;
// 添加拦截器
_dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
// 请求前处理
options.headers['Authorization'] = 'Bearer token';
return handler.next(options);
},
onResponse: (response, handler) {
// 响应处理
return handler.next(response);
},
onError: (DioError e, handler) {
// 错误处理
return handler.next(e);
},
));
}
// GET 请求
Future<List<Post>> getPosts() async {
try {
final response = await _dio.get('/posts');
return (response.data as List)
.map((json) => Post.fromJson(json))
.toList();
} catch (e) {
throw Exception('Failed to load posts');
}
}
// POST 请求
Future<Post> createPost(Post post) async {
try {
final response = await _dio.post(
'/posts',
data: post.toJson(),
);
return Post.fromJson(response.data);
} catch (e) {
throw Exception('Failed to create post');
}
}
}
2.2.2 GraphQL 请求
// 1. 定义查询
const String getPostsQuery = r'''
query GetPosts {
posts {
id
title
content
author {
name
}
}
}
''';
// 2. 实现 GraphQL 客户端
class GraphQLService {
late GraphQLClient _client;
GraphQLService() {
final HttpLink httpLink = HttpLink('https://api.example.com/graphql');
_client = GraphQLClient(
link: httpLink,
cache: GraphQLCache(),
);
}
Future<List<Post>> getPosts() async {
try {
final QueryResult result = await _client.query(
QueryOptions(
document: gql(getPostsQuery),
),
);
if (result.hasException) {
throw result.exception!;
}
return (result.data!['posts'] as List)
.map((json) => Post.fromJson(json))
.toList();
} catch (e) {
throw Exception('Failed to fetch posts: $e');
}
}
}
2.3 数据持久化
2.3.1 SharedPreferences
class PreferencesService {
static late SharedPreferences _prefs;
static Future<void> init() async {
_prefs = await SharedPreferences.getInstance();
}
// 存储数据
static Future<bool> setString(String key, String value) async {
return await _prefs.setString(key, value);
}
static Future<bool> setInt(String key, int value) async {
return await _prefs.setInt(key, value);
}
// 读取数据
static String? getString(String key) {
return