0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Flutter アプリでシステムの手動プロキシ設定を自動検知・適用する(Android/iOS)

0
Last updated at Posted at 2026-03-04

Flutter アプリでシステムの手動プロキシ設定を自動検知・適用する(Android/iOS)

はじめに

企業向け Flutter アプリを開発していると、社内ネットワークのプロキシ設定に対応する必要があります。
本記事では、デバイスに設定された手動プロキシ(固定の host:port)を自動検知し、Flutter アプリの HTTP 通信に適用する方法を解説します。

Flutter の標準 HTTP クライアント (dart:ioHttpClient) はデフォルトではシステムのプロキシ設定を参照しません。
そのため、ネイティブ側でプロキシ設定を取得し、Flutter 側に渡す仕組みを自前で実装する必要があります。


アーキテクチャ

MethodChannel を使ってネイティブ(Android/iOS)側のプロキシ設定を Dart 側に橋渡しします。

┌─────────────────────────────────────┐
│           Flutter (Dart)            │
│                                     │
│  ProxySettingsHelper                │
│    ↓ MethodChannel 呼び出し          │
│  HttpOverrides.global に適用         │
│    → dart:io HttpClient がプロキシ使用│
└──────────────┬──────────────────────┘
               │ com.example/proxy
   ┌───────────┴───────────┐
   │                       │
   ▼                       ▼
Android (Kotlin)        iOS (Swift)
MainActivity.kt         AppDelegate.swift
ConnectivityManager     CFNetworkCopy
.defaultProxy           SystemProxySettings()

Android 実装(Kotlin)

プロキシの検知

ConnectivityManager.defaultProxy を使ってシステムのプロキシ設定を取得します。

// MainActivity.kt
private fun detectProxyConfig(): ProxyConfig? {
    return try {
        val connectivityManager =
            getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager
                ?: return null

        val proxy = connectivityManager.defaultProxy ?: return null

        if (!proxy.host.isNullOrEmpty() && proxy.port != -1) {
            ProxyConfig(proxy.host, proxy.port)
        } else {
            null
        }
    } catch (e: Exception) {
        Log.e(TAG, "Error detecting proxy configuration", e)
        null
    }
}

JVM システムプロパティへの適用

取得したプロキシ情報を JVM のシステムプロパティに設定します。
これにより OkHttp や AWS Amplify 等のネイティブ SDK も自動でプロキシを使用するようになります。

private fun applyProxyProperties(host: String, port: String) {
    System.setProperty("http.proxyHost", host)
    System.setProperty("http.proxyPort", port)
    System.setProperty("https.proxyHost", host)
    System.setProperty("https.proxyPort", port)
}

private fun clearProxyProperties() {
    System.clearProperty("http.proxyHost")
    System.clearProperty("http.proxyPort")
    System.clearProperty("https.proxyHost")
    System.clearProperty("https.proxyPort")
}

アプリ起動時の自動適用

configureFlutterEngine でアプリ起動時にプロキシを自動検知・適用します。

override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)

    applySystemProxyIfAvailable()
    setupMethodChannel(flutterEngine)
}

private fun applySystemProxyIfAvailable() {
    val proxyConfig = detectProxyConfig()
    if (proxyConfig != null) {
        applyProxyProperties(proxyConfig.host, proxyConfig.port.toString())
        Log.i(TAG, "Auto-detected and set system proxy to ${proxyConfig.host}:${proxyConfig.port}")
    } else {
        clearProxyProperties()
    }
}

MethodChannel のセットアップ

Dart 側からも呼び出せるよう MethodChannel を実装します。

private fun setupMethodChannel(flutterEngine: FlutterEngine) {
    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
        .setMethodCallHandler { call, result ->
            when (call.method) {
                METHOD_GET_PROXY_SETTING -> {
                    result.success(getProxySetting())
                }
                METHOD_APPLY_SYSTEM_PROXY_IF_AVAILABLE -> {
                    applySystemProxyIfAvailable()
                    result.success(null)
                }
                else -> result.notImplemented()
            }
        }
}

private fun getProxySetting(): String? {
    return detectProxyConfig()?.let { "${it.host}:${it.port}" }
}

iOS 実装(Swift)

プロキシの検知

CFNetworkCopySystemProxySettings() でシステムのプロキシ設定を取得します。

// ProxyManager.swift
import Foundation
import SystemConfiguration

class ProxyManager {
    static let shared = ProxyManager()

    private init() {}

    func getProxyString() -> String? {
        guard let settings = fetchProxySettings(),
              let host = settings[kCFNetworkProxiesHTTPProxy as String] as? String,
              let port = settings[kCFNetworkProxiesHTTPPort as String] as? NSNumber else {
            return nil
        }
        return "\(host):\(port)"
    }

    private func fetchProxySettings() -> [String: Any]? {
        return CFNetworkCopySystemProxySettings()?.takeUnretainedValue() as? [String: Any]
    }

    private func hasProxy(settings: [String: Any]) -> Bool {
        return settings[kCFNetworkProxiesHTTPProxy as String] != nil &&
               settings[kCFNetworkProxiesHTTPPort as String] != nil
    }
}

URLSession への適用

取得したプロキシ設定を URLSessionConfiguration.default.connectionProxyDictionary に設定します。

func configure() {
    if let settings = fetchProxySettings(), hasProxy(settings: settings) {
        applyProxyConfiguration(settings: settings)
    } else {
        clearProxyConfiguration()
    }
}

