Flutter 从入门到精通(水)

232 阅读2分钟

第7章:网络请求与本地存储

现代 App 大多数都依赖远程接口(API)获取数据并在本地缓存用户状态持久化数据。本章将教你如何在 Flutter 中完成这些操作。


一、网络请求(使用 http 库)

1. 添加依赖

dependencies:
  http: ^0.13.0

2. 发起 GET 请求

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

Future<void> fetchData() async {
  final url = Uri.parse('https://jsonplaceholder.typicode.com/posts/1');
  final response = await http.get(url);

  if (response.statusCode == 200) {
    final data = json.decode(response.body);
    print(data['title']);
  } else {
    throw Exception('Failed to load data');
  }
}

3. 发起 POST 请求

Future<void> submitData() async {
  final url = Uri.parse('https://example.com/api');
  final response = await http.post(
    url,
    headers: {'Content-Type': 'application/json'},
    body: json.encode({'name': 'Eric', 'age': 25}),
  );

  if (response.statusCode == 200) {
    print('Submitted successfully');
  }
}

4. 解析 JSON 为模型类(推荐)

使用 Dart 类解析 JSON,结构更清晰:

class Post {
  final int id;
  final String title;

  Post({required this.id, required this.title});

  factory Post.fromJson(Map<String, dynamic> json) {
    return Post(
      id: json['id'],
      title: json['title'],
    );
  }
}

使用:

final data = json.decode(response.body);
Post post = Post.fromJson(data);

二、本地存储(轻量级持久化)

1. 使用 SharedPreferences(用于保存小型键值数据)

添加依赖
dependencies:
  shared_preferences: ^2.0.0
存储数据
final prefs = await SharedPreferences.getInstance();
prefs.setInt('counter', 10);
prefs.setString('username', 'Eric');
读取数据
final prefs = await SharedPreferences.getInstance();
int counter = prefs.getInt('counter') ?? 0;
String? username = prefs.getString('username');

2. 使用 SQLite 数据库(适合结构化数据)

添加依赖
dependencies:
  sqflite: ^2.0.0
  path: ^1.8.0
初始化数据库
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

Future<Database> initDB() async {
  final path = join(await getDatabasesPath(), 'demo.db');

  return openDatabase(
    path,
    version: 1,
    onCreate: (db, version) {
      return db.execute(
        'CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT)',
      );
    },
  );
}
插入数据
Future<void> insertUser(Database db, String name) async {
  await db.insert(
    'users',
    {'name': name},
    conflictAlgorithm: ConflictAlgorithm.replace,
  );
}
查询数据
Future<List<Map<String, dynamic>>> getUsers(Database db) async {
  return await db.query('users');
}

三、常见问题解析

❗ 问题 1:网络请求报错(SocketException、CORS)

可能原因:
  • Android 模拟器未连接网络
  • 请求使用了 http://,应改为 https://
  • 请求地址跨域(Web 平台)
解决方案:
  • Android:在 AndroidManifest.xml 中添加网络权限

    <uses-permission android:name="android.permission.INTERNET" />
    
  • Web 跨域:需服务端支持 CORS,或使用代理


❗ 问题 2:json.decode 报错

常见错误:
  • 数据为空或格式不对(返回非 JSON)
  • 接收的是数组而非对象(需区分 ListMap
示例解决:
final List data = json.decode(response.body); // 若返回数组

❗ 问题 3:SharedPreferences 读取不到值

检查点:
  • 写入后是否忘记 await 保存
  • 使用了不同的 key 名称
  • 读取前是否执行过 SharedPreferences.getInstance()

❗ 问题 4:SQLite 插入失败或查询不到数据

检查点:
  • 数据库路径是否正确(推荐使用 join
  • 字段名是否正确
  • 是否使用了正确的 async/await 流程