Skip to Content
Welcome to yujie-code-blogs build by Nextra 4.0 🎉
FlutterFlutter & Dart 学习指南

Flutter & Dart 学习指南

本指南旨在帮助你快速掌握 Dart 语言和 Flutter 框架的核心知识,从基础语法到实战开发,循序渐进。


一、Dart 语言基础

1. 变量

Dart 使用 varfinalconst 和显式类型来声明变量。

// 使用 var,类型自动推断为 int var age = 30; // 使用表达式 var age1 = 20 + 5; // 显式声明类型 String name = '张三'; int count = 10; double price = 19.99; bool isActive = true; // dynamic:动态类型,可以赋任意类型的值 dynamic anything = 'hello'; anything = 123; // 不会报错 // Object:所有类型的基类 Object obj = 'world'; obj = 456; // 不会报错

最佳实践:优先使用 var 让编译器推断类型;当需要明确表示变量不可变时使用 finalconst


2. 常量

// const:编译时常量,值在编译时确定 const pi = 3.14159; const greeting = 'Hello'; const combined = '$greeting, Dart!'; // 可以拼接其他 const // const 不能包含运行时才能确定的值 // const timestamp = DateTime.now(); // 错误! // final:运行时常量,只能赋值一次 final currentTime = DateTime.now(); // 正确,运行时赋值 final List<int> numbers = [1, 2, 3]; // numbers = [4, 5, 6]; // 错误:不能重新赋值 numbers.add(4); // 正确:可以修改内容(集合本身不是常量) // final 常量集合 final List<int> fixedNumbers = const [1, 2, 3]; // fixedNumbers.add(4); // 错误:集合也是不可变的
特性constfinal
赋值时机编译时运行时
重新赋值不允许不允许
集合内容可修改是(除非用 const []
内存优化相同值共享内存不共享

3. 空安全(Null Safety)

Dart 2.12+ 引入了空安全,默认所有类型不可为空。

dart中通过编译静态检查将运行时空指针提前暴露给开发者,减少线上崩溃

// 非空类型:不能为 null String name = '张三'; // name = null; // 编译错误 // 可空类型:加 ? 表示可以为 null String? nullableName; nullableName = null; // 正确 nullableName = '李四'; // 也正确 // 断言非空:! 表示我确定这里不为 null String sureName = nullableName!; // 如果 nullableName 为 null 会运行时错误 // 安全调用:?. 如果对象为 null 则返回 null int? length = nullableName?.length; // 空值合并:?? 提供默认值 String displayName = nullableName ?? '匿名用户'; // 空值合并赋值:??= String? config; config ??= 'default'; // 如果 config 为 null,赋值为 'default' // 级联操作符:.. 可以链式调用,即使对象为 null 也可用 ?.. class Person { String? name; int? age; void sayHello() => print('Hello, $name'); } var person = Person() ..name = '王五' ..age = 25 ..sayHello();
可空类型?声明可空变量String? x = null;
安全访问?.声明当前对象为null时候,跳过后续操作,返回nullx?.startWith(); x为null时候后面代码不执行
非空断言!.开发者保证变量非空,否则运行时崩溃x!.startWith(); x虽然可能为空,但是我保证在运行时不为空
空合并??左侧为null,返回右侧的值x ?? “Default” x为空,返回true,一般用于设置默认值

4.动态类型dynamic

  • Dart中,dynamic用来声明动态类型变量
  • 允许变量运行时自由改变类型,同时绕过编译时的静态检查、
void main(List<String> args) { dynamic a = 10; dynamic b = "Hello"; dynamic c = true; print(a); // Output: 10 print(b); // Output: Hello print(c); // Output: true a = "World"; }

4.1 对比var

  • Dynamic:运行时可以自由改变类型,无编译检查,方法和属性直接调用
  • var:根据初始值进行推断类型,确定类型后无法改变,有编译检查,仅限推断的属性和方法

5. 内置数据类型

5.1 String(字符串)

// 单引号和双引号都可以 String s1 = '单引号字符串'; String s2 = "双引号字符串"; // 多行字符串 String multiline = ''' 这是 多行 字符串 '''; // 字符串插值 String name = 'Dart'; int version = 3; String singer = '林俊杰'; String str = '我喜欢$singer'; String str1 = 'I like ${singer+name}' print('我正在学习 $name 版本 $version'); print('2 + 3 = ${2 + 3}'); // 表达式用 ${} // 字符串拼接 String full = 'Hello' + ' ' + 'World'; // 或使用相邻字符串 String adjacent = 'Hello' ' ' 'World'; // 等同于 'Hello World' // 常用方法 String text = ' Hello Dart '; print(text.trim()); // 'Hello Dart' print(text.toLowerCase()); // ' hello dart ' print(text.toUpperCase()); // ' HELLO DART ' print(text.contains('Dart')); // true print(text.startsWith(' H')); // true print(text.replaceAll('Dart', 'Flutter')); // ' Hello Flutter ' print(text.substring(2, 7)); // 'Hello' print(text.split(' ')); // ['', '', 'Hello', 'Dart', '', ''] // raw 字符串:不转义 String path = r'C:\Users\name\file.txt'; // 输出: C:\Users\name\file.txt

5.2 数值类型(num / int / double)

// int:整数 int age = 25; int hex = 0x1A; // 十六进制,值为 26 int big = 1000000000000000000; // Dart 支持大整数 // double:浮点数 double price = 19.99; double scientific = 1.5e3; // 1500.0 // num:int 和 double 的父类 num value = 10; // 可以是 int value = 10.5; // 也可以是 double // 常用操作 print(10 / 3); // 3.333... (double) print(10 ~/ 3); // 3 (整除,int) print(10 % 3); // 1 (取余) print(10.isEven); // true print(10.isOdd); // false print((-10).abs()); // 10 print(3.14159.toStringAsFixed(2)); // '3.14' print(int.parse('42')); // 42 print(double.parse('3.14')); // 3.14 print(42.toString()); // '42'

注意:

int age = 25; double numb = 25.1; //不能直接赋值 numb = age;//会报错 numb = age.toDouble();//这样就能赋值 age = numb;//会报错 age = numb.toInt();

5.3 布尔类型(bool)

bool isReady = true; bool isEmpty = false; // 条件判断 if (isReady) { print('准备好了'); } // Dart 不支持隐式类型转换,以下会报错 // if (1) { ... } // 错误:int 不能作为 bool 使用 // if ('hello') { ... } // 错误 // 比较运算符 print(5 > 3); // true print(5 == 5); // true print(5 != 3); // true

5.4 List(列表)

可以理解为数组

void main(List<String> args) { List<String> latters = ["a", "b", "c"]; print("letters:$latters"); latters.add("d"); print("letters:$latters"); latters.addAll(["e", "f"]); print("letters:$latters"); latters.insert(2, "g"); //在索引为2的位置插入g,原来的元素向后移动 print("letters:$latters"); latters.removeAt(2); //删除索引为2的元素 print("letters:$latters"); latters.remove("b"); //删除元素b print("letters:$latters"); latters.removeLast(); //删除最后一个元素 print("letters:$latters"); latters.removeRange(1, 3); //删除索引为1到3的元素,结束索引不包含在删除范围内 print("letters:$latters"); }
// 创建列表 var fruits = ['苹果', '香蕉', '橙子']; List<int> numbers = [1, 2, 3, 4, 5]; // 使用泛型限定类型 var emptyList = <String>[]; // 常用操作 fruits.add('葡萄'); // 尾部添加元素 fruits.addAll(['西瓜', '梨']); // 尾部添加列表所有元素 fruits.insert(0, '芒果'); // 在指定位置插入 fruits.remove('香蕉'); // 移除指定元素 fruits.removeAt(0); // 移除指定索引 print(fruits.length); // 列表长度 print(fruits.isEmpty); // 是否为空 print(fruits.contains('苹果')); // 是否包含 // 展开操作符 ... var moreFruits = ['草莓', ...fruits, '蓝莓']; // 空感知展开 ...? List<String>? nullableList; var safeList = ['a', ...?nullableList]; // 如果为 null 则忽略 // 集合条件 if/for var isVip = true; var menu = [ '首页', '商品', if (isVip) 'VIP专区', // 条件包含 '我的', ]; var nums = [for (var i = 1; i <= 5; i++) i * 2]; // [2, 4, 6, 8, 10] // 常用高阶方法 var evens = numbers.where((n) => n.isEven).toList(); // [2, 4] var doubled = numbers.map((n) => n * 2).toList(); // [2, 4, 6, 8, 10] var sum = numbers.reduce((a, b) => a + b); // 15 numbers.forEach((n) => print(n)); numbers.sort((a, b) => b.compareTo(a)); // 降序排序 // 生成固定长度列表 var fixed = List.filled(3, 'a'); // ['a', 'a', 'a'] // fixed.add('b'); // 错误:固定长度列表不能 add // 生成可增长列表 var growable = List.generate(5, (index) => index * index); // [0, 1, 4, 9, 16]

5.5 Map(映射/字典)

// 创建 Map var scores = { '张三': 90, '李四': 85, '王五': 92, }; Map<String, int> ages = {'Alice': 25, 'Bob': 30}; // 操作 scores['赵六'] = 88; // 添加/修改 print(scores['张三']); // 90 print(scores['不存在']); // null scores.remove('李四'); // 移除 print(scores.length); // 键值对数量 print(scores.keys); // 所有键 print(scores.values); // 所有值 print(scores.containsKey('王五')); // true print(scores.containsValue(100)); // false // 遍历 scores.forEach((name, score) { print('$name: $score'); }); for (var entry in scores.entries) { print('${entry.key} = ${entry.value}'); } // 使用 putIfAbsent 实现缓存 var cache = <String, String>{}; var value = cache.putIfAbsent('key', () => 'expensive computation'); // 使用 map 方法转换 var greetings = scores.map((name, score) { return MapEntry(name, '$name 得了 $score 分'); });

5.6 Set(集合)

// Set:无序且不重复的元素集合 var uniqueNumbers = {1, 2, 3, 3, 3}; // {1, 2, 3} Set<String> tags = {'flutter', 'dart', 'mobile'}; // 操作 tags.add('web'); tags.add('flutter'); // 重复,不会添加 tags.remove('dart'); print(tags.length); print(tags.contains('flutter')); // true // 集合运算 var a = {1, 2, 3}; var b = {2, 3, 4}; print(a.union(b)); // {1, 2, 3, 4} print(a.intersection(b)); // {2, 3} print(a.difference(b)); // {1} // List 去转 Set var unique = [1, 2, 2, 3, 3, 3].toSet().toList(); // [1, 2, 3]

5.7 Record(记录,Dart 3.0+)

// Record:轻量级的匿名不可变数据结构 var record = ('Hello', 42, true); print(record.$1); // 'Hello' print(record.$2); // 42 print(record.$3); // true // 命名字段 var user = (name: '张三', age: 25, isVip: true); print(user.name); // '张三' print(user.age); // 25 // 作为函数返回值(替代只能返回一个值的限制) (String, int) getUserInfo() { return ('李四', 30); } var (userName, userAge) = getUserInfo(); // 解构赋值 print('$userName 年龄 $userAge'); // 列表解构 var [first, second, ...rest] = [1, 2, 3, 4, 5]; // Map 解构 var {'name': n, 'age': a} = {'name': '王五', 'age': 28};

6. 函数

6.1 基础函数

// 完整写法 int add(int a, int b) { return a + b; } // 箭头函数(单行表达式) int subtract(int a, int b) => a - b; // 无返回值 void greet(String name) { print('你好, $name!'); } // 可选位置参数:用 [] 包裹 String formatDate(int year, int month, [int? day]) { if (day != null) { return '$year-$month-$day'; } return '$year-$month'; } formatDate(2024, 5); // '2024-5' formatDate(2024, 5, 27); // '2024-5-27' // 命名参数:更推荐使用,代码可读性更好 void createUser({ required String name, required int age, String? email, bool isActive = true, // 提供默认值 }) { print('创建用户: $name, 年龄: $age, 邮箱: $email, 激活: $isActive'); } createUser(name: '张三', age: 25); createUser(name: '李四', age: 30, email: 'li@example.com', isActive: false); // 位置 + 命名参数混合 void paint(String color, {double? opacity, int thickness = 1}) { // ... }

6.2 函数作为一等公民

// 函数可以赋值给变量 var multiply = (int a, int b) => a * b; print(multiply(3, 4)); // 12 // 函数可以作为参数 void execute(int a, int b, int Function(int, int) operation) { print(operation(a, b)); } execute(10, 5, add); // 15 execute(10, 5, (a, b) => a * b); // 50 // 函数可以作为返回值 Function makeMultiplier(int factor) { return (int value) => value * factor; } var triple = makeMultiplier(3); print(triple(5)); // 15 // 闭包 Function makeCounter() { int count = 0; return () => ++count; // 捕获外部变量 } var counter = makeCounter(); print(counter()); // 1 print(counter()); // 2 print(counter()); // 3

6.3 异步函数

// async/await:处理异步操作 Future<String> fetchUserData() async { // 模拟网络延迟 await Future.delayed(Duration(seconds: 2)); return '用户数据加载完成'; } // 使用 void main() async { print('开始加载...'); var data = await fetchUserData(); print(data); // 2秒后输出 } // Future.then 写法(不使用 await) fetchUserData().then((data) { print(data); }).catchError((error) { print('出错了: $error'); }); // 并行执行多个 Future Future<void> loadMultipleData() async { var futures = [ fetchUserData(), fetchUserData(), fetchUserData(), ]; var results = await Future.wait(futures); // 等待全部完成 print(results); // ['用户数据加载完成', '用户数据加载完成', '用户数据加载完成'] } // Stream:处理连续的异步事件 Stream<int> countStream(int max) async* { for (int i = 1; i <= max; i++) { await Future.delayed(Duration(milliseconds: 500)); yield i; // 产生一个值 } } // 监听 Stream void main() async { await for (var count in countStream(5)) { print(count); // 每隔 500ms 输出 1, 2, 3, 4, 5 } }

6.4 必传参数&可传参数

必传
void main(){ add(1,2); } int add(int a,int b){ return a+b; }
可传
可选位置参数
  • 必须在必传参数后面,采用中括号包裹
  • 语法:函数名(String a,[String? b,…]) 传递时候按照顺序传递
void main() { print(combine('Hello', 'World')); print(combine('Hello')); } String combine(String a, [String? b]) { return a + (b ?? ''); }
可选命名参数
void main() { // 可选命名参数 print(combine2("张三"));//{name: 张三, sex: 男, age: 0} print(combine2("李四", sex: "男", age: 18));//{name: 李四, sex: 男, age: 18} } Map<String, dynamic> combine2(String name, {String? sex = "男", int? age = 0}) { return {"name": name, "sex": sex, "age": age}; }

6.5 匿名函数

Function test = (){};//后面的是匿名函数,然后将匿名函数赋值给test这个变量

6.6 箭头函数

()=> a+b;// 匿名箭头函数 int add(int a,int b)=> a+b;//箭头函数

7. 面向对象编程

7.1 类与对象

class Person { // 实例变量(属性) String name; int age; // 构造方法 Person(this.name, this.age); // 简写形式 // 命名构造方法 Person.guest() : name = '游客', age = 0; // 工厂构造方法 factory Person.fromJson(Map<String, dynamic> json) { return Person(json['name'], json['age']); } // 方法 void sayHello() { print('你好,我是 $name,今年 $age 岁'); } // Getter 和 Setter String get info => '$name ($age岁)'; set birthday(int year) => age = DateTime.now().year - year; // 重写 toString @override String toString() => 'Person(name: $name, age: $age)'; } // 使用 void main() { var person = Person('张三', 25); person.sayHello(); print(person.info); // 张三 (25岁) var guest = Person.guest(); guest.sayHello(); // 你好,我是 游客,今年 0 岁 var fromJson = Person.fromJson({'name': '李四', 'age': 30}); print(fromJson); // Person(name: 李四, age: 30) }

7.2 继承

class Animal { String name; Animal(this.name); void speak() => print('$name 发出声音'); } class Dog extends Animal { String breed; // 调用父类构造方法 Dog(String name, this.breed) : super(name); @override void speak() => print('$name 汪汪叫'); void fetch() => print('$name 去捡球了'); } var dog = Dog('旺财', '金毛'); dog.speak(); // 旺财 汪汪叫 dog.fetch(); // 旺财 去捡球了

7.3 抽象类与接口

// 抽象类:不能直接实例化 abstract class Shape { double get area; // 抽象方法(没有实现) void describe(); // 抽象方法 // 普通方法也可以有实现 void printArea() { print('面积: $area'); } } // Dart 中没有 interface 关键字,类本身就是接口 // 使用 implements 实现接口(必须实现所有成员) class Circle implements Shape { double radius; Circle(this.radius); @override double get area => 3.14 * radius * radius; @override void describe() => print('这是一个圆,半径 $radius'); @override void printArea() => print('圆面积: $area'); } // 使用 extends 继承抽象类(只需实现抽象方法) class Rectangle extends Shape { double width, height; Rectangle(this.width, this.height); @override double get area => width * height; @override void describe() => print('矩形 ${width}x$height'); } var circle = Circle(5); circle.describe(); circle.printArea(); // 圆面积: 78.5

7.4 Mixin(混入)

在不使用传统继承的情况下,向类中添加新功能

  • 使用mixin定义一个新对象

  • 使用with关键字将定义的对象**混入**到当前对象

  • 一个类支持with多个mixin,调用优先级遵循**后来居上,栈的原则,即先混入会后混入覆盖**

// Mixin:在多个类层次结构中复用代码 mixin Flyable { void fly() => print('我在飞!'); } mixin Swimmable { void swim() => print('我在游泳!'); } // 使用 with 关键字混入 class Duck extends Animal with Flyable, Swimmable { Duck(String name) : super(name); } var duck = Duck('唐老鸭'); duck.speak(); // 唐老鸭 发出声音 duck.fly(); // 我在飞! duck.swim(); // 我在游泳! // 限制 Mixin 的使用范围 mixin Runnable on Animal { void run() => print('$name 在奔跑'); }

7.5 扩展方法(Extension)

// 给现有类型添加新方法 extension StringExtension on String { String get capitalize { if (isEmpty) return this; return '${this[0].toUpperCase()}${substring(1)}'; } String repeat(int times) { return List.filled(times, this).join(); } } // 使用 print('hello'.capitalize); // 'Hello' print('abc '.repeat(3)); // 'abc abc abc ' // 扩展 int extension IntExtension on int { Duration get seconds => Duration(seconds: this); Duration get minutes => Duration(minutes: this); } print(5.seconds); // Duration 0:00:05 print(10.minutes); // Duration 0:10:00

7.6 多态

  • Dart中的类是指同一个错做作用于不同对象,可以产生不同效果

实现:

  • 继承
  • 抽象类与接口

8. 枚举与模式匹配

// 增强枚举(Dart 2.17+) enum Status { pending('待处理', Icons.hourglass_empty), processing('处理中', Icons.sync), completed('已完成', Icons.check_circle), failed('失败', Icons.error); final String label; final IconData icon; const Status(this.label, this.icon); bool get isDone => this == Status.completed; } // 使用 var status = Status.processing; print(status.label); // '处理中' print(status.isDone); // false // 模式匹配(Dart 3.0+) String describe(Status status) { return switch (status) { Status.pending => '请耐心等待', Status.processing => '正在努力处理中', Status.completed => '已经完成啦!', Status.failed => '出错了,请重试', }; } // 密封类(Sealed Class)用于穷尽性检查 sealed class Result<T> {} class Success<T> extends Result<T> { final T data; Success(this.data); } class Error<T> extends Result<T> { final String message; Error(this.message); } // switch 会强制处理所有子类型 String handleResult(Result<int> result) { return switch (result) { Success(data: var d) => '成功: $d', Error(message: var m) => '错误: $m', }; }

9. 泛型

  • Dart允许使用类型参数,限定类型的同时又让类型更加灵活,让代码更加简装和维护性更强
// 泛型类 class Box<T> { T value; Box(this.value); T getValue() => value; } var intBox = Box<int>(42); var stringBox = Box<String>('hello'); // 泛型方法 T first<T>(List<T> list) => list.first; // 泛型约束 class ComparableBox<T extends Comparable<T>> { T a, b; ComparableBox(this.a, this.b); T get max => a.compareTo(b) > 0 ? a : b; } var numBox = ComparableBox<int>(10, 20); print(numBox.max); // 20 // 常用泛型类型别名 typealias JsonMap = Map<String, dynamic>;

10. 异常处理

// 抛出异常 void checkAge(int age) { if (age < 0) { throw ArgumentError('年龄不能为负数'); } if (age < 18) { throw Exception('未成年人禁止访问'); } } // 捕获异常 try { checkAge(15); } on ArgumentError catch (e) { print('参数错误: ${e.message}'); } on Exception catch (e) { print('其他异常: $e'); } catch (e, stackTrace) { // 捕获所有异常,同时获取堆栈 print('异常: $e'); print('堆栈: $stackTrace'); } finally { // 无论是否异常都会执行 print('清理工作'); } // 自定义异常 class NetworkException implements Exception { final int statusCode; final String message; NetworkException(this.statusCode, this.message); @override String toString() => 'NetworkException($statusCode): $message'; }

11. 运算符

11.1 算数运算符

运算符作用
+
-
*
/
~/整除
%取余数

11.2 赋值运算符

= /= += -= */

11.3 逻辑运算符

&&
||

参与变量必须为bool

11.4 比较运算符

==是否相等
!=是否不等
>大于
>=大于等于
<小于
<=小于等于

12. 循环

12.1 for

void main(List<String> args) { for (int i = 0; i < 5; i++) { print(i); } }

12.2 while

void main(){ var page = [1,2,3,4,5]; int index = 0; while(index < 5){ print(page[index]); } }

13. 异步编程

Dart是单线程语言,所以异步编程很有必要

  • 单线程+事件循环机制完成耗时任务处理

13.1 事件循环

同步->微任务队列->事件队列->结束

  • 和浏览器的事件循环机制很相似

微任务队列:Future.microtask()

事件队列:Future、Future.delay()、I/O操作(文件、网络)

13.2 Future

Uncompleted(等待)Pending
Completed with a valueFulfilled
Completed with a errorRejected
Future((){ }).then((){ throw Exception })

二、Flutter 框架基础

1. Widget 核心概念

Widget 是 Flutter 中一切的基础,是描述 UI 配置的不变对象。

// StatelessWidget:无状态,UI 只依赖传入的配置 class MyText extends StatelessWidget { final String content; // 使用 key 帮助 Flutter 识别 widget const MyText(this.content, {super.key}); @override Widget build(BuildContext context) { // build 方法描述 UI 结构 return Text(content); } } // StatefulWidget:有状态,UI 可以随时间变化 class Counter extends StatefulWidget { const Counter({super.key}); @override State<Counter> createState() => _CounterState(); } class _CounterState extends State<Counter> { int _count = 0; void _increment() { // setState 通知 Flutter 状态变化,需要重建 UI setState(() { _count++; }); } @override Widget build(BuildContext context) { return ElevatedButton( onPressed: _increment, child: Text('点击次数: $_count'), ); } }

选择原则

  • 如果 Widget 不需要变化 → 用 StatelessWidget
  • 如果 Widget 需要响应交互或数据变化 → 用 StatefulWidget

2. 基础 Widget 速查

2.1 文本与样式

Text( 'Hello Flutter', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Colors.blue, letterSpacing: 1.2, height: 1.5, ), textAlign: TextAlign.center, maxLines: 2, overflow: TextOverflow.ellipsis, ) // 富文本 RichText( text: TextSpan( style: TextStyle(color: Colors.black, fontSize: 16), children: [ TextSpan(text: '同意 '), TextSpan( text:《用户协议》, style: TextStyle(color: Colors.blue), recognizer: TapGestureRecognizer()..onTap = () => openUrl(), ), ], ), )

2.2 容器与装饰

Container( width: 200, height: 100, // 内边距 padding: EdgeInsets.all(16), // 外边距 margin: EdgeInsets.symmetric(horizontal: 20, vertical: 10), // 装饰 decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey.shade300), boxShadow: [ BoxShadow( color: Colors.black12, blurRadius: 8, offset: Offset(0, 4), ), ], gradient: LinearGradient( colors: [Colors.blue, Colors.purple], ), ), // 子元素对齐 alignment: Alignment.center, child: Text('内容'), ) // 快捷方式 Padding( padding: EdgeInsets.all(16), child: Text('有内边距的文本'), ) Center(child: Text('居中')) Align( alignment: Alignment.bottomRight, child: Text('右下角'), ) // SizedBox 用于固定尺寸或留白 SizedBox(width: 20, height: 20) // 固定尺寸 SizedBox.shrink() // 尽可能小 SizedBox.expand() // 尽可能大

2.3 图片

// 本地图片(需要在 pubspec.yaml 中配置 assets) Image.asset('assets/images/logo.png') // 网络图片 Image.network( 'https://example.com/image.jpg', width: 200, height: 200, fit: BoxFit.cover, // 裁剪填充 loadingBuilder: (context, child, progress) { if (progress == null) return child; return CircularProgressIndicator(); }, errorBuilder: (context, error, stack) { return Icon(Icons.error); }, ) // 圆形头像 CircleAvatar( radius: 40, backgroundImage: NetworkImage('https://example.com/avatar.jpg'), child: Text('AB'), // 作为 fallback ) // Icon Icon(Icons.favorite, color: Colors.red, size: 32) Icon(Icons.settings, color: Theme.of(context).primaryColor)

2.4 按钮

// 填充按钮 ElevatedButton( onPressed: () => print('点击'), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, foregroundColor: Colors.white, padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), child: Text('确认'), ) // 文本按钮 TextButton( onPressed: () {}, child: Text('取消'), ) // 轮廓按钮 OutlinedButton( onPressed: () {}, child: Text('选择'), ) // 图标按钮 IconButton( onPressed: () {}, icon: Icon(Icons.delete), tooltip: '删除', ) // 带图标的按钮 ElevatedButton.icon( onPressed: () {}, icon: Icon(Icons.add), label: Text('新增'), ) // Floating Action Button FloatingActionButton( onPressed: () {}, child: Icon(Icons.add), )

2.5 输入框

TextField( controller: _textController, // TextEditingController decoration: InputDecoration( labelText: '用户名', hintText: '请输入用户名', prefixIcon: Icon(Icons.person), suffixIcon: IconButton( icon: Icon(Icons.clear), onPressed: () => _textController.clear(), ), border: OutlineInputBorder(), filled: true, fillColor: Colors.grey.shade100, ), keyboardType: TextInputType.emailAddress, obscureText: false, // true 时隐藏输入(密码) maxLength: 20, onChanged: (value) => print('输入: $value'), onSubmitted: (value) => print('提交: $value'), ) // 表单中使用 TextFormField( validator: (value) { if (value == null || value.isEmpty) return '不能为空'; if (value.length < 6) return '至少6个字符'; return null; // 验证通过 }, ) // Controller 使用 class _MyFormState extends State<MyForm> { final _controller = TextEditingController(); @override void dispose() { _controller.dispose(); // 记得释放! super.dispose(); } }

3. 布局 Widget

3.1 线性布局(Row / Column)

// Row:水平排列 Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, // 主轴对齐 crossAxisAlignment: CrossAxisAlignment.center, // 交叉轴对齐 children: [ Icon(Icons.home), Text('首页'), Spacer(), // 占据剩余空间 Icon(Icons.arrow_forward), ], ) // Column:垂直排列 Column( mainAxisSize: MainAxisSize.min, // 最小高度 children: [ Text('标题', style: TextStyle(fontSize: 20)), SizedBox(height: 8), // 间距 Text('描述内容'), ], ) // MainAxisAlignment 选项: // start(默认)、end、center、spaceBetween、spaceAround、spaceEvenly // CrossAxisAlignment 选项: // start、end、center、stretch、baseline

3.2 弹性布局(Expanded / Flexible)

Row( children: [ // Expanded:占据所有剩余空间 Expanded( child: Container(color: Colors.red), ), // Flexible:按 flex 比例分配 Flexible( flex: 2, // 占 2 份 child: Container(color: Colors.green), ), Flexible( flex: 1, // 占 1 份 child: Container(color: Colors.blue), ), ], ) // 红色占剩余空间,绿色和蓝色按 2:1 分配

3.3 层叠布局(Stack)

Stack( alignment: Alignment.center, children: [ // 底层 Image.network('https://example.com/bg.jpg'), // 上层 Positioned( // 绝对定位 bottom: 16, left: 16, child: Text('底部文字', style: TextStyle(color: Colors.white)), ), Positioned.fill( // 填满整个 Stack child: Container( color: Colors.black.withOpacity(0.3), ), ), ], )

3.4 滚动布局

// ListView:列表滚动 ListView( padding: EdgeInsets.all(16), children: [ ListTile(title: Text('项目 1')), ListTile(title: Text('项目 2')), ListTile(title: Text('项目 3')), ], ) // 长列表优化:builder 模式只构建可见项 ListView.builder( itemCount: 1000, itemBuilder: (context, index) { return ListTile( title: Text('项目 $index'), subtitle: Text('描述'), leading: Icon(Icons.folder), trailing: Icon(Icons.chevron_right), onTap: () => print('点击 $index'), ); }, ) // 分隔线列表 ListView.separated( itemCount: 20, itemBuilder: (context, index) => ListTile(title: Text('Item $index')), separatorBuilder: (context, index) => Divider(height: 1), ) // 网格 GridView.count( crossAxisCount: 2, // 两列 crossAxisSpacing: 10, // 列间距 mainAxisSpacing: 10, // 行间距 children: List.generate(6, (i) => Container(color: Colors.blue)), ) // 网格 builder(适合大量数据) GridView.builder( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, childAspectRatio: 1, // 宽高比 ), itemCount: 100, itemBuilder: (context, index) => Card(child: Text('$index')), ) // 自定义滚动 + 吸顶 CustomScrollView( slivers: [ SliverAppBar( title: Text('可折叠标题'), floating: true, expandedHeight: 200, flexibleSpace: FlexibleSpaceBar( background: Image.asset('assets/banner.jpg', fit: BoxFit.cover), ), ), SliverToBoxAdapter(child: Text('普通内容')), SliverList( delegate: SliverChildBuilderDelegate( (context, index) => ListTile(title: Text('Item $index')), childCount: 50, ), ), ], ) // 下拉刷新 + 上拉加载 RefreshIndicator( onRefresh: () async => await loadData(), child: ListView(...), )

3.5 其他常用布局

// Wrap:自动换行 Wrap( spacing: 8, // 水平间距 runSpacing: 8, // 垂直间距 children: [ Chip(label: Text('标签1')), Chip(label: Text('标签2')), Chip(label: Text('标签3')), // ... 超出宽度自动换行 ], ) // 比例布局 AspectRatio( aspectRatio: 16 / 9, child: Container(color: Colors.blue), ) // FractionallySizedBox:百分比尺寸 FractionallySizedBox( widthFactor: 0.8, // 80% 宽度 child: ElevatedButton(onPressed: () {}, child: Text('宽按钮')), ) // LayoutBuilder:根据父容器尺寸自适应 LayoutBuilder( builder: (context, constraints) { if (constraints.maxWidth > 600) { return Row(children: [...]); // 宽屏布局 } return Column(children: [...]); // 窄屏布局 }, ) // OrientationBuilder:根据屏幕方向调整 OrientationBuilder( builder: (context, orientation) { return GridView.count( crossAxisCount: orientation == Orientation.portrait ? 2 : 4, ); }, )

4. 导航与路由

4.1 基础导航

// 页面跳转 Navigator.push( context, MaterialPageRoute(builder: (context) => SecondPage()), ); // 带返回值的跳转 final result = await Navigator.push( context, MaterialPageRoute(builder: (context) => SelectionPage()), ); print('返回结果: $result'); // 返回上一页 Navigator.pop(context); // 返回并传值 Navigator.pop(context, '选中的数据'); // 替换当前页面(无法返回) Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => HomePage()), ); // 清空栈并跳转到新页面 Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => LoginPage()), (route) => false, // 保留判断为 true 的页面 );

4.2 命名路由

// main.dart 中配置 MaterialApp( initialRoute: '/', routes: { '/': (context) => HomePage(), '/detail': (context) => DetailPage(), '/settings': (context) => SettingsPage(), }, // 动态路由传参 onGenerateRoute: (settings) { if (settings.name == '/profile') { final args = settings.arguments as Map<String, dynamic>; return MaterialPageRoute( builder: (context) => ProfilePage(userId: args['userId']), ); } return null; }, ) // 跳转 Navigator.pushNamed(context, '/detail'); Navigator.pushNamed(context, '/profile', arguments: {'userId': '123'});

4.3 推荐:使用 go_router

# pubspec.yaml dependencies: go_router: ^13.0.0
// router.dart import 'package:go_router/go_router.dart'; final router = GoRouter( initialLocation: '/', routes: [ GoRoute( path: '/', builder: (context, state) => HomePage(), ), GoRoute( path: '/detail/:id', // 路径参数 builder: (context, state) { final id = state.pathParameters['id']!; return DetailPage(id: id); }, ), GoRoute( path: '/profile', builder: (context, state) { final extra = state.extra as Map<String, dynamic>?; return ProfilePage(data: extra); }, ), ], ); // main.dart MaterialApp.router( routerConfig: router, ) // 跳转 textButton( onPressed: () => context.go('/'), // 直接跳转,替换当前 child: Text('首页'), ) textButton( onPressed: () => context.push('/detail/123'), // 压栈,可返回 child: Text('详情'), ) textButton( onPressed: () => context.pop(), // 返回 child: Text('返回'), )

5. 状态管理

5.1 基础方案:setState

适合简单的局部状态:

class LikeButton extends StatefulWidget { const LikeButton({super.key}); @override State<LikeButton> createState() => _LikeButtonState(); } class _LikeButtonState extends State<LikeButton> { bool _isLiked = false; @override Widget build(BuildContext context) { return IconButton( icon: Icon(_isLiked ? Icons.favorite : Icons.favorite_border), color: _isLiked ? Colors.red : null, onPressed: () => setState(() => _isLiked = !_isLiked), ); } }

5.2 推荐:Riverpod

# pubspec.yaml dependencies: flutter_riverpod: ^2.4.0
// main.dart void main() { runApp(ProviderScope(child: MyApp())); // 用 ProviderScope 包裹 } // 定义 Provider // counter_provider.dart import 'package:flutter_riverpod/flutter_riverpod.dart'; // StateProvider:简单状态 final counterProvider = StateProvider<int>((ref) => 0); // StateNotifierProvider:复杂状态逻辑 final todoListProvider = StateNotifierProvider<TodoListNotifier, List<Todo>>((ref) { return TodoListNotifier(); }); class TodoListNotifier extends StateNotifier<List<Todo>> { TodoListNotifier() : super([]); void addTodo(String title) { state = [...state, Todo(id: DateTime.now().toString(), title: title)]; } void toggleTodo(String id) { state = state.map((todo) { if (todo.id == id) return todo.copyWith(isDone: !todo.isDone); return todo; }).toList(); } void removeTodo(String id) { state = state.where((todo) => todo.id != id).toList(); } } // 使用 ConsumerWidget class CounterPage extends ConsumerWidget { const CounterPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final count = ref.watch(counterProvider); return Scaffold( appBar: AppBar(title: Text('计数器')), body: Center(child: Text('$count', style: TextStyle(fontSize: 48))), floatingActionButton: FloatingActionButton( onPressed: () => ref.read(counterProvider.notifier).state++, child: Icon(Icons.add), ), ); } } // 局部监听使用 Consumer class SomeWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Consumer( builder: (context, ref, child) { final count = ref.watch(counterProvider); return Text('$count'); }, ); } } // AsyncValue:处理异步状态 final userProvider = FutureProvider<User>((ref) async { final response = await dio.get('/user'); return User.fromJson(response.data); }); // 使用 class UserProfile extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final userAsync = ref.watch(userProvider); return userAsync.when( data: (user) => Text(user.name), loading: () => CircularProgressIndicator(), error: (err, stack) => Text('错误: $err'), ); } }

5.3 Bloc/Cubit 模式

适合大型项目:

# pubspec.yaml dependencies: flutter_bloc: ^8.1.0
// State typedef CounterState = int; // Cubit class CounterCubit extends Cubit<CounterState> { CounterCubit() : super(0); void increment() => emit(state + 1); void decrement() => emit(state - 1); } // 使用 class CounterPage extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( create: (_) => CounterCubit(), child: CounterView(), ); } } class CounterView extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: BlocBuilder<CounterCubit, CounterState>( builder: (context, count) { return Center(child: Text('$count')); }, ), floatingActionButton: FloatingActionButton( onPressed: () => context.read<CounterCubit>().increment(), child: Icon(Icons.add), ), ); } }

状态管理选择指南

方案适用场景复杂度
setState单个 Widget 的简单状态
ValueNotifier局部共享状态,不需要重建大量 Widget
Riverpod中大型应用,异步数据,依赖注入
Bloc/Cubit大型应用,严格的分层架构,复杂业务逻辑中高
GetX快速原型,但生产环境慎用

6. 网络请求

6.1 使用 Dio(推荐)

# pubspec.yaml dependencies: dio: ^5.4.0
import 'package:dio/dio.dart'; class ApiService { late final Dio _dio; ApiService() { _dio = Dio(BaseOptions( baseUrl: 'https://api.example.com', connectTimeout: Duration(seconds: 10), receiveTimeout: Duration(seconds: 10), headers: {'Content-Type': 'application/json'}, )); // 添加拦截器 _dio.interceptors.add(InterceptorsWrapper( onRequest: (options, handler) { // 添加认证 Token options.headers['Authorization'] = 'Bearer $token'; print('请求: ${options.method} ${options.path}'); handler.next(options); }, onResponse: (response, handler) { print('响应: ${response.statusCode}'); handler.next(response); }, onError: (error, handler) { print('错误: ${error.message}'); handler.next(error); }, )); } Future<List<User>> getUsers() async { final response = await _dio.get('/users'); return (response.data as List) .map((json) => User.fromJson(json)) .toList(); } Future<User> getUser(String id) async { final response = await _dio.get('/users/$id'); return User.fromJson(response.data); } Future<User> createUser(CreateUserDto dto) async { final response = await _dio.post('/users', data: dto.toJson()); return User.fromJson(response.data); } } // 数据模型 class User { final String id; final String name; final String email; User({required this.id, required this.name, required this.email}); factory User.fromJson(Map<String, dynamic> json) => User( id: json['id'], name: json['name'], email: json['email'], ); Map<String, dynamic> toJson() => { 'id': id, 'name': name, 'email': email, }; }

6.2 使用 http 包(轻量)

dependencies: http: ^1.1.0
import 'package:http/http.dart' as http; import 'dart:convert'; Future<void> fetchData() async { final response = await http.get(Uri.parse('https://api.example.com/data')); if (response.statusCode == 200) { final data = jsonDecode(response.body); print(data); } else { throw Exception('请求失败: ${response.statusCode}'); } } // POST 请求 Future<void> postData() async { final response = await http.post( Uri.parse('https://api.example.com/data'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'name': '张三', 'age': 25}), ); }

7. 本地存储

# pubspec.yaml dependencies: shared_preferences: ^2.2.0 # 简单键值存储 sqflite: ^2.3.0 # SQLite 数据库 hive: ^2.2.0 # 高性能本地存储 path_provider: ^2.1.0 # 获取文件路径

7.1 SharedPreferences

import 'package:shared_preferences/shared_preferences.dart'; class LocalStorage { static late SharedPreferences _prefs; static Future<void> init() async { _prefs = await SharedPreferences.getInstance(); } static Future<void> setString(String key, String value) async { await _prefs.setString(key, value); } static String? getString(String key) => _prefs.getString(key); static Future<void> setBool(String key, bool value) async { await _prefs.setBool(key, value); } static Future<void> remove(String key) async { await _prefs.remove(key); } } // 使用 await LocalStorage.setString('token', 'abc123'); final token = LocalStorage.getString('token');

7.2 Hive

import 'package:hive_flutter/hive_flutter.dart'; // 初始化 await Hive.initFlutter(); // 打开 Box(类似于表) var box = await Hive.openBox('settings'); // 存储 await box.put('theme', 'dark'); await box.put('user', {'name': '张三', 'age': 25}); // 读取 var theme = box.get('theme', defaultValue: 'light'); var user = box.get('user'); // 监听变化 box.watch().listen((event) { print('Key ${event.key} changed'); });

8. 主题与国际化

8.1 主题配置

MaterialApp( theme: ThemeData( useMaterial3: true, colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), textTheme: TextTheme( headlineLarge: TextStyle(fontSize: 32, fontWeight: FontWeight.bold), bodyLarge: TextStyle(fontSize: 16), ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), ), ), darkTheme: ThemeData.dark(useMaterial3: true), themeMode: ThemeMode.system, // 跟随系统 ) // 在 Widget 中使用主题 Text('标题', style: Theme.of(context).textTheme.headlineLarge) Container(color: Theme.of(context).colorScheme.primary) Icon(Icons.star, color: Theme.of(context).colorScheme.secondary)

8.2 国际化

# pubspec.yaml dependencies: flutter_localizations: sdk: flutter intl: ^0.19.0
MaterialApp( localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: [ Locale('zh', 'CN'), Locale('en', 'US'), ], ) // 获取当前语言 Locale myLocale = Localizations.localeOf(context);

9. 常用功能代码片段

9.1 对话框

// 确认对话框 Future<bool?> showConfirmDialog(BuildContext context) async { return showDialog<bool>( context: context, builder: (context) => AlertDialog( title: Text('确认删除'), content: Text('此操作不可撤销,确定要删除吗?'), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: Text('取消'), ), TextButton( onPressed: () => Navigator.pop(context, true), child: Text('删除', style: TextStyle(color: Colors.red)), ), ], ), ); } // 底部弹出 showModalBottomSheet( context: context, builder: (context) => Container( height: 300, child: Center(child: Text('底部内容')), ), ); // SnackBar ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('操作成功'), duration: Duration(seconds: 2), action: SnackBarAction( label: '撤销', onPressed: () {}, ), ), );

9.2 图片选择

dependencies: image_picker: ^1.0.0
import 'package:image_picker/image_picker.dart'; final picker = ImagePicker(); // 拍照 final photo = await picker.pickImage(source: ImageSource.camera); // 从相册选择 final image = await picker.pickImage( source: ImageSource.gallery, maxWidth: 1024, maxHeight: 1024, imageQuality: 85, ); // 获取路径 if (image != null) { print(image.path); }

9.3 动画

// 隐式动画(自动过渡) AnimatedContainer( duration: Duration(milliseconds: 300), width: _isExpanded ? 200 : 100, height: _isExpanded ? 200 : 100, color: _isExpanded ? Colors.red : Colors.blue, curve: Curves.easeInOut, child: ..., ) AnimatedOpacity( duration: Duration(milliseconds: 500), opacity: _visible ? 1.0 : 0.0, child: Text('渐隐渐现'), ) // Hero 动画(页面间共享元素) // 页面 A Hero( tag: 'image-1', // 唯一标识 child: Image.asset('photo.jpg'), ) // 页面 B Hero( tag: 'image-1', // 相同标识 child: Image.asset('photo.jpg'), ) // 显式动画控制器 class FadeIn extends StatefulWidget { final Widget child; const FadeIn({super.key, required this.child}); @override State<FadeIn> createState() => _FadeInState(); } class _FadeInState extends State<FadeIn> with SingleTickerProviderStateMixin { late final AnimationController _controller; late final Animation<double> _animation; @override void initState() { super.initState(); _controller = AnimationController( duration: Duration(milliseconds: 500), vsync: this, ); _animation = CurvedAnimation( parent: _controller, curve: Curves.easeIn, ); _controller.forward(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return FadeTransition( opacity: _animation, child: widget.child, ); } }

9.4 平台通道(调用原生代码)

import 'package:flutter/services.dart'; class PlatformChannel { static const platform = MethodChannel('com.example.app/channel'); // 调用原生方法 static Future<String> getBatteryLevel() async { try { final result = await platform.invokeMethod('getBatteryLevel'); return '电量: $result%'; } on PlatformException catch (e) { return '获取失败: ${e.message}'; } } // 接收原生调用 static void setupHandler() { platform.setMethodCallHandler((call) async { if (call.method == 'fromNative') { print('收到原生消息: ${call.arguments}'); } return null; }); } }

10. 项目结构与开发规范

10.1 推荐目录结构

lib/ ├── main.dart # 入口文件 ├── app.dart # MaterialApp 配置 ├── router.dart # 路由配置 ├── core/ # 核心共享代码 │ ├── constants/ # 常量定义 │ │ ├── api_constants.dart │ │ ├── app_colors.dart │ │ └── app_strings.dart │ ├── theme/ # 主题配置 │ │ └── app_theme.dart │ ├── utils/ # 工具类 │ │ ├── date_utils.dart │ │ └── validators.dart │ └── extensions/ # 扩展方法 │ └── context_extension.dart ├── data/ # 数据层 │ ├── models/ # 数据模型 │ │ ├── user.dart │ │ └── user.g.dart # json_serializable 生成 │ ├── repositories/ # 仓库 │ │ └── user_repository.dart │ └── datasources/ # 数据源 │ ├── remote/ # 远程 │ │ └── api_client.dart │ └── local/ # 本地 │ └── local_storage.dart ├── domain/ # 业务层 │ ├── entities/ # 实体(业务对象) │ └── usecases/ # 用例 ├── presentation/ # 表现层(UI) │ ├── screens/ # 页面 │ │ ├── home_screen.dart │ │ └── profile_screen.dart │ ├── widgets/ # 可复用组件 │ │ ├── loading_indicator.dart │ │ └── error_widget.dart │ └── providers/ # 状态管理 │ └── auth_provider.dart └── services/ # 第三方服务 └── push_notification_service.dart test/ # 测试 ├── unit/ ├── widget/ └── integration/

10.2 开发规范

// 命名规范 // 文件/目录:lower_snake_case // 类/枚举:PascalCase // 变量/函数:camelCase // 常量:camelCase 或 lowerCamelCase // 1. 总是使用 const 构造如果可能 const SizedBox(height: 16) // 好 SizedBox(height: 16) // 避免 // 2. Widget 方法不要太长,抽离小组件 // 不好 Widget build(BuildContext context) { return Column( children: [ Container(...50 行代码...), Container(...50 行代码...), ], ); } // 好 Widget build(BuildContext context) { return Column( children: [ HeaderSection(), ContentSection(), ], ); } // 3. 使用 trailing comma 让格式化更美观 Widget build() { return Container( width: 100, height: 100, color: Colors.red, ); // 有逗号,每个属性一行 } // 4. 字符串使用单引号 String text = 'Hello'; // 好 String text2 = "Hello"; // 避免(除非字符串内有单引号) // 5. 使用 super parameters class MyWidget extends StatelessWidget { const MyWidget({super.key}); // Dart 2.17+ } // 6. 总是处理 async 异常 Future<void> fetchData() async { try { final data = await api.getData(); } catch (e) { // 处理错误 } } // 7. BuildContext 异步使用时检查 mounted if (mounted) { Navigator.pop(context); }

三、Flutter 开发速查表

常用命令

# 创建项目 flutter create my_app cd my_app # 运行 flutter run # 运行到连接的设备 flutter run -d emulator-5554 # 指定设备 flutter run --release # 发布模式运行 # 依赖管理 flutter pub get # 安装依赖 flutter pub add dio # 添加依赖 flutter pub upgrade # 升级依赖 flutter pub outdated # 检查过时依赖 # 代码生成 flutter pub run build_runner build # 生成代码 flutter pub run build_runner watch # 监听并自动生成 flutter pub run build_runner build --delete-conflicting-outputs # 冲突时删除重新生成 # 构建 flutter build apk # Android APK flutter build apk --split-per-abi # 按 ABI 分包 flutter build appbundle # Android App Bundle flutter build ios # iOS flutter build web # Web # 测试与分析 flutter test # 运行测试 flutter analyze # 静态分析 flutter format lib/ # 格式化代码 flutter doctor # 检查环境 # 设备管理 flutter devices # 列出设备 flutter emulators # 列出模拟器 flutter emulators --launch Pixel_6_API_33 # 启动模拟器

pubspec.yaml 配置模板

name: my_app description: A new Flutter project. publish_to: "none" version: 1.0.0+1 environment: sdk: ">=3.0.0 <4.0.0" dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.6 # 状态管理 flutter_riverpod: ^2.4.9 # 网络请求 dio: ^5.4.0 retrofit: ^4.0.3 # 本地存储 shared_preferences: ^2.2.2 hive_flutter: ^1.1.0 # 路由 go_router: ^13.0.1 # 代码生成 freezed_annotation: ^2.4.1 json_annotation: ^4.8.1 # 工具 logger: ^2.0.2 flutter_screenutil: ^5.9.0 # 屏幕适配 cached_network_image: ^3.3.0 # 图片缓存 # UI 组件 shimmer: ^3.0.0 # 骨架屏 fluttertoast: ^8.2.4 # Toast flutter_slidable: ^3.0.1 # 侧滑 # 功能 image_picker: ^1.0.7 url_launcher: ^6.2.2 share_plus: ^7.2.1 # 国际化 flutter_localizations: sdk: flutter intl: ^0.19.0 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^3.0.1 # 代码生成 build_runner: ^2.4.7 freezed: ^2.4.5 json_serializable: ^6.7.1 retrofit_generator: ^8.0.6 hive_generator: ^2.0.1 flutter: uses-material-design: true assets: - assets/images/ - assets/icons/ - assets/fonts/ fonts: - family: CustomFont fonts: - asset: assets/fonts/CustomFont-Regular.ttf - asset: assets/fonts/CustomFont-Bold.ttf weight: 700

四、学习路线图

入门阶段(1-2 周)

  1. Dart 基础:变量、类型、函数、类、异步
  2. Flutter Widget:StatelessWidget、StatefulWidget、常用布局
  3. 基础项目:制作一个待办事项应用或计算器

进阶阶段(2-4 周)

  1. 状态管理:掌握 Riverpod 或 Bloc
  2. 网络请求:REST API 集成、数据序列化
  3. 本地存储:SharedPreferences、SQLite
  4. 导航路由:go_router、深度链接
  5. 项目:制作一个带网络请求的新闻应用或天气应用

高级阶段(持续学习)

  1. 性能优化:列表优化、图片缓存、渲染优化
  2. 自定义 Widget:CustomPainter、RenderObject
  3. 平台通道:调用原生功能
  4. 测试:单元测试、Widget 测试、集成测试
  5. CI/CD:自动化构建和发布
  6. 架构模式:Clean Architecture、DDD

五、推荐资源

资源链接说明
Flutter 官方文档https://docs.flutter.dev 最权威的学习资料
Dart 语言文档https://dart.dev/language Dart 语法详解
Flutter 中文社区https://flutter.cn 中文资源和文档
pub.devhttps://pub.dev Dart/Flutter 包仓库
Flutter Galleryhttps://gallery.flutter.dev Widget 示例展示
Flutter 官方示例https://github.com/flutter/samples 各种场景示例代码

提示:本指南涵盖了 Dart 和 Flutter 的核心知识点,建议边学边练。Flutter 开发最重要的是多写 Widget、多实践布局。遇到问题优先查阅官方文档,它是目前最全面和最新的资料。