flutter-use-http-package

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Implementing Flutter Networking

实现Flutter网络请求

Contents

目录

Configuration & Permissions

配置与权限

Configure the environment and platform-specific permissions required for network access.
  1. Add the
    http
    package dependency via the terminal:
    bash
    flutter pub add http
  2. Import the package in your Dart files:
    dart
    import 'package:http/http.dart' as http;
  3. Configure Android permissions by adding the Internet permission to
    android/app/src/main/AndroidManifest.xml
    :
    xml
    <uses-permission android:name="android.permission.INTERNET" />
  4. Configure macOS entitlements by adding the network client key to both
    macos/Runner/DebugProfile.entitlements
    and
    macos/Runner/Release.entitlements
    :
    xml
    <key>com.apple.security.network.client</key>
    <true/>
配置网络访问所需的环境和平台特定权限。
  1. 通过终端添加
    http
    包依赖:
    bash
    flutter pub add http
  2. 在Dart文件中导入该包:
    dart
    import 'package:http/http.dart' as http;
  3. 配置Android权限,在
    android/app/src/main/AndroidManifest.xml
    中添加互联网权限:
    xml
    <uses-permission android:name="android.permission.INTERNET" />
  4. 配置macOS权限,在
    macos/Runner/DebugProfile.entitlements
    macos/Runner/Release.entitlements
    中添加网络客户端密钥:
    xml
    <key>com.apple.security.network.client</key>
    <true/>

Request Execution & Response Handling

请求执行与响应处理

Execute HTTP operations and map responses to strongly typed Dart objects.
  • URIs: Always parse URL strings using
    Uri.parse('your_url')
    .
  • Headers: Inject authorization and content-type headers via the
    headers
    parameter map. Use
    HttpHeaders.authorizationHeader
    for auth tokens.
  • Payloads: For POST and PUT requests, encode the body using
    jsonEncode()
    from
    dart:convert
    .
  • Status Validation: Evaluate
    response.statusCode
    . Treat
    200 OK
    (GET/PUT/DELETE) and
    201 CREATED
    (POST) as success.
  • Error Handling: Throw explicit exceptions for non-success status codes. Never return
    null
    on failure, as this prevents
    FutureBuilder
    from triggering its error state and causes infinite loading indicators.
  • Deserialization: Parse the raw string using
    jsonDecode(response.body)
    and map it to a custom Dart object using a factory constructor (e.g.,
    fromJson
    ).
执行HTTP操作并将响应映射为强类型Dart对象。
  • URI: 始终使用
    Uri.parse('your_url')
    解析URL字符串。
  • 请求头: 通过
    headers
    参数注入授权和内容类型请求头。使用
    HttpHeaders.authorizationHeader
    处理认证令牌。
  • 负载: 对于POST和PUT请求,使用
    dart:convert
    中的
    jsonEncode()
    编码请求体。
  • 状态验证: 评估
    response.statusCode
    。将
    200 OK
    (GET/PUT/DELETE)和
    201 CREATED
    (POST)视为成功状态。
  • 错误处理: 针对非成功状态码抛出明确的异常。失败时绝不要返回
    null
    ,这会导致
    FutureBuilder
    无法触发错误状态,进而引发无限加载指示器问题。
  • 反序列化: 使用
    jsonDecode(response.body)
    解析原始字符串,并通过工厂构造函数(如
    fromJson
    )将其映射为自定义Dart对象。

Background Parsing

后台解析

Offload expensive JSON parsing to a separate Isolate to prevent UI jank (frame drops).
  • Import
    package:flutter/foundation.dart
    .
  • Use the
    compute()
    function to run the parsing logic in a background isolate.
  • Ensure the parsing function passed to
    compute()
    is a top-level function or a static method, as closures or instance methods cannot be passed across isolates.
将耗时的JSON解析任务卸载到单独的Isolate中,以避免UI卡顿(掉帧)。
  • 导入
    package:flutter/foundation.dart
  • 使用
    compute()
    函数在后台Isolate中运行解析逻辑。
  • 确保传递给
    compute()
    的解析函数是顶层函数或静态方法,因为闭包或实例方法无法跨Isolate传递。

Workflow: Executing Network Operations

工作流程:执行网络操作

Use the following checklist to implement and validate network operations.
Task Progress:
  • 1. Define the strongly typed Dart model with a
    fromJson
    factory constructor.
  • 2. Implement the network request method returning a
    Future<Model>
    .
  • 3. Apply conditional logic based on the operation type:
    • If fetching data (GET): Append query parameters to the URI.
    • If mutating data (POST/PUT): Set
      'Content-Type': 'application/json; charset=UTF-8'
      and attach the
      jsonEncode
      body.
    • If deleting data (DELETE): Return an empty model instance on success (
      200 OK
      ).
  • 4. Validate the
    statusCode
    and throw an
    Exception
    on failure.
  • 5. Integrate the
    Future
    into the UI using
    FutureBuilder
    .
  • 6. Handle
    snapshot.hasData
    ,
    snapshot.hasError
    , and default to a
    CircularProgressIndicator
    .
  • 7. Feedback Loop: Run the app -> trigger the network request -> review console for unhandled exceptions -> fix parsing or permission errors.
