当前位置:首页 > 技术 > 正文内容

Flutter 应用网络通信全解析:从入门到高级实践

访客 技术 2026年7月14日 1

掌握 Flutter 中的网络请求技术

在现代跨平台应用开发中,与后端服务的数据交互是核心功能之一。Flutter 通过 Dart 语言强大的异步支持,为开发者提供了高效、灵活的网络通信能力。本文将系统介绍如何在 Flutter 项目中实现 HTTP 请求、处理响应数据,并结合实际场景构建健壮的网络层架构。

理解基础机制

Flutter 的网络操作依赖于 dart:io 包,底层封装了 Android 和 iOS 原生的网络栈(如 OkHttp 和 NSURLSession),支持标准的 HTTP/HTTPS 协议以及 WebSocket 实时通信。所有网络调用均为异步执行,避免阻塞主线程影响用户体验。

关键概念包括:

  • Future 与 async/await:用于管理异步任务,确保 UI 流畅;
  • 请求结构:包含 URL、方法(GET、POST 等)、头部信息和可选的请求体;
  • 响应处理:根据状态码判断结果,常见如 200 表示成功,401 表示未授权,500 表示服务器错误;
  • JSON 数据转换:服务器返回的数据通常为 JSON 格式,需映射为 Dart 模型类。

权限配置说明

Android 平台需要在 android/app/src/main/AndroidManifest.xml 文件中添加互联网访问权限:

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

iOS 默认允许网络请求,无需额外配置,但若使用 HTTP 明文地址,需在 Info.plist 中配置 App Transport Security 例外。

主流网络库对比与实战

目前最常用的两种方案是官方 http 包和社区广泛采用的 Dio 库,各有适用场景。

1. 使用 http 包进行轻量级请求

该包由 Flutter 团队维护,API 简洁清晰,适合小型项目或学习阶段使用。

首先在 pubspec.yaml 添加依赖:

dependencies:
  http: ^0.13.6

定义模型类以解析返回数据:

class Article {
  final int id;
  final String title;
  final String content;

  Article({required this.id, required this.title, required this.content});

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

发起 GET 请求获取文章列表:

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

Future<List<Article>> loadArticles() async {
  final uri = Uri.parse('https://jsonplaceholder.typicode.com/posts');
  final response = await http.get(uri);

  if (response.statusCode == 200) {
    final List<dynamic> rawList = json.decode(response.body);
    return rawList.map((item) => Article.fromJson(item)).toList();
  } else {
    throw Exception('Failed to fetch data: ${response.statusCode}');
  }
}

提交数据使用 POST 方法:

Future<Article> submitArticle(String title, String content) async {
  final uri = Uri.parse('https://jsonplaceholder.typicode.com/posts');
  final body = json.encode({
    'title': title,
    'body': content,
    'userId': 1
  });

  final response = await http.post(
    uri,
    headers: {'Content-Type': 'application/json'},
    body: body,
  );

  if (response.statusCode == 201) {
    return Article.fromJson(json.decode(response.body));
  } else {
    throw Exception('Submission failed');
  }
}

2. Dio:构建企业级网络层

Dio 提供更丰富的特性,适用于复杂业务需求,如拦截器、文件传输、请求取消等。

添加依赖:

dependencies:
  dio: ^5.4.0

初始化全局实例并设置默认参数:

import 'package:dio/dio.dart';

final Dio apiClient = Dio(
  BaseOptions(
    baseUrl: 'https://jsonplaceholder.typicode.com',
    connectTimeout: const Duration(seconds: 10),
    receiveTimeout: const Duration(seconds: 5),
    headers: {'Content-Type': 'application/json'},
  ),
);

添加日志和认证拦截器:

// 日志输出
apiClient.interceptors.add(LogInterceptor(requestBody: true, responseBody: true));

// 自动附加 Token
apiClient.interceptors.add(InterceptorsWrapper(
  onRequest: (options, handler) {
    const String token = 'your-auth-token';
    options.headers['Authorization'] = 'Bearer $token';
    handler.next(options);
  },
  onError: (DioException error, handler) {
    if (error.response?.statusCode == 401) {
      // 处理登录过期,跳转至登录页
    }
    handler.reject(error);
  },
));

简化请求调用:

Future<List<Article>> getArticles() async {
  final res = await apiClient.get('/posts');
  return List<dynamic>.from(res.data).map((e) => Article.fromJson(e)).toList();
}

Future<Article> createArticle(String title, String content) async {
  final res = await apiClient.post('/posts', data: {
    'title': title,
    'body': content,
    'userId': 1
  });
  return Article.fromJson(res.data);
}

支持文件上传与进度监控:

Future<String> uploadImage(String path) async {
  final formData = FormData.fromMap({
    'file': await MultipartFile.fromFile(path, filename: 'avatar.jpg')
  });

  final result = await apiClient.post('/upload', data: formData);
  return result.data['url'];
}

// 下载文件并监听进度
Future<void> downloadFile(String url, String savePath) async {
  await apiClient.download(
    url,
    savePath,
    onReceiveProgress: (received, total) {
      final percent = (received / total * 100).toStringAsFixed(1);
      print('Progress: $percent%');
    },
  );
}

实现请求可取消机制:

final cancelToken = CancelToken();

// 发起带取消能力的请求
apiClient.get('/posts', cancelToken: cancelToken)
  .then((res) => print(res.data))
  .catchError((err) {
    if (CancelToken.isCancel(err)) {
      print('Request was canceled');
    }
  });

// 在页面销毁时调用
cancelToken.cancel();

构建可复用的服务层

为了提升代码组织性和可维护性,建议封装统一的 API 接口类:

class NetworkService {
  static final Dio _client = Dio(BaseOptions(baseUrl: 'https://jsonplaceholder.typicode.com'));

