📡 第一章:HTTP 基础
1.1 http 包简介
Flutter 官方提供的 http 包是进行网络请求的基础工具,支持常见的 HTTP 方法。
pubspec.yaml # 添加 http 依赖 dependencies: http: ^1.2.0
1.2 GET 请求
Dart import 'package:http/http.dart' as http; import 'dart:convert'; // GET 请求:获取用户列表 Future<List<dynamic>> fetchUsers() async { final response = await http.get( Uri.parse('https://api.example.com/users'), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, ); if (response.statusCode == 200) { return jsonDecode(response.body); } else { throw Exception('请求失败:${response.statusCode}'); } }
1.3 POST 请求
Dart // POST 请求:创建新用户 Future<Map<String, dynamic>> createUser(String name, String email) async { final response = await http.post( Uri.parse('https://api.example.com/users'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({ 'name': name, 'email': email, }), ); if (response.statusCode == 201) { return jsonDecode(response.body); } else { throw Exception('创建失败:${response.statusCode}'); } }
1.4 PUT 和 DELETE 请求
Dart // PUT 请求:更新用户信息 Future<void> updateUser(int id, String name) async { final response = await http.put( Uri.parse('https://api.example.com/users/$id'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'name': name}), ); if (response.statusCode != 200) { throw Exception('更新失败'); } } // DELETE 请求:删除用户 Future<void> deleteUser(int id, String token) async { final response = await http.delete( Uri.parse('https://api.example.com/users/$id'), headers: {'Authorization': 'Bearer $token'}, ); if (response.statusCode != 204) { throw Exception('删除失败'); } }
1.5 响应状态码与错误处理
网络请求中常见的状态码:200 成功、201 创建成功、400 请求错误、401 未授权、404 未找到、500 服务器错误。
Dart import 'dart:io'; import 'dart:async'; Future<dynamic> safeRequest() async { try { final response = await http.get( Uri.parse('https://api.example.com/data'), ).timeout(const Duration(seconds: 10)); switch (response.statusCode) { case 200: return jsonDecode(response.body); case 401: throw Exception('未授权,请重新登录'); case 404: throw Exception('资源不存在'); case 500: throw Exception('服务器内部错误'); default: throw Exception('请求失败:${response.statusCode}'); } } on SocketException { throw Exception('网络连接失败,请检查网络'); } on TimeoutException { throw Exception('请求超时,请稍后重试'); } on FormatException { throw Exception('数据格式解析错误'); } }
提示:
http 包适合简单的网络请求场景。对于需要拦截器、取消请求、文件上传等高级功能,推荐使用 Dio。🚀 第二章:Dio 进阶
2.1 安装与基本使用
pubspec.yaml dependencies: dio: ^5.4.0
Dart import 'package:dio/dio.dart'; // 基本 GET 请求 final dio = Dio(); final response = await dio.get('https://api.example.com/users'); print(response.data); // Dio 自动解析 JSON
2.2 Dio 实例配置
Dart final dio = Dio(BaseOptions( baseUrl: 'https://api.example.com', connectTimeout: const Duration(seconds: 10), receiveTimeout: const Duration(seconds: 15), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, ));
2.3 拦截器
Dio 的拦截器系统允许你在请求发送前、响应返回后、以及错误发生时进行统一处理。
Dart class AuthInterceptor extends Interceptor { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) { // 在每个请求中自动添加 token final token = AuthService.instance.token; if (token != null) { options.headers['Authorization'] = 'Bearer $token'; } handler.next(options); } @override void onResponse(Response response, ResponseInterceptorHandler handler) { // 统一处理响应日志 print('[${response.statusCode}] ${response.requestOptions.path}'); handler.next(response); } @override void onError(DioException err, ErrorInterceptorHandler handler) { // 统一错误处理 if (err.response?.statusCode == 401) { AuthService.instance.logout(); } handler.next(err); } } // 注册拦截器 dio.interceptors.add(AuthInterceptor());
2.4 Token 刷新拦截器模式
Dart class TokenRefreshInterceptor extends Interceptor { final Dio _dio; bool _isRefreshing = false; TokenRefreshInterceptor(this._dio); @override void onError(DioException err, ErrorInterceptorHandler handler) async { if (err.response?.statusCode == 401 && !_isRefreshing) { _isRefreshing = true; try { // 使用 refreshToken 获取新的 accessToken final newToken = await _refreshToken(); AuthService.instance.saveToken(newToken); // 用新 token 重试原始请求 final opts = err.requestOptions; opts.headers['Authorization'] = 'Bearer $newToken'; final response = await _dio.fetch(opts); handler.resolve(response); } catch (e) { handler.reject(err); } finally { _isRefreshing = false; } } else { handler.next(err); } } Future<String> _refreshToken() async { final resp = await Dio().post( 'https://api.example.com/auth/refresh', data: {'refreshToken': AuthService.instance.refreshToken}, ); return resp.data['accessToken']; } }
2.5 文件上传与下载
Dart // 文件上传(带进度回调) Future<void> uploadFile(String filePath) async { final formData = FormData.fromMap({ 'file': await MultipartFile.fromFile( filePath, filename: 'upload.jpg', ), 'description': '用户头像', }); await dio.post( '/upload', data: formData, onSendProgress: (sent, total) { final progress = (sent / total * 100).toStringAsFixed(1); print('上传进度:$progress%'); }, ); } // 文件下载(带进度回调) Future<void> downloadFile(String url, String savePath) async { await dio.download( url, savePath, onReceiveProgress: (received, total) { if (total != -1) { final progress = (received / total * 100).toStringAsFixed(1); print('下载进度:$progress%'); } }, ); }
2.6 请求取消
Dart // 使用 CancelToken 取消请求 final cancelToken = CancelToken(); dio.get('/search', queryParameters: {'q': 'flutter'}, cancelToken: cancelToken, ); // 在用户切换页面或输入新关键词时取消 cancelToken.cancel('用户取消了搜索');
2.7 重试逻辑
Dart Future<Response> requestWithRetry( String path, { int maxRetries = 3, }) async { int retries = 0; while (true) { try { return await dio.get(path); } on DioException catch (e) { retries++; if (retries >= maxRetries) rethrow; // 指数退避策略 await Future.delayed(Duration(seconds: retries * 2)); } } }
2.8 完整 ApiClient 类
Dart class ApiClient { static final ApiClient _instance = ApiClient._internal(); factory ApiClient() => _instance; late final Dio _dio; ApiClient._internal() { _dio = Dio(BaseOptions( baseUrl: 'https://api.example.com/v1', connectTimeout: const Duration(seconds: 10), receiveTimeout: const Duration(seconds: 15), )); _dio.interceptors.addAll([ AuthInterceptor(), LogInterceptor(requestBody: true, responseBody: true), ]); } Future<T> get<T>(String path, {Map<String, dynamic>? params}) async { final r = await _dio.get(path, queryParameters: params); return r.data as T; } Future<T> post<T>(String path, {dynamic data}) async { final r = await _dio.post(path, data: data); return r.data as T; } Future<T> put<T>(String path, {dynamic data}) async { final r = await _dio.put(path, data: data); return r.data as T; } Future<T> delete<T>(String path) async { final r = await _dio.delete(path); return r.data as T; } }
📋 第三章:JSON 序列化
3.1 手动 JSON 解析
Dart import 'dart:convert'; // 手动解码 JSON 字符串 final jsonStr = '{"name": "张三", "age": 25}'; final Map<String, dynamic> map = jsonDecode(jsonStr); print(map['name']); // 张三 // 手动编码为 JSON 字符串 final encoded = jsonEncode({'name': '李四', 'age': 30}); print(encoded); // {"name":"李四","age":30}
3.2 模型类与 fromJson / toJson
Dart class User { final int id; final String name; final String email; final DateTime createdAt; User({ required this.id, required this.name, required this.email, required this.createdAt, }); // 从 JSON Map 构建对象 factory User.fromJson(Map<String, dynamic> json) { return User( id: json['id'] as int, name: json['name'] as String, email: json['email'] as String, createdAt: DateTime.parse(json['created_at']), ); } // 将对象转为 JSON Map Map<String, dynamic> toJson() => { 'id': id, 'name': name, 'email': email, 'created_at': createdAt.toIso8601String(), }; }
3.3 json_serializable 代码生成
pubspec.yaml dependencies: json_annotation: ^4.8.1 dev_dependencies: build_runner: ^2.4.0 json_serializable: ^6.7.1
Dart import 'package:json_annotation/json_annotation.dart'; part 'user.g.dart'; @JsonSerializable() class User { final int id; final String name; final String email; @JsonKey(name: 'created_at') final DateTime createdAt; @JsonKey(defaultValue: false) final bool isActive; User({ required this.id, required this.name, required this.email, required this.createdAt, this.isActive = false, }); // 由代码生成器自动生成 factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); Map<String, dynamic> toJson() => _$UserToJson(this); } // 运行代码生成命令: // dart run build_runner build
3.4 freezed 不可变模型 + JSON
Dart import 'package:freezed_annotation/freezed_annotation.dart'; part 'user.freezed.dart'; part 'user.g.dart'; @freezed class User with _$User { const factory User({ required int id, required String name, required String email, @JsonKey(name: 'created_at') required DateTime createdAt, @Default(false) bool isActive, }) = _User; factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); }
3.5 嵌套对象与列表序列化
Dart @JsonSerializable(explicitToJson: true) class Post { final int id; final String title; final User author; // 嵌套对象 final List<String> tags; // 字符串列表 final List<Comment> comments; // 对象列表 Post({ required this.id, required this.title, required this.author, required this.tags, required this.comments, }); factory Post.fromJson(Map<String, dynamic> json) => _$PostFromJson(json); Map<String, dynamic> toJson() => _$PostToJson(this); } // 解析列表 List<User> parseUsers(String jsonStr) { final List list = jsonDecode(jsonStr); return list.map((json) => User.fromJson(json)).toList(); }
注意:使用
explicitToJson: true 确保嵌套对象也会调用其 toJson 方法,否则嵌套对象只会输出 Instance of 'User'。🏗 第四章:Repository 模式
4.1 什么是 Repository 模式
Repository 模式将数据获取逻辑从业务逻辑中分离出来,提供统一的数据访问接口。它隐藏了数据来源的具体实现(网络、本地缓存、数据库),使代码更易测试和维护。
4.2 抽象接口定义
Dart // 定义仓库抽象接口 abstract class UserRepository { Future<List<User>> getUsers(); Future<User> getUserById(int id); Future<void> createUser(User user); Future<void> updateUser(User user); Future<void> deleteUser(int id); }
4.3 远程数据源
Dart class UserRemoteDataSource { final ApiClient _api; UserRemoteDataSource(this._api); Future<List<User>> fetchUsers() async { final data = await _api.get<List>('/users'); return data.map((j) => User.fromJson(j)).toList(); } Future<User> fetchUserById(int id) async { final data = await _api.get<Map<String, dynamic>>('/users/$id'); return User.fromJson(data); } }
4.4 本地数据源(缓存)
Dart class UserLocalDataSource { final Box<User> _box; UserLocalDataSource(this._box); List<User> getCachedUsers() => _box.values.toList(); Future<void> cacheUsers(List<User> users) async { await _box.clear(); for (final user in users) { await _box.put(user.id, user); } } }
4.5 缓存策略
常见的缓存策略有两种:
- 缓存优先(Cache First):先返回本地缓存数据,同时在后台刷新
- 网络优先(Network First):先尝试网络请求,失败时回退到缓存
4.6 使用 Result 模式处理错误
Dart // 简单的 Result 类型 sealed class Result<T> {} class Success<T> extends Result<T> { final T data; Success(this.data); } class Failure<T> extends Result<T> { final String message; Failure(this.message); }
4.7 完整 Repository 实现
Dart class UserRepositoryImpl implements UserRepository { final UserRemoteDataSource _remote; final UserLocalDataSource _local; UserRepositoryImpl(this._remote, this._local); // 网络优先策略 @override Future<List<User>> getUsers() async { try { // 尝试从网络获取 final users = await _remote.fetchUsers(); // 成功后缓存到本地 await _local.cacheUsers(users); return users; } catch (e) { // 网络失败,返回本地缓存 final cached = _local.getCachedUsers(); if (cached.isNotEmpty) return cached; rethrow; } } @override Future<User> getUserById(int id) async { return await _remote.fetchUserById(id); } @override Future<void> createUser(User user) async { await _remote._api.post('/users', data: user.toJson()); } @override Future<void> updateUser(User user) async { await _remote._api.put('/users/${user.id}', data: user.toJson()); } @override Future<void> deleteUser(int id) async { await _remote._api.delete('/users/$id'); } }
🗄 第六章:SQLite 数据库
6.1 sqflite 安装
pubspec.yaml dependencies: sqflite: ^2.3.0 path: ^1.8.3
6.2 数据库创建与版本管理
Dart import 'package:sqflite/sqflite.dart'; import 'package:path/path.dart'; class DatabaseHelper { static Database? _db; Future<Database> get database async { _db ??= await _initDB(); return _db!; } Future<Database> _initDB() async { final dbPath = await getDatabasesPath(); final path = join(dbPath, 'notes.db'); return await openDatabase( path, version: 2, onCreate: (db, version) async { // 首次创建表 await db.execute(''' CREATE TABLE notes ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL, created_at TEXT NOT NULL ) '''); }, onUpgrade: (db, oldVersion, newVersion) async { // 数据库迁移 if (oldVersion < 2) { await db.execute( 'ALTER TABLE notes ADD COLUMN is_pinned INTEGER DEFAULT 0', ); } }, ); } }
6.3 CRUD 操作
Dart class NoteDao { final DatabaseHelper _dbHelper; NoteDao(this._dbHelper); // 创建笔记 Future<int> insert(Note note) async { final db = await _dbHelper.database; return await db.insert('notes', note.toMap()); } // 查询所有笔记 Future<List<Note>> getAll() async { final db = await _dbHelper.database; final maps = await db.query('notes', orderBy: 'created_at DESC', ); return maps.map((m) => Note.fromMap(m)).toList(); } // 条件查询 Future<List<Note>> search(String keyword) async { final db = await _dbHelper.database; final maps = await db.query( 'notes', where: 'title LIKE ? OR content LIKE ?', whereArgs: ['%$keyword%', '%$keyword%'], orderBy: 'created_at DESC', limit: 20, ); return maps.map((m) => Note.fromMap(m)).toList(); } // 更新笔记 Future<int> update(Note note) async { final db = await _dbHelper.database; return await db.update( 'notes', note.toMap(), where: 'id = ?', whereArgs: [note.id], ); } // 删除笔记 Future<int> delete(int id) async { final db = await _dbHelper.database; return await db.delete('notes', where: 'id = ?', whereArgs: [id]); } }
6.4 事务处理
Dart // 在事务中批量操作,保证原子性 Future<void> batchInsert(List<Note> notes) async { final db = await _dbHelper.database; await db.transaction((txn) async { for (final note in notes) { await txn.insert('notes', note.toMap()); } }); }
6.5 drift(类型安全的 SQL)
drift(原名 moor)是一个类型安全的 SQLite 封装库,提供编译时检查和代码生成。
Dart import 'package:drift/drift.dart'; // 定义表结构 class Notes extends Table { IntColumn get id => integer().autoIncrement()(); TextColumn get title => text().withLength(min: 1, max: 100)(); TextColumn get content => text()(); DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); } // 编译时类型安全的查询 Future<List<Note>> searchNotes(String query) { return (select(notes) ..where((n) => n.title.contains(query)) ..orderBy([(n) => OrderingTerm.desc(n.createdAt)]) ..limit(20)) .get(); }
6.6 完整示例:笔记数据库模型
Dart class Note { final int? id; final String title; final String content; final DateTime createdAt; Note({this.id, required this.title, required this.content, DateTime? createdAt}) : createdAt = createdAt ?? DateTime.now(); Map<String, dynamic> toMap() => { 'id': id, 'title': title, 'content': content, 'created_at': createdAt.toIso8601String(), }; factory Note.fromMap(Map<String, dynamic> map) => Note( id: map['id'], title: map['title'], content: map['content'], createdAt: DateTime.parse(map['created_at']), ); }
📦 第七章:Hive & Isar
7.1 Hive 简介与设置
pubspec.yaml dependencies: hive: ^2.2.3 hive_flutter: ^1.1.0 dev_dependencies: hive_generator: ^2.0.1 build_runner: ^2.4.0
Dart import 'package:hive_flutter/hive_flutter.dart'; // 初始化 Hive void main() async { await Hive.initFlutter(); // 注册适配器 Hive.registerAdapter(ContactAdapter()); // 打开 Box await Hive.openBox<Contact>('contacts'); runApp(MyApp()); }
7.2 Hive 数据模型
Dart import 'package:hive/hive.dart'; part 'contact.g.dart'; @HiveType(typeId: 0) class Contact extends HiveObject { @HiveField(0) String name; @HiveField(1) String phone; @HiveField(2) String? email; Contact({required this.name, required this.phone, this.email}); } // 运行:dart run build_runner build
7.3 Hive CRUD 操作
Dart final box = Hive.box<Contact>('contacts'); // 添加 final contact = Contact(name: '张三', phone: '13800138000'); await box.add(contact); // 自动生成 key await box.put('zhangsan', contact); // 自定义 key // 读取 final c = box.get('zhangsan'); final all = box.values.toList(); // 更新 contact.phone = '13900139000'; await contact.save(); // HiveObject 提供的快捷方法 // 删除 await box.delete('zhangsan'); await contact.delete(); // 或直接删除对象
7.4 Isar:现代 NoSQL 方案
Dart import 'package:isar/isar.dart'; part 'task.g.dart'; @collection class Task { Id id = Isar.autoIncrement; late String title; late bool isCompleted; @Index() late DateTime createdAt; } // 查询示例 final isar = await Isar.open([TaskSchema]); // 查询未完成的任务,按创建时间倒序 final tasks = await isar.tasks .filter() .isCompletedEqualTo(false) .sortByCreatedAtDesc() .findAll();
7.5 方案对比
| 特性 | sqflite | Hive | Isar |
|---|---|---|---|
| 类型 | 关系型 (SQL) | 键值对 NoSQL | 文档型 NoSQL |
| 查询能力 | 完整 SQL | 基本过滤 | 强大的查询构建器 |
| 性能 | 中等 | 快 | 非常快 |
| 类型安全 | 弱 | 需适配器 | 编译时检查 |
| Web 支持 | 否 | 是 | 是 |
| 适用场景 | 复杂查询、关系数据 | 设置、缓存 | 通用本地存储 |
🔒 第八章:安全存储
8.1 flutter_secure_storage
敏感数据(密码、token、密钥)应使用安全存储。flutter_secure_storage 在 iOS 上使用 Keychain,在 Android 上使用 KeyStore 加密。
pubspec.yaml dependencies: flutter_secure_storage: ^9.0.0
Dart import 'package:flutter_secure_storage/flutter_secure_storage.dart'; class SecureStorageService { static const _storage = FlutterSecureStorage(); // 存储 Token static Future<void> saveToken(String token) async { await _storage.write(key: 'auth_token', value: token); } // 读取 Token static Future<String?> getToken() async { return await _storage.read(key: 'auth_token'); } // 存储用户密码 static Future<void> savePassword(String password) async { await _storage.write(key: 'user_password', value: password); } // 删除指定键 static Future<void> deleteToken() async { await _storage.delete(key: 'auth_token'); } // 清除所有安全存储 static Future<void> clearAll() async { await _storage.deleteAll(); } // 检查是否存在 static Future<bool> hasToken() async { final token = await _storage.read(key: 'auth_token'); return token != null; } }
平台说明:iOS 使用 Keychain Services(硬件加密),Android 使用 EncryptedSharedPreferences 或 KeyStore。数据在应用卸载后会被清除。
🔥 第九章:Firebase 集成
9.1 FlutterFire 初始化
pubspec.yaml dependencies: firebase_core: ^2.24.0 firebase_auth: ^4.16.0 cloud_firestore: ^4.14.0 firebase_storage: ^11.6.0 firebase_messaging: ^14.7.0
Dart import 'package:firebase_core/firebase_core.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(MyApp()); }
9.2 Firebase Auth:用户认证
Dart import 'package:firebase_auth/firebase_auth.dart'; class AuthService { final _auth = FirebaseAuth.instance; // 监听登录状态 Stream<User?> get authStateChanges => _auth.authStateChanges(); // 邮箱注册 Future<UserCredential> signUp(String email, String password) async { try { return await _auth.createUserWithEmailAndPassword( email: email, password: password, ); } on FirebaseAuthException catch (e) { throw Exception('注册失败:${e.message}'); } } // 邮箱登录 Future<UserCredential> signIn(String email, String password) async { return await _auth.signInWithEmailAndPassword( email: email, password: password, ); } // Google 登录 Future<UserCredential> signInWithGoogle() async { final googleUser = await GoogleSignIn().signIn(); final googleAuth = await googleUser!.authentication; final credential = GoogleAuthProvider.credential( accessToken: googleAuth.accessToken, idToken: googleAuth.idToken, ); return await _auth.signInWithCredential(credential); } // 退出登录 Future<void> signOut() => _auth.signOut(); }
9.3 Cloud Firestore
Dart import 'package:cloud_firestore/cloud_firestore.dart'; class FirestoreService { final _db = FirebaseFirestore.instance; // 创建文档 Future<void> addPost(Map<String, dynamic> data) async { await _db.collection('posts').add({ ...data, 'createdAt': FieldValue.serverTimestamp(), }); } // 获取文档列表 Future<List<Map<String, dynamic>>> getPosts() async { final snapshot = await _db .collection('posts') .orderBy('createdAt', descending: true) .limit(20) .get(); return snapshot.docs.map((d) => {'id': d.id, ...d.data()}).toList(); } // 实时监听 Stream<QuerySnapshot> postsStream() { return _db .collection('posts') .orderBy('createdAt', descending: true) .snapshots(); } // 更新文档 Future<void> updatePost(String id, Map<String, dynamic> data) async { await _db.collection('posts').doc(id).update(data); } // 删除文档 Future<void> deletePost(String id) async { await _db.collection('posts').doc(id).delete(); } // 条件查询 Future<List<Map<String, dynamic>>> getPostsByAuthor(String uid) async { final snapshot = await _db .collection('posts') .where('authorId', isEqualTo: uid) .get(); return snapshot.docs.map((d) => d.data()).toList(); } }
9.4 Firebase Storage
Dart import 'package:firebase_storage/firebase_storage.dart'; // 上传文件 Future<String> uploadImage(File file) async { final ref = FirebaseStorage.instance .ref() .child('images/${DateTime.now().millisecondsSinceEpoch}.jpg'); await ref.putFile(file); return await ref.getDownloadURL(); }
9.5 Firebase Messaging
Dart import 'package:firebase_messaging/firebase_messaging.dart'; // 初始化推送通知 Future<void> initFCM() async { final messaging = FirebaseMessaging.instance; // 请求权限(iOS) await messaging.requestPermission(); // 获取 FCM token final token = await messaging.getToken(); print('FCM Token: $token'); // 前台消息处理 FirebaseMessaging.onMessage.listen((message) { print('收到消息:${message.notification?.title}'); }); // 后台点击消息处理 FirebaseMessaging.onMessageOpenedApp.listen((message) { print('用户点击了通知:${message.data}'); }); }
🔌 第十章:WebSocket
10.1 Dart 原生 WebSocket
Dart import 'dart:io'; Future<void> connectWebSocket() async { final ws = await WebSocket.connect('wss://echo.websocket.org'); // 发送消息 ws.add('Hello WebSocket!'); // 监听消息 ws.listen( (data) => print('收到:$data'), onDone: () => print('连接关闭'), onError: (err) => print('错误:$err'), ); }
10.2 web_socket_channel 包
pubspec.yaml dependencies: web_socket_channel: ^2.4.0
10.3 完整示例:简单聊天
Dart import 'package:web_socket_channel/web_socket_channel.dart'; import 'dart:async'; class ChatService { WebSocketChannel? _channel; Timer? _reconnectTimer; final _controller = StreamController<String>.broadcast(); bool _isConnected = false; // 消息流,供 UI 监听 Stream<String> get messages => _controller.stream; // 连接 WebSocket Future<void> connect(String url) async { try { _channel = WebSocketChannel.connect(Uri.parse(url)); _isConnected = true; _channel!.stream.listen( (data) { _controller.add(data as String); }, onDone: () { _isConnected = false; _scheduleReconnect(url); }, onError: (err) { _isConnected = false; _scheduleReconnect(url); }, ); } catch (e) { _scheduleReconnect(url); } } // 发送消息 void send(String message) { if (_isConnected && _channel != null) { _channel!.sink.add(message); } } // 自动重连策略 void _scheduleReconnect(String url) { _reconnectTimer?.cancel(); _reconnectTimer = Timer( const Duration(seconds: 5), () => connect(url), ); } // 断开连接 Future<void> disconnect() async { _reconnectTimer?.cancel(); await _channel?.sink.close(); _isConnected = false; } void dispose() { disconnect(); _controller.close(); } } // 在 Widget 中使用 class ChatScreen extends StatefulWidget { @override State<ChatScreen> createState() => _ChatScreenState(); } class _ChatScreenState extends State<ChatScreen> { final _chat = ChatService(); final _textController = TextEditingController(); final List<String> _messages = []; @override void initState() { super.initState(); _chat.connect('wss://chat.example.com/ws'); _chat.messages.listen((msg) { setState(() => _messages.add(msg)); }); } @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: ListView.builder( itemCount: _messages.length, itemBuilder: (_, i) => ListTile(title: Text(_messages[i])), ), ), Row( children: [ Expanded(child: TextField(controller: _textController)), IconButton( icon: Icon(Icons.send), onPressed: () { _chat.send(_textController.text); _textController.clear(); }, ), ], ), ], ); } @override void dispose() { _chat.dispose(); _textController.dispose(); super.dispose(); } }
🎯 第十一章:实践练习
练习 1:天气应用
使用 Dio 调用公开天气 API,实现以下功能:
- 输入城市名搜索天气信息
- 使用 json_serializable 解析天气数据模型
- 用 SharedPreferences 保存最近搜索的城市
- 实现网络错误时的友好提示
- 添加下拉刷新功能
练习 2:笔记本应用
构建一个完整的笔记本应用,练习本地存储技术:
- 使用 SQLite(sqflite)存储笔记,支持增删改查
- 实现搜索功能(按标题或内容模糊查询)
- 支持笔记置顶和排序
- 使用 Repository 模式组织数据层代码
- 添加数据库版本迁移逻辑
练习 3:实时聊天
结合 Firebase 或 WebSocket 实现简易聊天功能:
- 使用 Firebase Auth 实现邮箱注册和登录
- 使用 Cloud Firestore 存储聊天消息并实时同步
- 使用 flutter_secure_storage 安全存储登录凭证
- 实现消息列表的实时更新(StreamBuilder)
- 添加推送通知(Firebase Messaging)