Flutter网络代理选择插件proxy_selector的使用

Flutter网络代理选择插件proxy_selector的使用

该插件允许您为特定的URL检索代理设置。

目前支持iOS和Android平台。

在Android上,它使用了java.net包中的ProxySelector。 在iOS上,它使用了CFNetworkCopySystemProxySettings/CFNetworkCopyProxiesForURL来解析代理设置,并且对于PAC(脚本/Url)使用了CFNetworkExecuteProxyAutoConfigurationURL/CFNetworkExecuteProxyAutoConfigurationScript来解析PAC脚本。

注意:FTP(仅限iOS)和SOCKS尚未测试。iOS上的凭证不支持。

如何测试

您可以使用自己的代理服务器或任何其他代理工具进行测试。这里以Proxyman为例:

  1. 安装Proxyman
  2. 配置您的物理设备以使用Proxyman,目的是能够跟踪所有流量
  3. 打开浏览器并输入一些随机地址
  4. Proxyman会显示所有请求
  5. 在您的设备上运行示例项目
  6. 执行输入的URL
  7. Proxyman中没有显示代理
  8. 启用代理
  9. 再次执行输入的URL
  10. 显示代理设置
测试PAC

您需要在您的设备上安装nginx,并且有一个PAC文件。为了方便设置,可以使用指向Proxyman代理的PAC文件。

function FindProxyForURL (url, host) {
    // return 'PROXY 1111.1111.1111.1111:9999; DIRECT';
    return 'PROXY [Enter here ip address of Proxyman and port]; DIRECT';
}

并且配置nginx.conf文件,将根目录设置为包含pac文件的目录。

events {}
http {
    include       mime.types;
    default_type   application/x-ns-proxy-autoconfig;

    server {
        location / {
            # path to root where testfile.pac located
            root /../../../;
        }
    }
}

完成上述步骤后,重新加载或启动nginx。您的PAC现在应该可以通过本地IP地址(localhost)访问。请注意,返回的文件应具有application/x-ns-proxy-autoconfig MIME类型。

在设备的代理设置中输入您的IP地址作为URL。之后,您可以使用示例测试PAC。

Example:
1111.1111.1111.1111/testfile.pac

源码

以下来源用于实现此插件。

iOS:
Android:

示例代码

以下是使用proxy_selector插件的完整示例代码:

import 'package:dio/dio.dart';
import 'package:dio_proxy_adapter/dio_proxy_adapter.dart';
import 'package:flutter/material.dart';
import 'dart:async';

