Flutter

Flutter learning curve

Using Charles proxy with Flutter apps

Unfortunately Flutter ignores any proxy settings on your device or simulator and this prevents the use of Charles Proxy to debug network I/O. Luckily, you can override HTTP requests using HttpOverrides, such that all requests are routed through a app-defined proxy.

Set up

First, let's create a custom class CustomProxy to store the the ip and port, and also provide easy to use enable() and disable() methods, in addition to toString() and fromString() helpers that will make it easy to store the info in shared preferences. (You will have an error first, we'll add CustomProxyHttpOverride below.)

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

/// @todo move all of this to separate package!

/// A public-facing class
class CustomProxy {
  /// A string representing an IP address for the proxy server
  final String ipAddress;

  /// The port number for the proxy server
  /// Can be null if port is default.
  final int port;

  /// Initializer
  CustomProxy({@required this.ipAddress, this.port});

  /// Initializer from string
  /// Note: Uses static method, rather than named init to allow final properties.
  static CustomProxy fromString({@required String proxy}) {
    
    // Check if valid
    if(proxy == null || proxy == "") {
      assert(false, "Proxy string passed to CustomProxy.fromString() is invalid.");
      return null;
    }
    
    // Build and return
    final proxyParts = proxy.split(":");
    final _ipAddress = proxyParts[0];
    final _port = proxyParts.length > 0 ? int.tryParse(proxyParts[1]) : null;
    return CustomProxy(
      ipAddress: _ipAddress,
      port: _port,
    );
  }

  /// Enable the proxy
  void enable() {
    HttpOverrides.global =
        new CustomProxyHttpOverride.withProxy(this.toString());
  }

  /// Disable the proxy
  void disable() {
    HttpOverrides.global = null;
  }

  @override
  String toString() {
    String _proxy = this.ipAddress;
    if (this.port != null) {
      _proxy += ":" + this.port.toString();
    }
    return _proxy;
  }
}

Next we need to create the class that will override HttpOverrides. This is the class that will override our default HTTP requests and route them through the proxy:

/// This class overrides the global proxy settings.
class CustomProxyHttpOverride extends HttpOverrides {
  /// The entire proxy server
  /// Format: "localhost:8888"
  String proxyString;

  /// Initializer
  CustomProxyHttpOverride.withProxy(String proxy) {
    this.proxyString = proxy;
  }
  /// Override HTTP client creation
  @override
  HttpClient createHttpClient(SecurityContext context) {
    return super.createHttpClient(context)
      ..findProxy = (uri) {
        assert(this.proxyString != null && this.proxyString.isNotEmpty,
            'You must set a valid proxy if you enable it!');
        return "PROXY " + this.proxyString + ";";
      }
      ..badCertificateCallback =
          (X509Certificate cert, String host, int port) => true;
  }
}

Enabling the proxy

Now that we have our base classes, enabling the proxy is clean and easy...let's put the following code in our main.dart file:

// You can get the stored shared preferences here if you have an in-app settings panel to set up the proxy settings....

final proxy = new CustomProxy(ipAddress: "localhost", port: 8888);
proxy.enable();

runApp(MyApp());