Flutter之?dāng)?shù)據(jù)調(diào)用和后端

?? 目錄

  1. 概述
  2. 網(wǎng)絡(luò)請求(HTTP)
  3. JSON 序列化和反序列化
  4. 數(shù)據(jù)持久化
  5. 狀態(tài)管理與數(shù)據(jù)
  6. 錯(cuò)誤處理
  7. 緩存策略
  8. 與后端 API 交互
  9. 最佳實(shí)踐
  10. 常見問題

概述

Flutter 應(yīng)用需要與后端服務(wù)交互來獲取和發(fā)送數(shù)據(jù)。主要涉及:

  • 網(wǎng)絡(luò)請求:使用 HTTP 協(xié)議與服務(wù)器通信
  • 數(shù)據(jù)序列化:將 JSON 數(shù)據(jù)轉(zhuǎn)換為 Dart 對象
  • 數(shù)據(jù)持久化:本地存儲(chǔ)數(shù)據(jù)(SharedPreferences、SQLite、文件等)
  • 狀態(tài)管理:管理異步數(shù)據(jù)的狀態(tài)
  • 錯(cuò)誤處理:處理網(wǎng)絡(luò)錯(cuò)誤和異常

網(wǎng)絡(luò)請求(HTTP)

1. 使用 http 包

安裝:

dependencies:
  http: ^1.1.0

基本用法:

import 'package:http/http.dart' as http;
import 'dart:convert';

// GET 請求
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 {
    // 請求失敗
    print('請求失敗: ${response.statusCode}');
  }
}

// POST 請求
Future<void> sendData() async {
  final response = await http.post(
    Uri.parse('https://api.example.com/data'),
    headers: {'Content-Type': 'application/json'},
    body: jsonEncode({
      'name': 'John',
      'email': 'john@example.com',
    }),
  );
  
  if (response.statusCode == 201) {
    print('數(shù)據(jù)發(fā)送成功');
  }
}

2. 完整的 HTTP 服務(wù)類

import 'package:http/http.dart' as http;
import 'dart:convert';

class ApiService {
  static const String baseUrl = 'https://api.example.com';
  
  // GET 請求
  static Future<Map<String, dynamic>> get(String endpoint) async {
    try {
      final response = await http.get(
        Uri.parse('$baseUrl$endpoint'),
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
        },
      );
      
      return _handleResponse(response);
    } catch (e) {
      throw Exception('網(wǎng)絡(luò)請求失敗: $e');
    }
  }
  
  // POST 請求
  static Future<Map<String, dynamic>> post(
    String endpoint,
    Map<String, dynamic> data,
  ) async {
    try {
      final response = await http.post(
        Uri.parse('$baseUrl$endpoint'),
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
        },
        body: jsonEncode(data),
      );
      
      return _handleResponse(response);
    } catch (e) {
      throw Exception('網(wǎng)絡(luò)請求失敗: $e');
    }
  }
  
  // PUT 請求
  static Future<Map<String, dynamic>> put(
    String endpoint,
    Map<String, dynamic> data,
  ) async {
    try {
      final response = await http.put(
        Uri.parse('$baseUrl$endpoint'),
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
        },
        body: jsonEncode(data),
      );
      
      return _handleResponse(response);
    } catch (e) {
      throw Exception('網(wǎng)絡(luò)請求失敗: $e');
    }
  }
  
  // DELETE 請求
  static Future<void> delete(String endpoint) async {
    try {
      final response = await http.delete(
        Uri.parse('$baseUrl$endpoint'),
        headers: {
          'Content-Type': 'application/json',
        },
      );
      
      _handleResponse(response);
    } catch (e) {
      throw Exception('網(wǎng)絡(luò)請求失敗: $e');
    }
  }
  
  // 處理響應(yīng)
  static Map<String, dynamic> _handleResponse(http.Response response) {
    if (response.statusCode >= 200 && response.statusCode < 300) {
      if (response.body.isEmpty) {
        return {};
      }
      return jsonDecode(response.body) as Map<String, dynamic>;
    } else {
      throw HttpException(
        '請求失敗: ${response.statusCode}',
        statusCode: response.statusCode,
      );
    }
  }
}

