跳到主要内容

Flutter跨平台开发详解

Flutter是Google开发的开源UI工具包,用于从单一代码库构建漂亮的、原生编译的多平台应用程序。Flutter使用Dart语言,提供了丰富的Widget系统和强大的性能,是现代跨平台开发的优秀选择。

核心价值

Flutter = 单一代码库 + 原生性能 + 丰富UI + 热重载

  • 🚀 跨平台统一:一套代码运行在iOS、Android、Web、桌面
  • 原生性能:直接编译为原生代码,性能接近原生应用
  • 🎨 丰富UI组件:Material Design和Cupertino风格组件
  • 🔥 热重载:毫秒级代码更新,极大提升开发效率
  • 📦 完整生态:丰富的第三方包和工具链
  • 🎯 声明式UI:直观的UI构建方式,易于理解和维护

1. Dart语言基础

1.1 Dart语言特性

Dart是Flutter的编程语言,具有现代编程语言的特性,易于学习且功能强大。

Dart基础语法

Dart基础语法示例
dart
1// 1. 变量声明
2void main() {
3 // 类型推断
4 var name = 'Flutter';
5 var year = 2024;
6 var isAwesome = true;
7
8 // 显式类型声明
9 String language = 'Dart';
10 int version = 3;
11 bool isNullSafe = true;
12
13 // 常量
14 const pi = 3.14159;
15 final currentTime = DateTime.now();
16
17 // 空安全
18 String? nullableName; // 可为空
19 String nonNullableName = 'Flutter'; // 不可为空
20
21 // 空值检查
22 print(nullableName?.length ?? 0); // 安全调用
23
24 // 集合类型
25 List<String> fruits = ['apple', 'banana', 'orange'];
26 Set<int> uniqueNumbers = {1, 2, 3, 4, 5};
27 Map<String, int> scores = {
28 'Alice': 95,
29 'Bob': 87,
30 'Charlie': 92,
31 };
32
33 // 展开操作符
34 List<String> moreFruits = [...fruits, 'grape', 'kiwi'];
35
36 // 条件表达式
37 String message = isAwesome ? 'Flutter is awesome!' : 'Try Flutter';
38
39 print('$name $year: $message');
40}
41
42// 2. 函数定义
43// 普通函数
44String greet(String name, {String greeting = 'Hello'}) {
45 return '$greeting, $name!';
46}
47
48// 箭头函数
49String greetShort(String name) => 'Hello, $name!';
50
51// 可选参数
52void printInfo(String name, [int? age, String? city]) {
53 print('Name: $name');
54 if (age != null) print('Age: $age');
55 if (city != null) print('City: $city');
56}
57
58// 命名参数
59void createUser({
60 required String name,
61 required String email,
62 int age = 18,
63 bool isActive = true,
64}) {
65 print('Creating user: $name ($email)');
66}
67
68// 3. 类和对象
69class Person {
70 // 属性
71 String name;
72 int age;
73 String? email;
74
75 // 构造函数
76 Person(this.name, this.age, {this.email});
77
78 // 命名构造函数
79 Person.guest() : name = 'Guest', age = 0;
80
81 // Getter和Setter
82 String get displayName => name.toUpperCase();
83
84 set displayName(String value) {
85 name = value.toLowerCase();
86 }
87
88 // 方法
89 void introduce() {
90 print('Hi, I\'m $name, $age years old.');
91 }
92
93 // 静态方法
94 static Person fromJson(Map<String, dynamic> json) {
95 return Person(
96 json['name'] as String,
97 json['age'] as int,
98 email: json['email'] as String?,
99 );
100 }
101
102 // 重写toString
103 @override
104 String toString() => 'Person(name: $name, age: $age)';
105}
106
107// 4. 继承
108class Student extends Person {
109 String school;
110 List<String> subjects;
111
112 Student(String name, int age, this.school, this.subjects)
113 : super(name, age);
114
115 @override
116 void introduce() {
117 super.introduce();
118 print('I study at $school.');
119 }
120
121 void addSubject(String subject) {
122 subjects.add(subject);
123 }
124}
125
126// 5. 抽象类和接口
127abstract class Animal {
128 String name;
129
130 Animal(this.name);
131
132 // 抽象方法
133 void makeSound();
134
135 // 具体方法
136 void sleep() {
137 print('$name is sleeping...');
138 }
139}
140
141class Dog extends Animal {
142 Dog(String name) : super(name);
143
144 @override
145 void makeSound() {
146 print('$name says: Woof!');
147 }
148}
149
150// 6. Mixin混入
151mixin Flyable {
152 void fly() {
153 print('Flying high!');
154 }
155}
156
157mixin Swimmable {
158 void swim() {
159 print('Swimming gracefully!');
160 }
161}
162
163class Duck extends Animal with Flyable, Swimmable {
164 Duck(String name) : super(name);
165
166 @override
167 void makeSound() {
168 print('$name says: Quack!');
169 }
170}

