Flutter 跨平台移动开发入门教程
Flutter 作为 Google 推出的跨平台移动开发框架,以其"一次编写,多处运行"的理念和出色的性能表现,正在改变移动开发的格局。本文将从核心概念、环境搭建、Widget 体系、布局系统、状态管理、网络请求等方面,带你系统掌握 Flutter 开发基础,并通过实战案例巩固所学知识。
# 下载并解压 cd ~/development unzip ~/Downloads/flutter_macos_3.16.0-stable.zip # 添加到 PATH(编辑 ~/.zshrc 或 ~/.bash_profile) export PATH="$PATH:`pwd`/flutter/bin" # 验证安装 flutter doctor`flutter doctor` 命令会检查环境配置,列出缺少的工具链。根据提示补充安装即可。 ### 2.2 配置 IDE VS Code 和 Android Studio 是主流选择。推荐使用 VS Code 配合 Flutter 插件: 1. 安装 Flutter 和 Dart 插件 2. 运行 `Flutter: Run Flutter Doctor` 验证配置 3. 使用 `Flutter: New Project` 创建项目 Android Studio 则内置了完整的 Flutter 支持,包括设备管理、性能分析工具、布局检查器等。 ### 2.3 创建第一个项目
# 创建项目 flutter create hello_flutter # 进入项目目录 cd hello_flutter # 运行项目(确保有模拟器或真机连接) flutter run首次运行会下载依赖,可能需要几分钟。成功后会看到一个简单的计数器应用。 ## 三、Widget 基础 Flutter 中一切皆 Widget。从按钮、文本到布局容器,所有 UI 元素都是 Widget。 ### 3.1 StatelessWidget 与 StatefulWidget Widget 分为无状态和有状态两类: **StatelessWidget**:不可变组件,创建后状态不会改变。适用于纯展示场景。
class GreetingCard extends StatelessWidget {
final String name;
const GreetingCard({super.key, required this.name});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16),
child: Text('Hello, $name!'),
);
}
}
**StatefulWidget**:可变组件,通过 State 对象管理可变状态。状态改变时触发重建。
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State createState() => _CounterState();
}
class _CounterState extends State {
int count = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Count: $count'),
ElevatedButton(
onPressed: () => setState(() => count++),
child: const Text('Increment'),
),
],
);
}
}
关键点在于 `setState` 调用。它会标记 State 为 dirty,触发下一帧重建 Widget 树。避免在 build 方法中调用 setState,会导致无限循环。
### 3.2 常用基础 Widget
**Text**:显示文本,支持丰富样式配置。
Text(
'Flutter Tutorial',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
)
**Image**:加载图片,支持网络、本地、资源等多种来源。
// 网络图片
Image.network('https://example.com/photo.jpg')
// 本地资源(需要在 pubspec.yaml 声明)
Image.asset('assets/logo.png')
// 本地文件
Image.file(File('/path/to/image.jpg'))
**Container**:多功能容器,可设置背景、边框、阴影、圆角等。
Container(
width: 200,
height: 100,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: const Text('Card Content'),
)
## 四、布局系统
Flutter 采用 Flex 布局模型,通过 Row、Column、Stack 等容器组织 Widget。
### 4.1 Row 与 Column
Row 和 Column 是最常用的线性布局容器。
// 水平排列
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Icon(Icons.home),
Icon(Icons.search),
Icon(Icons.settings),
],
)
// 垂直排列
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Title'),
Text('Subtitle'),
Text('Description'),
],
)
`mainAxisAlignment` 控制主轴对齐,`crossAxisAlignment` 控制交叉轴对齐。理解这两个属性是掌握布局的关键。
### 4.2 Flex 扩展
使用 Expanded 和 Flexible 控制 Widget 在 Flex 容器中的占比。
Row(
children: [
Expanded(
flex: 2,
child: Container(color: Colors.red),
),
Expanded(
flex: 1,
child: Container(color: Colors.blue),
),
],
)
这段代码创建两个容器,红色占据 2/3 宽度,蓝色占据 1/3。Expanded 强制子组件填满剩余空间,Flexible 则允许子组件根据内容大小分配空间。
### 4.3 Stack 层叠布局
Stack 允许子组件堆叠显示,配合 Positioned 实现绝对定位。
Stack(
children: [
Image.network('https://example.com/background.jpg'),
Positioned(
bottom: 16,
left: 16,
right: 16,
child: Text(
'Overlay Text',
style: TextStyle(
color: Colors.white,
fontSize: 20,
shadows: [Shadow(blurRadius: 4, color: Colors.black)],
),
),
),
],
)
常用于实现卡片上的标签、图片上的文字遮罩等效果。
## 五、状态管理
随着应用复杂度增加,状态管理成为关键挑战。Flutter 提供了多种方案。
### 5.1 InheritedWidget
Flutter 内置的状态传递机制,允许 Widget 树中共享数据。
class InheritedCounter extends InheritedWidget {
final int count;
const InheritedCounter({
super.key,
required this.count,
required super.child,
});
static InheritedCounter? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType();
}
@override
bool updateShouldNotify(InheritedCounter old) => count != old.count;
}
// 使用时
class CounterDisplay extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = InheritedCounter.of(context);
return Text('Count: ${counter?.count ?? 0}');
}
}
InheritedWidget 是 Provider 等库的基础,理解它有助于掌握状态管理原理。
### 5.2 Provider
最流行的状态管理库之一,基于 InheritedWidget 封装,使用更简单。
// 定义状态模型
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
// 在顶层提供状态
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => CounterModel(),
child: const MyApp(),
),
);
}
// 消费状态
class CounterWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer(
builder: (context, counter, child) {
return Text('Count: ${counter.count}');
},
);
}
}
Provider 通过 ChangeNotifier 和 Consumer 实现状态共享和自动更新。优点是学习曲线平缓,性能开销小。
### 5.3 Riverpod 与 Bloc
对于更复杂的场景,Riverpod 和 Bloc 是流行选择。
**Riverpod**:Provider 的改进版,解决了 Provider 的部分限制,支持多 Provider 实例、更好的可测试性。
**Bloc**:基于事件驱动的状态管理,适合复杂业务逻辑。通过 Stream 管理状态流转,代码结构清晰但有一定学习成本。
选择建议:小型项目用 Provider,中大型项目用 Riverpod 或 Bloc。关键是保持一致性,避免混用多种方案。
## 六、网络请求
移动应用通常需要与后端 API 交互。Flutter 中常用 http 和 dio 库。
### 6.1 使用 http 库
import 'package:http/http.dart' as http; import 'dart:convert'; Future### 6.2 使用 dio 库 dio 提供了更强大的功能:拦截器、请求取消、文件上传等。fetchUser(int id) async { final response = await http.get( Uri.parse('https://api.example.com/users/$id'), ); if (response.statusCode == 200) { return User.fromJson(jsonDecode(response.body)); } else { throw Exception('Failed to load user'); } } // 数据模型 class User { final int id; final String name; User({required this.id, required this.name}); factory User.fromJson(Map json) { return User( id: json['id'], name: json['name'], ); } }
import 'package:dio/dio.dart';
final dio = Dio(BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 3),
));
// 添加拦截器
dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
options.headers['Authorization'] = 'Bearer $token';
return handler.next(options);
},
onError: (error, handler) {
// 统一错误处理
return handler.next(error);
},
));
// 发送请求
Future> fetchPosts() async {
final response = await dio.get('/posts');
return (response.data as List)
.map((json) => Post.fromJson(json))
.toList();
}
实际项目中建议封装网络层,统一处理错误、重试、缓存等逻辑。
## 七、实战案例:新闻应用
结合前面所学,构建一个简单的新闻列表应用。
### 7.1 项目结构
lib/
├── main.dart
├── models/
│ └── article.dart
├── services/
│ └── news_service.dart
├── providers/
│ └── news_provider.dart
└── screens/
├── home_screen.dart
└── detail_screen.dart
### 7.2 数据模型与服务
// models/article.dart
class Article {
final String title;
final String description;
final String urlToImage;
final String url;
Article({
required this.title,
required this.description,
required this.urlToImage,
required this.url,
});
factory Article.fromJson(Map json) {
return Article(
title: json['title'] ?? '',
description: json['description'] ?? '',
urlToImage: json['urlToImage'] ?? '',
url: json['url'] ?? '',
);
}
}
// services/news_service.dart
class NewsService {
final Dio _dio = Dio();
final String apiKey = 'YOUR_API_KEY';
Future> fetchTopHeadlines() async {
final response = await _dio.get(
'https://newsapi.org/v2/top-headlines',
queryParameters: {
'country': 'us',
'apiKey': apiKey,
},
);
final articles = response.data['articles'] as List;
return articles.map((json) => Article.fromJson(json)).toList();
}
}
### 7.3 状态管理
// providers/news_provider.dart
class NewsProvider extends ChangeNotifier {
final NewsService _service = NewsService();
List _articles = [];
bool _loading = false;
String? _error;
List get articles => _articles;
bool get loading => _loading;
String? get error => _error;
Future loadNews() async {
_loading = true;
_error = null;
notifyListeners();
try {
_articles = await _service.fetchTopHeadlines();
} catch (e) {
_error = e.toString();
} finally {
_loading = false;
notifyListeners();
}
}
}
### 7.4 UI 实现
// screens/home_screen.dart
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('News')),
body: Consumer(
builder: (context, provider, _) {
if (provider.loading) {
return const Center(child: CircularProgressIndicator());
}
if (provider.error != null) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Error: ${provider.error}'),
ElevatedButton(
onPressed: provider.loadNews,
child: const Text('Retry'),
),
],
),
);
}
return ListView.builder(
itemCount: provider.articles.length,
itemBuilder: (context, index) {
final article = provider.articles[index];
return ArticleCard(article: article);
},
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: context.read().loadNews,
child: const Icon(Icons.refresh),
),
);
}
}
class ArticleCard extends StatelessWidget {
final Article article;
const ArticleCard({required this.article});
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.all(8),
child: InkWell(
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => DetailScreen(article: article),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (article.urlToImage.isNotEmpty)
Image.network(
article.urlToImage,
height: 200,
width: double.infinity,
fit: BoxFit.cover,
),
Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
article.title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
article.description,
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
),
);
}
}
### 7.5 运行效果
这个应用实现了新闻列表的加载、显示和跳转详情页功能。通过 Provider 管理状态,代码结构清晰,易于维护和扩展。
## 总结
Flutter 以其独特的渲染机制、高效的开发体验和完整的生态,成为跨平台移动开发的优秀选择。掌握 Widget、布局、状态管理、网络请求等核心概念后,你已经具备了构建完整应用的能力。
学习路径建议:
1. **基础巩固**:深入理解 Widget 生命周期、布局约束机制
2. **进阶提升**:学习动画、自定义绘制、平台交互
3. **工程实践**:掌握测试、CI/CD、性能优化
Flutter 社区活跃,文档完善。遇到问题时,官方文档和 Stack Overflow 通常能找到答案。动手实践是最好的学习方式,从简单项目开始,逐步挑战更复杂的应用。
本文链接:https://www.kkkliao.cn/?id=956 转载需授权!
版权声明:本文由廖万里的博客发布,如需转载请注明出处。



手机流量卡
免费领卡
号卡合伙人
产品服务
关于本站