使用以下清单实现并验证网络操作。
任务进度:
  • 1. 定义带有
    fromJson
    工厂构造函数的强类型Dart模型。
  • 2. 实现返回
    Future<Model>
    的网络请求方法。
  • 3. 根据操作类型应用条件逻辑:
    • 如果是获取数据(GET): 向URI追加查询参数。
    • 如果是修改数据(POST/PUT): 设置
      'Content-Type': 'application/json; charset=UTF-8'
      并附加
      jsonEncode
      处理后的请求体。
    • 如果是删除数据(DELETE): 成功时(
      200 OK
      )返回空模型实例。
  • 4. 验证
    statusCode
    ,失败时抛出
    Exception
  • 5. 使用
    FutureBuilder
    Future
    集成到UI中。
  • 6. 处理
    snapshot.hasData
    snapshot.hasError
    状态,默认显示
    CircularProgressIndicator
  • 7. 反馈循环: 运行应用 -> 触发网络请求 -> 查看控制台中的未处理异常 -> 修复解析或权限错误。

Examples

示例

High-Fidelity Implementation: Fetching and Parsing in the Background

高保真实现:后台获取与解析

dart
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

// 1. Top-level parsing function for Isolate
List<Photo> parsePhotos(String responseBody) {
  final parsed = (jsonDecode(responseBody) as List<Object?>)
      .cast<Map<String, Object?>>();
  return parsed.map<Photo>(Photo.fromJson).toList();
}

// 2. Network execution with background parsing
Future<List<Photo>> fetchPhotos() async {
  final response = await http.get(
    Uri.parse('https://jsonplaceholder.typicode.com/photos'),
    headers: {
      HttpHeaders.authorizationHeader: 'Bearer your_token_here',
      HttpHeaders.acceptHeader: 'application/json',
    },
  );

  if (response.statusCode == 200) {
    // Offload heavy parsing to a background isolate
    return compute(parsePhotos, response.body);
  } else {
    throw Exception('Failed to load photos. Status: ${response.statusCode}');
  }
}
}

// 3. Strongly typed model
class Photo {
  final int id;
  final String title;
  final String thumbnailUrl;

  const Photo({
    required this.id,
    required this.title,
    required this.thumbnailUrl,
  });

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

// 4. UI Integration
class PhotoGallery extends StatefulWidget {
  const PhotoGallery({super.key});

  
  State<PhotoGallery> createState() => _PhotoGalleryState();
}

class _PhotoGalleryState extends State<PhotoGallery> {
  late Future<List<Photo>> _futurePhotos;

  
  void initState() {
    super.initState();
    // Initialize Future once to prevent re-fetching on rebuilds
    _futurePhotos = fetchPhotos();
  }

  
  Widget build(BuildContext context) {
    return FutureBuilder<List<Photo>>(
      future: _futurePhotos,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          final photos = snapshot.data!;
          return ListView.builder(
            itemCount: photos.length,
            itemBuilder: (context, index) => ListTile(
              leading: Image.network(photos[index].thumbnailUrl),
              title: Text(photos[index].title),
            ),
          );
        } else if (snapshot.hasError) {
          return Center(child: Text('Error: ${snapshot.error}'));
        }
        
        // Default loading state
        return const Center(child: CircularProgressIndicator());
      },
    );
  }
}
dart
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

// 1. 用于Isolate的顶层解析函数
List<Photo> parsePhotos(String responseBody) {
  final parsed = (jsonDecode(responseBody) as List<Object?>)
      .cast<Map<String, Object?>>();
  return parsed.map<Photo>(Photo.fromJson).toList();
}

// 2. 带后台解析的网络执行
Future<List<Photo>> fetchPhotos() async {
  final response = await http.get(
    Uri.parse('https://jsonplaceholder.typicode.com/photos'),
    headers: {
      HttpHeaders.authorizationHeader: 'Bearer your_token_here',
      HttpHeaders.acceptHeader: 'application/json',
    },
  );

  if (response.statusCode == 200) {
    // 将繁重的解析任务卸载到后台Isolate
    return compute(parsePhotos, response.body);
  } else {
    throw Exception('Failed to load photos. Status: ${response.statusCode}');
  }
}
}

// 3. 强类型模型
class Photo {
  final int id;
  final String title;
  final String thumbnailUrl;

  const Photo({
    required this.id,
    required this.title,
    required this.thumbnailUrl,
  });

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

// 4. UI集成
class PhotoGallery extends StatefulWidget {
  const PhotoGallery({super.key});

  
  State<PhotoGallery> createState() => _PhotoGalleryState();
}

class _PhotoGalleryState extends State<PhotoGallery> {
  late Future<List<Photo>> _futurePhotos;

  
  void initState() {
    super.initState();
    // 初始化Future一次,避免重建时重新请求
    _futurePhotos = fetchPhotos();
  }

  
  Widget build(BuildContext context) {
    return FutureBuilder<List<Photo>>(
      future: _futurePhotos,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          final photos = snapshot.data!;
          return ListView.builder(
            itemCount: photos.length,
            itemBuilder: (context, index) => ListTile(
              leading: Image.network(photos[index].thumbnailUrl),
              title: Text(photos[index].title),
            ),
          );
        } else if (snapshot.hasError) {
          return Center(child: Text('Error: ${snapshot.error}'));
        }
        
        // 默认加载状态
        return const Center(child: CircularProgressIndicator());
      },
    );
  }
}