private func applyProxyConfiguration(settings: [String: Any]) {
    guard let host = settings[kCFNetworkProxiesHTTPProxy as String] as? String,
          let port = settings[kCFNetworkProxiesHTTPPort as String] as? NSNumber else {
        return
    }

    URLSessionConfiguration.default.connectionProxyDictionary = [
        kCFNetworkProxiesHTTPEnable: true,
        kCFNetworkProxiesHTTPProxy: host,
        kCFNetworkProxiesHTTPPort: port
    ]
    print("Proxy configured: \(host):\(port)")
}

private func clearProxyConfiguration() {
    URLSessionConfiguration.default.connectionProxyDictionary = nil
}

AppDelegate での初期化と MethodChannel

// AppDelegate.swift
override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
    GeneratedPluginRegistrant.register(with: self)

    // 起動時にプロキシを適用
    ProxyManager.shared.configure()

    setupMethodChannel()
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

private func handleMethodCall(call: FlutterMethodCall, result: @escaping FlutterResult) {
    switch call.method {
    case "getProxySetting":
        result(ProxyManager.shared.getProxyString())
    case "applySystemProxyIfAvailable":
        ProxyManager.shared.configure()
        result(nil)
    default:
        result(FlutterMethodNotImplemented)
    }
}

Dart 実装

HttpProxyOverrides

dart:ioHttpClient にプロキシを適用するためのクラスです。
デバッグ・プロファイルモードでは証明書エラーを無視する設定も含めています。

// http_proxy_overrides.dart
import 'dart:io';
import 'package:flutter/foundation.dart';

class HttpProxyOverrides extends HttpOverrides {
  HttpProxyOverrides({this.proxyString});

  /// プロキシの URL(例: "192.168.1.100:8080")
  final String? proxyString;

  @override
  HttpClient createHttpClient(SecurityContext? context) {
    final client = super.createHttpClient(context);

    if (kDebugMode || kProfileMode) {
      // 開発環境では自己署名証明書を許可
      client.badCertificateCallback = (cert, host, port) => true;
    }

    if (proxyString != null && proxyString!.isNotEmpty) {
      client.findProxy = (uri) => 'PROXY $proxyString; DIRECT';
    }
    return client;
  }
}

ProxySettingsHelper

ネイティブ側と通信してプロキシ設定を取得・適用するヘルパークラスです。

// proxy_settings_helper.dart
import 'dart:io';
import 'package:flutter/services.dart';

class ProxySettingsHelper {
  ProxySettingsHelper({
    MethodChannel? methodChannel,
  }) : _methodChannel =
           methodChannel ?? const MethodChannel('com.example/proxy');

  final MethodChannel _methodChannel;

  /// システムプロキシ設定を検出して適用します
  Future<void> detectAndApplyProxySettings() async {
    try {
      final proxyInfo = await _methodChannel
          .invokeMethod<String?>('getProxySetting');

      if (proxyInfo != null && proxyInfo.isNotEmpty) {
        HttpOverrides.global = HttpProxyOverrides(proxyString: proxyInfo);
        debugPrint('Auto-detected and set HttpOverrides proxy: $proxyInfo');
      } else {
        HttpOverrides.global = null;
        debugPrint('No proxy detected');
      }
    } catch (e, stackTrace) {
      // do something
    }
  }
}

main.dart での初期化

アプリ起動時にプロキシ設定を検知・適用します。

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final proxySettingsHelper = ProxySettingsHelper();
  await proxySettingsHelper.detectAndApplyProxySettings();

  runApp(const MyApp());
}

動作の仕組みまとめ

Android

  1. ConnectivityManager.defaultProxy でシステムプロキシを取得
  2. JVM のシステムプロパティ(http.proxyHost / http.proxyPort)に設定
  3. MethodChannel 経由で Dart 側に "host:port" を渡す
  4. HttpOverrides.globalHttpProxyOverrides を設定
  5. dart:ioHttpClient(Dio・graphql_flutter 等)がプロキシを通る

iOS

  1. CFNetworkCopySystemProxySettings() でプロキシ設定を取得
  2. URLSessionConfiguration.default.connectionProxyDictionary に設定
  3. MethodChannel 経由で Dart 側に "host:port" を渡す
  4. HttpOverrides.globalHttpProxyOverrides を設定

制限事項

PAC(自動プロキシ)には対応できない

CFNetworkCopySystemProxySettings() はプロキシの種別が「自動」の場合、
kCFNetworkProxiesHTTPProxy / Port を返さず、PAC ファイルの URL のみを返します。
そのため hasProxy()false となり、プロキシは適用されません。

// PAC 設定時の CFNetworkCopySystemProxySettings() の返り値(例)
{
  "ProxyAutoConfigEnable": 1,
  "ProxyAutoConfigURLString": "http://192.168.1.1:8000/proxy.pac"
  // HTTPProxy / HTTPPort は含まれない
}

PAC(自動プロキシ)への対応は cupertino_http パッケージを使った別のアプローチが必要です。
詳しくは関連記事「Flutter アプリで PAC ファイルベースの自動プロキシに対応する」を参照してください。


まとめ

項目 Android iOS
プロキシ検知 API ConnectivityManager.defaultProxy CFNetworkCopySystemProxySettings()
適用先(ネイティブ) JVM システムプロパティ URLSessionConfiguration.default
適用先(Dart) HttpOverrides.global HttpOverrides.global
手動プロキシ対応
PAC 自動プロキシ対応 ✅(Android OS が解決)

Flutter アプリでプロキシ対応を実装する際の参考になれば幸いです。


参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?