Flutter原生网络请求适配插件native_dio_adapter的使用

发布于 1周前 作者 h691938207 来自 Flutter

Flutter原生网络请求适配插件native_dio_adapter的使用

Native Dio Adapter

pub package likes popularity pub points

注意: 此插件在macOS、iOS和Android上使用原生HTTP实现。其他平台仍然使用Dart HTTP栈。

native_dio_adapterDio的一个客户端,它利用了cupertino_httpcronet_http,将HTTP请求委托给原生平台而不是dart:io平台。

灵感来源于Dart 2.18发布博客

动机

使用原生平台实现,而非基于套接字的dart:io HttpClient实现,有以下几个优点:

  • 自动支持平台特性,如VPN和HTTP代理。
  • 支持更多的配置选项,例如只允许通过WiFi访问和阻止Cookie。
  • 支持更多HTTP特性,如HTTP/3和自定义重定向处理。

开始使用

安装

在您的pubspec.yaml文件中添加native_dio_adapter包到依赖项中。

dependencies:
  native_dio_adapter: ^latest_version # 替换为最新版本号

示例代码

以下是一个完整的Flutter应用示例,展示了如何使用native_dio_adapter进行GET和POST请求:

// ignore_for_file: use_build_context_synchronously

import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:native_dio_adapter/native_dio_adapter.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  void _doGetRequest() async {
    final dio = Dio();

    dio.httpClientAdapter = NativeAdapter(
      createCupertinoConfiguration: () =>
          URLSessionConfiguration.ephemeralSessionConfiguration()
            ..allowsCellularAccess = false
            ..allowsConstrainedNetworkAccess = false
            ..allowsExpensiveNetworkAccess = false,
    );
    try {
      final response = await dio.get<String>('https://flutter.dev');
      await showDialog<void>(
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text('Response ${response.statusCode}'),
            content: SingleChildScrollView(
              child: Text(response.data ?? 'No response'),
            ),
            actions: [
              MaterialButton(
                onPressed: () => Navigator.pop(context),
                child: const Text('Close'),
              )
            ],
          );
        },
      );
    } catch (e) {
      print(e);
    }
  }

  void _doPostRequest() async {
    final dio = Dio();

    dio.httpClientAdapter = NativeAdapter(
      createCupertinoConfiguration: () =>
          URLSessionConfiguration.ephemeralSessionConfiguration()
            ..allowsCellularAccess = false
            ..allowsConstrainedNetworkAccess = false
            ..allowsExpensiveNetworkAccess = false,
    );
    try {
      final response = await dio.post<String>(
        'https://httpbin.org/post',
        queryParameters: {'foo': 'bar'},
        data: jsonEncode({'foo': 'bar'}),
        options: Options(headers: {'foo': 'bar'}),
      );

      await showDialog<void>(
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text('Response ${response.statusCode}'),
            content: SingleChildScrollView(
              child: Text(response.data ?? 'No response'),
            ),
            actions: [
              MaterialButton(
                onPressed: () => Navigator.pop(context),
                child: const Text('Close'),
              )
            ],
          );
        },
      );
    } catch (e) {
      print(e);
    }
  }

  void _doHttpClientRequest() async {
    final config = URLSessionConfiguration.ephemeralSessionConfiguration()
      ..allowsCellularAccess = false
      ..allowsConstrainedNetworkAccess = false
      ..allowsExpensiveNetworkAccess = false;
    final client = CupertinoClient.fromSessionConfiguration(config);
    try {
      final response = await client.get(Uri.parse('https://flutter.dev/'));
      await showDialog<void>(
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text('Response ${response.statusCode}'),
            content: SingleChildScrollView(
              child: Text(response.body),
            ),
            actions: [
              MaterialButton(
                onPressed: () => Navigator.pop(context),
                child: const Text('Close'),
              )
            ],
          );
        },
      );
    } catch (e) {
      print(e);
    }
  }

  void _doHttpClientPostRequest() async {
    final config = URLSessionConfiguration.ephemeralSessionConfiguration()
      ..allowsCellularAccess = false
      ..allowsConstrainedNetworkAccess = false
      ..allowsExpensiveNetworkAccess = false;
    final client = CupertinoClient.fromSessionConfiguration(config);

    try {
      final response = await client.post(
        Uri.parse('https://httpbin.org/post'),
        body: jsonEncode({'foo': 'bar'}),
      );

      await showDialog<void>(
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text('Response ${response.statusCode}'),
            content: SingleChildScrollView(
              child: Text(response.body),
            ),
            actions: [
              MaterialButton(
                onPressed: () => Navigator.pop(context),
                child: const Text('Close'),
              )
            ],
          );
        },
      );
    } catch (e) {
      print(e);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        mainAxisSize: MainAxisSize.max,
        children: [
          ElevatedButton(
            onPressed: _doGetRequest,
            child: const Text('make get request'),
          ),
          ElevatedButton(
            onPressed: _doPostRequest,
            child: const Text('make post request'),
          ),
          ElevatedButton(
            onPressed: _doHttpClientRequest,
            child: const Text('make client request'),
          ),
          ElevatedButton(
            onPressed: _doHttpClientPostRequest,
            child: const Text('make client post request'),
          ),
        ],
      ),
    );
  }
}

这个示例展示了如何使用native_dio_adapter执行基本的GET和POST请求,并显示响应结果。您可以通过点击按钮来触发这些请求,并在弹出的对话框中查看响应内容。

使用嵌入式Cronet

cronet_http v1.2.0开始,您可以使用嵌入式的Cronet实现,只需简单地配置dart-define。详情请参阅cronet_http文档