// 自定義異常
class HttpException implements Exception {
  final String message;
  final int? statusCode;
  
  HttpException(this.message, {this.statusCode});
  
  @override
  String toString() => message;
}

3. 帶認(rèn)證的請求

class AuthenticatedApiService {
  static String? _token;
  
  static void setToken(String token) {
    _token = token;
  }
  
  static Map<String, String> _getHeaders() {
    final headers = {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
    };
    
    if (_token != null) {
      headers['Authorization'] = 'Bearer $_token';
    }
    
    return headers;
  }
  
  static Future<Map<String, dynamic>> get(String endpoint) async {
    final response = await http.get(
      Uri.parse('https://api.example.com$endpoint'),
      headers: _getHeaders(),
    );
    
    return _handleResponse(response);
  }
  
  // ... 其他方法類似
}

4. 使用 dio 包(推薦)

dio 是一個(gè)功能更強(qiáng)大的 HTTP 客戶端庫。

安裝:

dependencies:
  dio: ^5.4.0

基本用法:

import 'package:dio/dio.dart';

class DioService {
  late Dio _dio;
  
  DioService() {
    _dio = Dio(
      BaseOptions(
        baseUrl: 'https://api.example.com',
        connectTimeout: const Duration(seconds: 5),
        receiveTimeout: const Duration(seconds: 3),
        headers: {
          'Content-Type': 'application/json',
        },
      ),
    );
    
    // 添加攔截器
    _dio.interceptors.add(LogInterceptor(
      requestBody: true,
      responseBody: true,
    ));
  }
  
  // GET 請求
  Future<Response> get(String path, {Map<String, dynamic>? queryParameters}) async {
    try {
      return await _dio.get(path, queryParameters: queryParameters);
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }
  
  // POST 請求
  Future<Response> post(
    String path,
    Map<String, dynamic>? data,
  ) async {
    try {
      return await _dio.post(path, data: data);
    } on DioException catch (e) {
      throw _handleError(e);
    }
  }
  
  // 錯(cuò)誤處理
  String _handleError(DioException error) {
    switch (error.type) {
      case DioExceptionType.connectionTimeout:
        return '連接超時(shí)';
      case DioExceptionType.sendTimeout:
        return '發(fā)送超時(shí)';
      case DioExceptionType.receiveTimeout:
        return '接收超時(shí)';
      case DioExceptionType.badResponse:
        return '服務(wù)器錯(cuò)誤: ${error.response?.statusCode}';
      case DioExceptionType.cancel:
        return '請求已取消';
      default:
        return '網(wǎng)絡(luò)錯(cuò)誤: ${error.message}';
    }
  }
}

JSON 序列化和反序列化

1. 手動(dòng)序列化

class User {
  final int id;
  final String name;
  final String email;
  
  User({
    required this.id,
    required this.name,
    required this.email,
  });
  
  // 從 JSON 創(chuàng)建對象
  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'] as int,
      name: json['name'] as String,
      email: json['email'] as String,
    );
  }
  
  // 轉(zhuǎn)換為 JSON
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'email': email,
    };
  }
}

// 使用
void example() {
  // JSON 字符串
  final jsonString = '{"id": 1, "name": "John", "email": "john@example.com"}';
  
  // 解析 JSON
  final json = jsonDecode(jsonString) as Map<String, dynamic>;
  final user = User.fromJson(json);
  
  // 轉(zhuǎn)換為 JSON
  final userJson = user.toJson();
  final jsonString2 = jsonEncode(userJson);
}

2. 使用 json_serializable(推薦)

自動(dòng)生成序列化代碼,減少樣板代碼。

安裝:

dependencies:
  json_annotation: ^4.8.1

dev_dependencies:
  json_serializable: ^6.7.1
  build_runner: ^2.4.7

定義模型:

import 'package:json_annotation/json_annotation.dart';

part 'user.g.dart';

@JsonSerializable()
class User {
  final int id;
  final String name;
  final String email;
  
  User({
    required this.id,
    required this.name,
    required this.email,
  });
  
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

生成代碼:

flutter pub run build_runner build

3. 處理嵌套對象

@JsonSerializable()
class Address {
  final String street;
  final String city;
  