import 'package:flutter/services.dart';
import 'package:proxy_selector/models/proxy_dto.dart';
import 'package:proxy_selector/proxy_selector.dart';

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

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

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  TextEditingController textEditingController = TextEditingController();
  String _proxy = '未知';
  String _response = "-";
  String? _error;
  String? _timeForExecution;
  bool _activateProxy = false;
  ProxySelector _proxyPlugin = ProxySelector();

  [@override](/user/override)
  void initState() {
    super.initState();
    textEditingController.text = "https://example.com";
  }

  // 平台消息是异步的,所以我们初始化在一个异步方法中。
  Future<void> _buttonCall() async {
    _proxyPlugin = ProxySelector();

    String? proxyAsList = "-";
    Response? response;
    String? error;
    String? timeForExecution;

    if (!mounted) return;

    setState(() {
      _proxy = proxyAsList ?? "-";
      _response = response?.data.toString() ?? "-";
      _error = error;
    });

    // 平台消息可能会失败,所以我们使用try/catch来捕获PlatformException。
    // 我们还处理消息可能返回null的情况。

    Dio dio = Dio();
    try {
      final address = Uri.tryParse(textEditingController.text);

      if (!(address != null)) {
        error = "无法解析您的输入";
      } else {
        final stopwatch = Stopwatch()..start();
        final proxyForThisURL = _activateProxy
            ? (await _proxyPlugin.getSystemProxyForUri(address))
            : null;
        timeForExecution =
            _activateProxy ? stopwatch.elapsed.inMilliseconds.toString() : null;
        stopwatch.stop();

        if (proxyForThisURL != null && proxyForThisURL.isNotEmpty) {
          proxyAsList = proxyForThisURL.join();
          List<ProxyDto> proxies = List.from(proxyForThisURL);
          proxies.removeWhere((element) => element.type == "NONE");
          if (proxies.isNotEmpty) {
            dio.useProxy("${proxies.first.host}:${proxies.first.port}");
          }
        } else {
          error = "未找到代理";
        }

        response = await dio.get(
          address.toString(),
        );
      }
    } on PlatformException {
      _error = "获取代理失败。";
    } on DioError {
      _error = "请求失败";
    }

    // 如果小部件从树中被移除而异步平台消息还在飞行中,我们希望丢弃回复而不是调用setState来更新我们的不存在的外观。
    if (!mounted) return;

    setState(() {
      _proxy = proxyAsList ?? "-";
      _response = response?.data.toString() ?? "-";
      _timeForExecution = timeForExecution ?? "-";
      _error = error;
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('代理'),
          backgroundColor: Colors.green,
          actions: [
            const Text("代理开启: "),
            Switch(
              value: _activateProxy,
              onChanged: (value) {
                setState(() {
                  _activateProxy = value;
                });
              },
            )
          ],
        ),
        body: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            TextField(
              controller: textEditingController,
            ),
            Text(_proxy),
            Text("获取代理所需时间: $_timeForExecution ms"),
            if (_error != null && _error!.isNotEmpty)
              Text(
                _error!,
                style: const TextStyle(color: Colors.red),
              ),
            OutlinedButton(
                onPressed: _buttonCall,
                child: const Text("获取代理并调用")),
            Expanded(
              child: SingleChildScrollView(
                child: Text(_response),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

更多关于Flutter网络代理选择插件proxy_selector的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter网络代理选择插件proxy_selector的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在 Flutter 中,proxy_selector 是一个用于选择网络代理的插件。它可以帮助你在应用中动态地选择和配置网络代理,以便在不同的网络环境下进行调试或访问特定的网络资源。以下是如何使用 proxy_selector 插件的基本步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 proxy_selector 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  proxy_selector: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来安装依赖。

2. 导入插件

在你的 Dart 文件中导入 proxy_selector 插件:

import 'package:proxy_selector/proxy_selector.dart';

3. 设置代理

你可以使用 ProxySelector 类来设置代理。以下是一个简单的示例,展示如何设置 HTTP 和 HTTPS 代理:

void setProxy() {
  ProxySelector().setProxy(
    ProxySettings(
      httpProxy: 'http://proxy.example.com:8080',
      httpsProxy: 'https://proxy.example.com:8080',
      bypass: ['localhost', '127.0.0.1'],
    ),
  );
}

在上面的代码中,httpProxyhttpsProxy 分别设置了 HTTP 和 HTTPS 代理的地址和端口。bypass 参数用于指定不需要通过代理的地址。

4. 清除代理

如果你想清除之前设置的代理,可以调用 clearProxy 方法:

void clearProxy() {
  ProxySelector().clearProxy();
}

5. 获取当前代理设置

你可以通过 getProxySettings 方法来获取当前的代理设置:

void getCurrentProxy() async {
  ProxySettings? settings = await ProxySelector().getProxySettings();
  if (settings != null) {
    print('HTTP Proxy: ${settings.httpProxy}');
    print('HTTPS Proxy: ${settings.httpsProxy}');
    print('Bypass: ${settings.bypass}');
  } else {
    print('No proxy settings.');
  }
}

6. 处理代理设置的变化

proxy_selector 插件还提供了监听代理设置变化的功能。你可以通过 onProxyChanged 来监听代理设置的变更:

void listenProxyChanges() {
  ProxySelector().onProxyChanged.listen((ProxySettings settings) {
    print('Proxy settings changed:');
    print('HTTP Proxy: ${settings.httpProxy}');
    print('HTTPS Proxy: ${settings.httpsProxy}');
    print('Bypass: ${settings.bypass}');
  });
}

7. 示例代码

以下是一个完整的示例代码,展示了如何使用 proxy_selector 插件来设置、获取和监听代理设置:

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

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Proxy Selector Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: setProxy,
                child: Text('Set Proxy'),
              ),
              ElevatedButton(
                onPressed: clearProxy,
                child: Text('Clear Proxy'),
              ),
              ElevatedButton(
                onPressed: getCurrentProxy,
                child: Text('Get Current Proxy'),
              ),
            ],
          ),
        ),
      ),
    );
  }

  void setProxy() {
    ProxySelector().setProxy(
      ProxySettings(
        httpProxy: 'http://proxy.example.com:8080',
        httpsProxy: 'https://proxy.example.com:8080',
        bypass: ['localhost', '127.0.0.1'],
      ),
    );
  }

  void clearProxy() {
    ProxySelector().clearProxy();
  }

  void getCurrentProxy() async {
    ProxySettings? settings = await ProxySelector().getProxySettings();
    if (settings != null) {
      print('HTTP Proxy: ${settings.httpProxy}');
      print('HTTPS Proxy: ${settings.httpsProxy}');
      print('Bypass: ${settings.bypass}');
    } else {
      print('No proxy settings.');
    }
  }
}
回到顶部