2. Widget与布局系统

2.1 Widget核心概念

Flutter中一切皆Widget,Widget是Flutter应用的基本构建块。

StatelessWidget详解

StatelessWidget示例
dart
1import 'package:flutter/material.dart';
2
3// 1. 基础StatelessWidget
4class WelcomeScreen extends StatelessWidget {
5 final String title;
6 final String subtitle;
7 final VoidCallback? onPressed;
8
9 const WelcomeScreen({
10 Key? key,
11 required this.title,
12 this.subtitle = '',
13 this.onPressed,
14 }) : super(key: key);
15
16 @override
17 Widget build(BuildContext context) {
18 return Scaffold(
19 appBar: AppBar(
20 title: Text(title),
21 backgroundColor: Theme.of(context).primaryColor,
22 ),
23 body: Center(
24 child: Column(
25 mainAxisAlignment: MainAxisAlignment.center,
26 children: [
27 Icon(
28 Icons.flutter_dash,
29 size: 100,
30 color: Theme.of(context).primaryColor,
31 ),
32 SizedBox(height: 20),
33 Text(
34 title,
35 style: Theme.of(context).textTheme.headlineMedium,
36 textAlign: TextAlign.center,
37 ),
38 if (subtitle.isNotEmpty) ...[
39 SizedBox(height: 10),
40 Text(
41 subtitle,
42 style: Theme.of(context).textTheme.bodyLarge,
43 textAlign: TextAlign.center,
44 ),
45 ],
46 SizedBox(height: 30),
47 if (onPressed != null)
48 ElevatedButton(
49 onPressed: onPressed,
50 child: Text('开始使用'),
51 ),
52 ],
53 ),
54 ),
55 );
56 }
57}
58
59// 2. 自定义卡片组件
60class CustomCard extends StatelessWidget {
61 final String title;
62 final String description;
63 final String? imageUrl;
64 final List<String> tags;
65 final VoidCallback? onTap;
66
67 const CustomCard({
68 Key? key,
69 required this.title,
70 required this.description,
71 this.imageUrl,
72 this.tags = const [],
73 this.onTap,
74 }) : super(key: key);
75
76 @override
77 Widget build(BuildContext context) {
78 return Card(
79 elevation: 4,
80 margin: EdgeInsets.all(8),
81 child: InkWell(
82 onTap: onTap,
83 borderRadius: BorderRadius.circular(8),
84 child: Column(
85 crossAxisAlignment: CrossAxisAlignment.start,
86 children: [
87 // 图片部分
88 if (imageUrl != null)
89 ClipRRect(
90 borderRadius: BorderRadius.vertical(top: Radius.circular(8)),
91 child: Image.network(
92 imageUrl!,
93 height: 200,
94 width: double.infinity,
95 fit: BoxFit.cover,
96 errorBuilder: (context, error, stackTrace) {
97 return Container(
98 height: 200,
99 color: Colors.grey[300],
100 child: Icon(Icons.error, size: 50),
101 );
102 },
103 ),
104 ),
105
106 // 内容部分
107 Padding(
108 padding: EdgeInsets.all(16),
109 child: Column(
110 crossAxisAlignment: CrossAxisAlignment.start,
111 children: [
112 Text(
113 title,
114 style: Theme.of(context).textTheme.titleLarge,
115 maxLines: 2,
116 overflow: TextOverflow.ellipsis,
117 ),
118 SizedBox(height: 8),
119 Text(
120 description,
121 style: Theme.of(context).textTheme.bodyMedium,
122 maxLines: 3,
123 overflow: TextOverflow.ellipsis,
124 ),
125
126 // 标签部分
127 if (tags.isNotEmpty) ...[
128 SizedBox(height: 12),
129 Wrap(
130 spacing: 8,
131 runSpacing: 4,
132 children: tags.map((tag) => Chip(
133 label: Text(
134 tag,
135 style: TextStyle(fontSize: 12),
136 ),
137 backgroundColor: Theme.of(context).primaryColor.withOpacity(0.1),
138 )).toList(),
139 ),
140 ],
141 ],
142 ),
143 ),
144 ],
145 ),
146 ),
147 );
148 }
149}
150
151// 3. 响应式布局组件
152class ResponsiveLayout extends StatelessWidget {
153 final Widget mobile;
154 final Widget? tablet;
155 final Widget? desktop;
156
157 const ResponsiveLayout({
158 Key? key,
159 required this.mobile,
160 this.tablet,
161 this.desktop,
162 }) : super(key: key);
163
164 @override
165 Widget build(BuildContext context) {
166 return LayoutBuilder(
167 builder: (context, constraints) {
168 if (constraints.maxWidth >= 1200) {
169 return desktop ?? tablet ?? mobile;
170 } else if (constraints.maxWidth >= 800) {
171 return tablet ?? mobile;
172 } else {
173 return mobile;
174 }
175 },
176 );
177 }
178}
179
180// 4. 加载状态组件
181class LoadingWidget extends StatelessWidget {
182 final String? message;
183 final double size;
184 final Color? color;
185
186 const LoadingWidget({
187 Key? key,
188 this.message,
189 this.size = 50.0,
190 this.color,
191 }) : super(key: key);
192
193 @override
194 Widget build(BuildContext context) {
195 return Center(
196 child: Column(
197 mainAxisSize: MainAxisSize.min,
198 children: [
199 SizedBox(
200 width: size,
201 height: size,
202 child: CircularProgressIndicator(
203 valueColor: AlwaysStoppedAnimation<Color>(
204 color ?? Theme.of(context).primaryColor,
205 ),
206 ),
207 ),
208 if (message != null) ...[
209 SizedBox(height: 16),
210 Text(
211 message!,
212 style: Theme.of(context).textTheme.bodyMedium,
213 textAlign: TextAlign.center,
214 ),
215 ],
216 ],
217 ),
218 );
219 }
220}
221
222// 5. 错误状态组件
223class ErrorWidget extends StatelessWidget {
224 final String message;
225 final VoidCallback? onRetry;
226 final IconData icon;
227
228 const ErrorWidget({
229 Key? key,
230 required this.message,
231 this.onRetry,
232 this.icon = Icons.error_outline,
233 }) : super(key: key);
234
235 @override
236 Widget build(BuildContext context) {
237 return Center(
238 child: Padding(
239 padding: EdgeInsets.all(32),
240 child: Column(
241 mainAxisSize: MainAxisSize.min,
242 children: [
243 Icon(
244 icon,
245 size: 80,
246 color: Colors.red[300],
247 ),
248 SizedBox(height: 16),
249 Text(
250 '出错了',
251 style: Theme.of(context).textTheme.headlineSmall,
252 ),
253 SizedBox(height: 8),
254 Text(
255 message,
256 style: Theme.of(context).textTheme.bodyMedium,
257 textAlign: TextAlign.center,
258 ),
259 if (onRetry != null) ...[
260 SizedBox(height: 24),
261 ElevatedButton.icon(
262 onPressed: onRetry,
263 icon: Icon(Icons.refresh),
264 label: Text('重试'),
265 ),
266 ],
267 ],
268 ),
269 ),
270 );
271 }
272}
273
274// 使用示例
275class ExampleApp extends StatelessWidget {
276 @override
277 Widget build(BuildContext context) {
278 return MaterialApp(
279 title: 'StatelessWidget示例',
280 theme: ThemeData(
281 primarySwatch: Colors.blue,
282 visualDensity: VisualDensity.adaptivePlatformDensity,
283 ),
284 home: WelcomeScreen(
285 title: 'Flutter开发',
286 subtitle: '构建美丽的跨平台应用',
287 onPressed: () {
288 // 导航到下一个页面
289 },
290 ),
291 );
292 }
293}

评论