  Address({required this.street, required this.city});
  
  factory Address.fromJson(Map<String, dynamic> json) => _$AddressFromJson(json);
  Map<String, dynamic> toJson() => _$AddressToJson(this);
}

@JsonSerializable()
class User {
  final int id;
  final String name;
  final Address address;
  
  User({
    required this.id,
    required this.name,
    required this.address,
  });
  
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

4. 處理列表

// 解析 JSON 數(shù)組
final jsonString = '''
[
  {"id": 1, "name": "John"},
  {"id": 2, "name": "Jane"}
]
''';

final List<dynamic> jsonList = jsonDecode(jsonString);
final List<User> users = jsonList
    .map((json) => User.fromJson(json as Map<String, dynamic>))
    .toList();

數(shù)據(jù)持久化

1. SharedPreferences - 鍵值對存儲(chǔ)

安裝:

dependencies:
  shared_preferences: ^2.2.2

使用:

import 'package:shared_preferences/shared_preferences.dart';

class PreferencesService {
  static SharedPreferences? _prefs;
  
  // 初始化
  static Future<void> init() async {
    _prefs = await SharedPreferences.getInstance();
  }
  
  // 保存字符串
  static Future<bool> saveString(String key, String value) async {
    return await _prefs?.setString(key, value) ?? false;
  }
  
  // 讀取字符串
  static String? getString(String key) {
    return _prefs?.getString(key);
  }
  
  // 保存整數(shù)
  static Future<bool> saveInt(String key, int value) async {
    return await _prefs?.setInt(key, value) ?? false;
  }
  
  // 讀取整數(shù)
  static int? getInt(String key) {
    return _prefs?.getInt(key);
  }
  
  // 保存布爾值
  static Future<bool> saveBool(String key, bool value) async {
    return await _prefs?.setBool(key, value) ?? false;
  }
  
  // 讀取布爾值
  static bool? getBool(String key) {
    return _prefs?.getBool(key);
  }
  
  // 刪除
  static Future<bool> remove(String key) async {
    return await _prefs?.remove(key) ?? false;
  }
  
  // 清空所有
  static Future<bool> clear() async {
    return await _prefs?.clear() ?? false;
  }
}

// 使用示例
void example() async {
  await PreferencesService.init();
  
  // 保存數(shù)據(jù)
  await PreferencesService.saveString('username', 'John');
  await PreferencesService.saveInt('age', 25);
  
  // 讀取數(shù)據(jù)
  final username = PreferencesService.getString('username');
  final age = PreferencesService.getInt('age');
}

2. SQLite - 關(guān)系型數(shù)據(jù)庫

安裝:

dependencies:
  sqflite: ^2.3.0
  path: ^1.8.3

使用:

import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

class DatabaseHelper {
  static final DatabaseHelper instance = DatabaseHelper._init();
  static Database? _database;
  
  DatabaseHelper._init();
  
  Future<Database> get database async {
    if (_database != null) return _database!;
    _database = await _initDB('app.db');
    return _database!;
  }
  
  Future<Database> _initDB(String filePath) async {
    final dbPath = await getDatabasesPath();
    final path = join(dbPath, filePath);
    
    return await openDatabase(
      path,
      version: 1,
      onCreate: _createDB,
    );
  }
  
  Future<void> _createDB(Database db, int version) async {
    await db.execute('''
      CREATE TABLE users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        email TEXT NOT NULL
      )
    ''');
  }
  
  // 插入數(shù)據(jù)
  Future<int> insertUser(Map<String, dynamic> user) async {
    final db = await database;
    return await db.insert('users', user);
  }
  
  // 查詢所有
  Future<List<Map<String, dynamic>>> getAllUsers() async {
    final db = await database;
    return await db.query('users');
  }
  
  // 查詢單個(gè)
  Future<Map<String, dynamic>?> getUser(int id) async {
    final db = await database;
    final results = await db.query(
      'users',
      where: 'id = ?',
      whereArgs: [id],
    );
    
    if (results.isNotEmpty) {
      return results.first;
    }
    return null;
  }
  