  static void setupInterceptors() {
    _client.interceptors
      ..add(LogInterceptor(requestBody: true))
      ..add(AuthInterceptor());
  }

  static Future<T> get<T>(String endpoint, {Map<String, dynamic>? query}) async {
    try {
      final resp = await _client.get(endpoint, queryParameters: query);
      return resp.data as T;
    } on DioException catch (e) {
      _handleFailure(e);
      rethrow;
    }
  }

  static Future<T> post<T>(String endpoint, {dynamic payload}) async {
    try {
      final resp = await _client.post(endpoint, data: payload);
      return resp.data as T;
    } on DioException catch (e) {
      _handleFailure(e);
      rethrow;
    }
  }

  static void _handleFailure(DioException e) {
    switch (e.type) {
      case DioExceptionType.connectionTimeout:
        throw TimeoutException('Network timeout');
      case DioExceptionType.badResponse:
        throw HttpException('Server error: ${e.response?.statusCode}');
      case DioExceptionType.cancel:
        throw CanceledException('Request canceled');
      default:
        throw NetworkException('Network error occurred');
    }
  }
}

// 自定义异常类
class NetworkException implements Exception {}
class CanceledException implements Exception {
  final String message;
  CanceledException(this.message);
}
class HttpException implements Exception {
  final int? code;
  HttpException(this.code);
}
class TimeoutException implements Exception {
  final String message;
  TimeoutException(this.message);
}

连接 UI 与数据流

利用 FutureBuilder 可直接在界面中响应异步状态变化:

FutureBuilder<List<Article>>(
  future: NetworkService.get<List<dynamic>>('/posts').then((list) =>
      list.map((e) => Article.fromJson(e)).toList()),
  builder: (ctx, snapshot) {
    if (snapshot.hasData) {
      final articles = snapshot.data!;
      return ListView.builder(
        itemCount: articles.length,
        itemBuilder: (ctx, index) => ListTile(
          title: Text(articles[index].title),
          subtitle: Text(articles[index].content),
        ),
      );
    } else if (snapshot.hasError) {
      return Center(child: Text('Error: ${snapshot.error}'));
    } else {
      return const Center(child: CircularProgressIndicator());
    }
  },
)

性能优化与安全建议

  • 启用 HTTPS:生产环境必须使用加密连接;
  • 引入缓存:对静态资源使用 dio_cache_interceptor 减少重复请求;
  • 设置重试策略:弱网环境下自动重试失败请求,可用 dio_retry 插件;
  • 敏感信息保护:使用 flutter_secure_storage 安全存储 Token;
  • 大文件分片处理:上传下载大文件时采用分块传输,防止内存溢出。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。