希望这个指南能帮助您更好地理解和使用native_dio_adapter!如果您有任何问题或需要进一步的帮助,请随时提问。


更多关于Flutter原生网络请求适配插件native_dio_adapter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter原生网络请求适配插件native_dio_adapter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是关于如何在Flutter项目中使用native_dio_adapter插件来进行原生网络请求适配的示例代码。native_dio_adapter是一个允许Flutter应用通过原生代码(如Android的Java/Kotlin或iOS的Swift/Objective-C)执行网络请求的插件。这通常用于需要更高效的网络操作或处理一些Flutter本身难以处理的复杂网络需求。

1. 添加依赖

首先,在你的pubspec.yaml文件中添加native_dio_adapter依赖:

dependencies:
  flutter:
    sdk: flutter
  dio: ^4.0.0  # 确保dio版本与native_dio_adapter兼容
  native_dio_adapter: ^最新版本号  # 替换为实际最新版本号

然后运行flutter pub get来获取依赖。

2. 配置原生代码

iOS

在iOS项目中,你通常不需要做额外的配置,除非你有特定的网络设置需要在原生代码中处理。

Android

在Android项目中,你可能需要在AndroidManifest.xml中添加网络权限:

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

3. 使用native_dio_adapter进行网络请求

接下来,我们来看如何在Flutter代码中使用native_dio_adapter

初始化Dio实例并配置native_dio_adapter

import 'package:dio/dio.dart';
import 'package:native_dio_adapter/native_dio_adapter.dart';

void main() {
  // 创建Dio实例
  final dio = Dio();

  // 配置native_dio_adapter
  dio.options.transformResponse = NativeDioAdapter.transformResponse;
  dio.options.baseUrl = "https://api.example.com"; // 替换为你的API基础URL

  // 示例:GET请求
  dio.get("/endpoint")
      .then((response) {
        // 处理响应
        print(response.data);
      })
      .catchError((error) {
        // 处理错误
        print(error.response?.data ?? error.message);
      });

  // 示例:POST请求
  final postData = {"key": "value"};
  dio.post("/endpoint", data: postData)
      .then((response) {
        // 处理响应
        print(response.data);
      })
      .catchError((error) {
        // 处理错误
        print(error.response?.data ?? error.message);
      });

  runApp(MyApp());
}

自定义原生网络请求(高级用法)

如果你需要在原生代码中执行更复杂的网络请求,你可以在Android和iOS项目中分别创建自定义的网络请求逻辑,并通过MethodChannel与Flutter通信。以下是一个简单的示例,展示如何在原生代码中处理网络请求,并通过MethodChannel返回结果。

Android(Java/Kotlin)
// MainActivity.java 或 MainActivity.kt
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;

import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class MainActivity extends FlutterActivity {
  private static final String CHANNEL = "com.example.native_network_channel";

  @Override
  public void configureFlutterEngine(FlutterEngine flutterEngine) {
    super.configureFlutterEngine(flutterEngine);
    new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
        .setMethodCallHandler(
            (call, result) -> {
              if (call.method.equals("fetchData")) {
                fetchData(result);
              } else {
                result.notImplemented();
              }
            });
  }

  private void fetchData(MethodChannel.Result result) {
    OkHttpClient client = new OkHttpClient();

    Request request = new Request.Builder()
        .url("https://api.example.com/endpoint")
        .build();

    client.newCall(request).enqueue(new Callback() {
      @Override
      public void onFailure(Call call, IOException e) {
        result.error("NETWORK_ERROR", e.getMessage(), null);
      }

      @Override
      public void onResponse(Call call, Response response) throws IOException {
        if (response.isSuccessful()) {
          result.success(response.body().string());
        } else {
          result.error("HTTP_ERROR", response.message(), null);
        }
      }
    });
  }
}
iOS(Swift/Objective-C)
// AppDelegate.swift
import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)

    let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
    let channel = FlutterMethodChannel(name: "com.example.native_network_channel", binaryMessenger: controller)
    
    channel.setMethodCallHandler({
      (call: FlutterMethodCall, result: @escaping FlutterResult) in
      if call.method == "fetchData" {
        fetchData(result: result)
      } else {
        result(FlutterMethodNotImplemented)
      }
    })
    
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

func fetchData(result: @escaping FlutterResult) {
  let url = URL(string: "https://api.example.com/endpoint")!
  let task = URLSession.shared.dataTask(with: url) { data, response, error in
    if let error = error {
      result(FlutterError(code: "NETWORK_ERROR", message: error.localizedDescription, details: nil))
      return
    }
    
    guard let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 else {
      result(FlutterError(code: "HTTP_ERROR", message: (response as? HTTPURLResponse)?.localizedString ?? "Unknown error", details: nil))
      return
    }
    
    if let jsonString = String(data: data, encoding: .utf8) {
      result(jsonString)
    } else {
      result(FlutterError(code: "PARSING_ERROR", message: "Failed to parse response", details: nil))
    }
  }
  task.resume()
}
Flutter端调用原生方法
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  static const platform = MethodChannel('com.example.native_network_channel');

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Native Network Request'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: _fetchData,
            child: Text('Fetch Data'),
          ),
        ),
      ),
    );
  }

  Future<void> _fetchData() async {
    try {
      final String result = await platform.invokeMethod('fetchData');
      print(result);
    } on PlatformException catch (e) {
      print("Failed to fetch data: '${e.message}'.");
    }
  }
}

这个示例展示了如何通过native_dio_adapter和自定义原生代码来实现网络请求的适配。请注意,native_dio_adapter的具体用法可能会随着版本更新而变化,因此建议查阅最新的官方文档和示例代码。

回到顶部