  // 更新
  Future<int> updateUser(int id, Map<String, dynamic> user) async {
    final db = await database;
    return await db.update(
      'users',
      user,
      where: 'id = ?',
      whereArgs: [id],
    );
  }
  
  // 刪除
  Future<int> deleteUser(int id) async {
    final db = await database;
    return await db.delete(
      'users',
      where: 'id = ?',
      whereArgs: [id],
    );
  }
  
  // 關(guān)閉數(shù)據(jù)庫
  Future<void> close() async {
    final db = await database;
    await db.close();
  }
}

3. 文件存儲(chǔ)

import 'dart:io';
import 'package:path_provider/path_provider.dart';

class FileService {
  // 獲取應(yīng)用文檔目錄
  static Future<File> getLocalFile(String filename) async {
    final directory = await getApplicationDocumentsDirectory();
    final path = directory.path;
    return File('$path/$filename');
  }
  
  // 寫入文件
  static Future<File> writeFile(String filename, String content) async {
    final file = await getLocalFile(filename);
    return await file.writeAsString(content);
  }
  
  // 讀取文件
  static Future<String> readFile(String filename) async {
    try {
      final file = await getLocalFile(filename);
      return await file.readAsString();
    } catch (e) {
      return '';
    }
  }
  
  // 刪除文件
  static Future<void> deleteFile(String filename) async {
    try {
      final file = await getLocalFile(filename);
      await file.delete();
    } catch (e) {
      print('刪除文件失敗: $e');
    }
  }
}

狀態(tài)管理與數(shù)據(jù)

1. 使用 Provider 管理數(shù)據(jù)狀態(tài)

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

// 數(shù)據(jù)模型
class User {
  final int id;
  final String name;
  final String email;
  
  User({required this.id, required this.name, required this.email});
  
  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'],
      name: json['name'],
      email: json['email'],
    );
  }
}

// 狀態(tài)管理類
class UserProvider extends ChangeNotifier {
  List<User> _users = [];
  bool _isLoading = false;
  String? _error;
  
  List<User> get users => _users;
  bool get isLoading => _isLoading;
  String? get error => _error;
  
  // 獲取用戶列表
  Future<void> fetchUsers() async {
    _isLoading = true;
    _error = null;
    notifyListeners();
    
    try {
      final response = await http.get(
        Uri.parse('https://api.example.com/users'),
      );
      
      if (response.statusCode == 200) {
        final List<dynamic> jsonList = jsonDecode(response.body);
        _users = jsonList
            .map((json) => User.fromJson(json as Map<String, dynamic>))
            .toList();
      } else {
        _error = '獲取數(shù)據(jù)失敗';
      }
    } catch (e) {
      _error = '網(wǎng)絡(luò)錯(cuò)誤: $e';
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }
  
  // 添加用戶
  Future<void> addUser(User user) async {
    try {
      final response = await http.post(
        Uri.parse('https://api.example.com/users'),
        headers: {'Content-Type': 'application/json'},
        body: jsonEncode(user.toJson()),
      );
      
      if (response.statusCode == 201) {
        _users.add(user);
        notifyListeners();
      }
    } catch (e) {
      _error = '添加用戶失敗: $e';
      notifyListeners();
    }
  }
}

// 使用
class UserListScreen extends StatelessWidget {
  const UserListScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('用戶列表')),
      body: Consumer<UserProvider>(
        builder: (context, userProvider, child) {
          if (userProvider.isLoading) {
            return const Center(child: CircularProgressIndicator());
          }
          
          if (userProvider.error != null) {
            return Center(child: Text('錯(cuò)誤: ${userProvider.error}'));
          }
          
          return ListView.builder(
            itemCount: userProvider.users.length,
            itemBuilder: (context, index) {
              final user = userProvider.users[index];
              return ListTile(
                title: Text(user.name),
                subtitle: Text(user.email),
              );
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          context.read<UserProvider>().fetchUsers();
        },
        child: const Icon(Icons.refresh),
      ),
    );
  }
}

2. 使用 FutureBuilder

class UserListScreen extends StatelessWidget {
  const UserListScreen({super.key});

