This document describes how to integrate the HTTPDNS SDK for Android and iOS into a Flutter project.
Overview
Flutter is a mobile UI framework from Google for building high-quality native interfaces on iOS and Android from a single codebase. Flutter works with existing code. It is free, open source, and used by developers and organizations worldwide. Flutter uses the Dart language, which has its own virtual machine. Dart code is compiled ahead-of-time (AOT) to native code. This allows Flutter to communicate directly with the platform without an intermediate bridge, making it an efficient cross-platform framework. The two main development scenarios are:
Standard scenario
The entire Flutter project is implemented in Dart. You write the code once and run it on both Android and iOS.
Hybrid development model
An Android or iOS project integrates Flutter as a module. MethodChannel enables calls between the native code and Flutter code.
Dart's underlying HttpClient does not provide an interface for HTTPS certificate validation. This means you cannot make direct HTTPS requests using an IP address with Dart's HttpClient or third-party network frameworks such as Dio or Http. To enable direct HTTPS connections, set up a local proxy. When a socket connection is established, replace the host with the IP address resolved by the HTTPDNS SDK. This enables direct HTTPS access using the IP address.
For the complete sample code for integrating the Android and iOS SDKs in a Flutter project, see the flutterDNSDemo source code.
Implementation guide
Integrate the native HTTPDNS SDK for Android in a Flutter project
Open the Android module of your Flutter project in Android Studio:
Start Android Studio and select File > Open…
Navigate to your Flutter app directory, select the android folder, and click OK. In the java directory, open MainActivity.java.
Integrate the HTTPDNS Android SDK in the Application class:
For more information about how to integrate the HTTPDNS Android SDK, see the Android SDK Developer Guide.
In the android directory, define a MethodChannel to communicate with Flutter. The channel name must match the one in your Flutter code.
package com.example.flutter;
import android.widget.Toast;
import com.alibaba.pdns.DNSResolver;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.embedding.android.FlutterActivity;
import android.content.Context;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.util.Log; // Import the Log class.
public class MainActivity extends FlutterActivity {
/** Define a TAG for log identification. **/
private static final String TAG = "MainActivity";
private MethodChannel channel;
private Context context;
/** This must be the same as the channel name in Flutter. **/
private static final String CHANNEL = "samples.flutter.io/getIP";
/** Define the channel method for the Flutter side to call the native Android method. **/
private static final String channelMethod = "getIP";
/** Parameter passed from Flutter to the Android side. **/
private static final String hostArgument = "host";
/** Initialize the DNSResolver object provided in the Alibaba Cloud HTTPDNS Android SDK. **/
private DNSResolver dnsResolver = null;
/** Create a fixed-size thread pool. **/
private ExecutorService executor = Executors.newFixedThreadPool(5);
@Override
public void configureFlutterEngine(FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
// Initialize DNSResolver.
dnsResolver = DNSResolver.getInstance();
channel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL);
channel.setMethodCallHandler(new MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, Result result) {
// Check if the getIP method is called, because all methods pass through here.
if (call.method.equals(channelMethod)) {
if (call.hasArgument(hostArgument)) {
String resolverName = call.argument(hostArgument);
Log.d(TAG, "resolverName: " + resolverName); // Use Log.d to print the log.
executor.execute(() -> {
try {
String[] IPArray = dnsResolver.getIpv4ByHostFromCache(resolverName,true);
String ip = null;
if (IPArray==null || IPArray.length==0){
ip = dnsResolver.getIPV4ByHost(resolverName);
}else {
ip = IPArray[0];
}
String finalIp = ip;
runOnUiThread(() -> {
if (finalIp != null) {
Log.d(TAG, "DNS_RESOLUTION_SUCCESS: " + resolverName + "==" + finalIp); // Use Log.d to print the log.
result.success(finalIp);
Toast.makeText(MainActivity.this, "Successfully called native Android domain name resolution!", Toast.LENGTH_SHORT).show();
} else {
Log.e(TAG, "Error resolving IP address"); // Use Log.e to print the error log.
result.error("DNS_RESOLUTION_FAILED", "Failed to resolve IP address", null);
}
});
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "Error resolving IP address", e); // Use Log.e to print the error log.
runOnUiThread(() -> result.error("DNS_RESOLUTION_FAILED", e.getMessage(), null));
}
});
}
}
}
});
}
}Integrate the native HTTPDNS SDK for iOS in a Flutter project
Open the iOS module of your Flutter project in Xcode:
In your Flutter project, navigate to the ios directory and open the Xcode project file.
Integrate the HTTPDNS iOS SDK in the Xcode project:
For more information about how to integrate the HTTPDNS iOS SDK, see the iOS SDK Developer Guide.
In your iOS code, define a FlutterMethodChannel to communicate with Flutter:
FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController; FlutterMethodChannel* getIPChannel = [FlutterMethodChannel methodChannelWithName:@"samples.flutter.io/getIP" binaryMessenger:controller];// This must be the same as the channel name in Flutter. [getIPChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { if ([@"getIP" isEqualToString:call.method]) { NSDictionary *dic = call.arguments; NSString *hostName = dic[@"host"]; NSLog(@"hostName==%@",hostName); NSArray *arr = [[DNSResolver share] getIpv4ByCacheWithDomain:hostName andExpiredIPEnabled:YES]; if (arr != nil && arr.count > 0) { result(arr[0]); }else { [[DNSResolver share] getIpv4DataWithDomain:hostName complete:^(NSArray<NSString *> *dataArray) { NSLog(@"array==%@", dataArray); if (dataArray != nil && dataArray.count > 0) { result(dataArray[0]); } else { // Handle the case where resolution fails by returning the original hostName. result(hostName); } }]; } } else { result(FlutterMethodNotImplemented); } }];
Retrieve domain name resolution results in Flutter using a MethodChannel and a custom method
static const platform = const MethodChannel('samples.flutter.io/getIP'); final String result = await platform.invokeMethod('getIP', {'host': 'The domain name that you want to resolve'});Use a direct IP connection for HTTPS requests in a Flutter project
void runPoxyRequest() async { var proxy = CustomHttpsProxy(); final result = await proxy.init(); print('proxy established: ${result != null}'); await doHttpGet(); proxy.close(); } Future doHttpGet() async{ var httpClient= HttpClient(); httpClient.findProxy= (uri) => 'PROXY localhost:4041'; var request= await httpClient.getUrl(Uri.parse('https://www.taobao.com')); var response= await request.close(); // Read the response content. var responseBody= await response.transform(Utf8Decoder()).join(); print(responseBody); }When you establish a socket connection, replace the host with the IP address to enable a direct HTTPS connection.
import 'dart:io'; import 'dart:convert'; import 'package:flutter/services.dart'; class CustomHttpsProxy { final int port = 4041; ServerSocket? serverSocket; CustomHttpsProxy(); Future init() async { await ServerSocket.bind(InternetAddress.anyIPv4, port).then((serverSocket) { this.serverSocket = serverSocket; serverSocket.listen((client) { try { ClientConnectionHandler(client).handle(); } catch (e) { print('ClientConnectionHandler exception $e'); } }); }).catchError((e) { print('serverSocket exception $e'); }); return serverSocket; } void close() { if (serverSocket != null) { serverSocket?.close(); } } } class ClientConnectionHandler { final RegExp regx = RegExp(r'CONNECT ([^ :]+)(?::([0-9]+))? HTTP/1.1\r\n'); static const platform = const MethodChannel('samples.flutter.io/getIP'); Socket? server; Socket? client; String content = ''; String host = ''; int port = 443; ClientConnectionHandler(this.client); void closeSockets() { // print('socket is going to destroy'); if (server != null) { server?.destroy(); } client?.destroy(); } Future<void> dataHandler(data) async { if (server == null) { content += utf8.decode(data); final m = regx.firstMatch(content); if (m != null) { host = m.group(1) ?? 'unknown'; port = int.tryParse(m.group(2) ?? '') ?? 443; final String? resultIP = await platform.invokeMethod('getIP', {'host': host}); final String realHost = resultIP ?? host; print('~~~~~$resultIP'); try { ServerConnectionHandler(realHost, port, this) .handle() .catchError((e) { print('Server error $e'); closeSockets(); }); } catch (e) { print('Server exception $e'); closeSockets(); } } } else { try { server?.add(data); } catch (e) { print('server has been shut down'); closeSockets(); } } } void errorHandler(error, StackTrace trace) { print('client socket error: $error'); } void doneHandler() { closeSockets(); } void handle() { client?.listen(dataHandler, onError: errorHandler, onDone: doneHandler, cancelOnError: true); } } class ServerConnectionHandler { final String RESPONSE = 'HTTP/1.1 200 Connection Established\r\n\r\n'; final String host; final int port; final ClientConnectionHandler handler; Socket? server; Socket? client; String content = ''; ServerConnectionHandler(this.host, this.port, this.handler) { client = handler.client; } // Receive the message. void dataHandler(data) { try { client?.add(data); } on Exception catch (e) { print('client has been shut down $e'); handler.closeSockets(); } } void errorHandler(error, StackTrace trace) { print('server socket error: $error'); } void doneHandler() { handler.closeSockets(); } Future handle() async { print('Attempting to connect to: $host:$port'); server = await Socket.connect(host, port, timeout: Duration(seconds: 60)); server?.listen(dataHandler, onError: errorHandler, onDone: doneHandler, cancelOnError: true); handler.server = server; client?.write(RESPONSE); } }
This document applies only to using the HTTPDNS SDK for Android and iOS in Flutter projects.
For the complete sample code for integrating the HTTPDNS Android and iOS SDKs in a Flutter project, see the flutterDNSDemo source code.