  Future<List<User>> fetchUsers() async {
    final response = await http.get(
      Uri.parse('https://api.example.com/users'),
    );
    
    if (response.statusCode == 200) {
      final List<dynamic> jsonList = jsonDecode(response.body);
      return jsonList
          .map((json) => User.fromJson(json as Map<String, dynamic>))
          .toList();
    } else {
      throw Exception('獲取數(shù)據(jù)失敗');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('用戶列表')),
      body: FutureBuilder<List<User>>(
        future: fetchUsers(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          }
          
          if (snapshot.hasError) {
            return Center(child: Text('錯(cuò)誤: ${snapshot.error}'));
          }
          
          if (!snapshot.hasData) {
            return const Center(child: Text('沒有數(shù)據(jù)'));
          }
          
          final users = snapshot.data!;
          return ListView.builder(
            itemCount: users.length,
            itemBuilder: (context, index) {
              final user = users[index];
              return ListTile(
                title: Text(user.name),
                subtitle: Text(user.email),
              );
            },
          );
        },
      ),
    );
  }
}

錯(cuò)誤處理

1. 網(wǎng)絡(luò)錯(cuò)誤處理

class ApiException implements Exception {
  final String message;
  final int? statusCode;
  
  ApiException(this.message, {this.statusCode});
  
  @override
  String toString() => message;
}

class ApiService {
  static Future<Map<String, dynamic>> get(String endpoint) async {
    try {
      final response = await http.get(
        Uri.parse('https://api.example.com$endpoint'),
      );
      
      if (response.statusCode == 200) {
        return jsonDecode(response.body) as Map<String, dynamic>;
      } else {
        throw ApiException(
          '請求失敗',
          statusCode: response.statusCode,
        );
      }
    } on SocketException {
      throw ApiException('網(wǎng)絡(luò)連接失敗,請檢查網(wǎng)絡(luò)');
    } on TimeoutException {
      throw ApiException('請求超時(shí),請重試');
    } on FormatException {
      throw ApiException('數(shù)據(jù)格式錯(cuò)誤');
    } catch (e) {
      throw ApiException('未知錯(cuò)誤: $e');
    }
  }
}

2. 重試機(jī)制

Future<T> retryRequest<T>(
  Future<T> Function() request, {
  int maxRetries = 3,
  Duration delay = const Duration(seconds: 1),
}) async {
  int attempts = 0;
  
  while (attempts < maxRetries) {
    try {
      return await request();
    } catch (e) {
      attempts++;
      if (attempts >= maxRetries) {
        rethrow;
      }
      await Future.delayed(delay);
    }
  }
  
  throw Exception('重試失敗');
}

緩存策略

1. 簡單的內(nèi)存緩存

class CacheService {
  static final Map<String, CacheItem> _cache = {};
  
  static void set(String key, dynamic value, {Duration? expiry}) {
    _cache[key] = CacheItem(
      value: value,
      expiry: expiry != null ? DateTime.now().add(expiry) : null,
    );
  }
  
  static dynamic get(String key) {
    final item = _cache[key];
    if (item == null) return null;
    
    if (item.expiry != null && DateTime.now().isAfter(item.expiry!)) {
      _cache.remove(key);
      return null;
    }
    
    return item.value;
  }
  
  static void clear() {
    _cache.clear();
  }
}

class CacheItem {
  final dynamic value;
  final DateTime? expiry;
  
  CacheItem({required this.value, this.expiry});
}

2. 使用 cached_network_image

安裝:

dependencies:
  cached_network_image: ^3.3.0

使用:

CachedNetworkImage(
  imageUrl: 'https://example.com/image.jpg',
  placeholder: (context, url) => const CircularProgressIndicator(),
  errorWidget: (context, url, error) => const Icon(Icons.error),
)

與后端 API 交互

1. RESTful API 示例

class UserApi {
  static const String baseUrl = 'https://api.example.com';
  
  // 獲取所有用戶
  static Future<List<User>> getUsers() async {
    final response = await http.get(Uri.parse('$baseUrl/users'));
    // ... 處理響應(yīng)
  }
  
  // 獲取單個(gè)用戶
  static Future<User> getUser(int id) async {
    final response = await http.get(Uri.parse('$baseUrl/users/$id'));
    // ... 處理響應(yīng)
  }
  
  // 創(chuàng)建用戶
  static Future<User> createUser(User user) async {
    final response = await http.post(
      Uri.parse('$baseUrl/users'),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode(user.toJson()),
    );
    // ... 處理響應(yīng)
  }
  
  // 更新用戶
  static Future<User> updateUser(int id, User user) async {
    final response = await http.put(
      Uri.parse('$baseUrl/users/$id'),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode(user.toJson()),
    );
    // ... 處理響應(yīng)
  }
  
  // 刪除用戶
  static Future<void> deleteUser(int id) async {
    final response = await http.delete(
      Uri.parse('$baseUrl/users/$id'),
    );
    // ... 處理響應(yīng)
  }
}

2. WebSocket 通信

安裝:

dependencies:
  web_socket_channel: ^2.4.0

使用:

import 'package:web_socket_channel/web_socket_channel.dart';

class WebSocketService {
  WebSocketChannel? _channel;
  
  void connect(String url) {
    _channel = WebSocketChannel.connect(Uri.parse(url));
  }
  
  void sendMessage(String message) {
    _channel?.sink.add(message);
  }
  
  Stream<dynamic> get stream => _channel?.stream ?? const Stream.empty();
  
  void disconnect() {
    _channel?.sink.close();
  }
}

最佳實(shí)踐

1. 代碼組織

lib/
  models/
    user.dart
    product.dart
  services/
    api_service.dart
    database_service.dart
    cache_service.dart
  providers/
    user_provider.dart
    product_provider.dart
  screens/
    user_list_screen.dart
    user_detail_screen.dart

2. 使用 Repository 模式

abstract class UserRepository {
  Future<List<User>> getUsers();
  Future<User> getUser(int id);
  Future<User> createUser(User user);
  Future<User> updateUser(int id, User user);
  Future<void> deleteUser(int id);
}

class UserRepositoryImpl implements UserRepository {
  final ApiService _apiService;
  final DatabaseHelper _databaseHelper;
  
  UserRepositoryImpl(this._apiService, this._databaseHelper);
  
  @override
  Future<List<User>> getUsers() async {
    try {
      // 先嘗試從網(wǎng)絡(luò)獲取
      final users = await _apiService.getUsers();
      // 保存到本地?cái)?shù)據(jù)庫
      await _databaseHelper.saveUsers(users);
      return users;
    } catch (e) {
      // 網(wǎng)絡(luò)失敗,從本地獲取
      return await _databaseHelper.getUsers();
    }
  }
  
  // ... 其他方法
}

3. 環(huán)境配置

class AppConfig {
  static const String apiBaseUrl = String.fromEnvironment(
    'API_BASE_URL',
    defaultValue: 'https://api.example.com',
  );
  
  static const bool enableLogging = bool.fromEnvironment(
    'ENABLE_LOGGING',
    defaultValue: false,
  );
}

常見問題

1. CORS 錯(cuò)誤(Web)

在 Web 開發(fā)中,如果遇到 CORS 錯(cuò)誤,需要在后端配置 CORS 頭。

2. 證書錯(cuò)誤(Android)

android/app/src/main/res/xml/network_security_config.xml 中配置:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>
</network-security-config>

3. 網(wǎng)絡(luò)權(quán)限(Android)

AndroidManifest.xml 中添加:

<uses-permission android:name="android.permission.INTERNET"/>

總結(jié)

Flutter 的數(shù)據(jù)調(diào)用和后端交互涉及多個(gè)方面:

  1. 網(wǎng)絡(luò)請求:使用 httpdio
  2. JSON 序列化:手動(dòng)或使用 json_serializable
  3. 數(shù)據(jù)持久化:SharedPreferences、SQLite、文件存儲(chǔ)
  4. 狀態(tài)管理:Provider、FutureBuilder 等
  5. 錯(cuò)誤處理:完善的異常處理機(jī)制
  6. 緩存策略:提高性能和用戶體驗(yàn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容