All Products
Search
Document Center

Alibaba Cloud DNS:Best practices for integrating Alibaba Cloud Public DNS SDK for Android or iOS on Flutter

Last Updated:Nov 20, 2023

This topic describes how to integrate Alibaba Cloud Public DNS SDK for Android or iOS on Flutter.

Overview

Flutter is a mobile user interface (UI) framework developed by Google. This framework allows you to build high-quality native UIs on Android or iOS in an efficient manner. You can use Flutter with existing code. Flutter is an open source framework that is provided free of charge. More and more developers and organizations around the world use Flutter. The programming language Dart is used to develop Flutter and independent Dart virtual machines (VMs) are used to develop Flutter apps. The ahead-of-time (AOT) compiler can compile Dart as code for different platforms. This way, Flutter can connect to each platform without an intermediate bridging process. Flutter is an efficient mobile cross-platform framework. You can use Flutter in the following project development scenarios:

  • Common development

The entire Flutter project is developed based on Dart. In this scenario, you can write the same code to run the Flutter project on both Android and iOS platforms.

  • Hybrid development

Flutter is integrated as a module into an Android or iOS project. Flutter and Android, or Flutter and iOS can call each other by using MethodChannel.

Warning

The underlying HttpClient of Dart does not provide an operation for setting the host field during certificate verification for HTTPS requests. Therefore, you cannot use IP addresses to initiate HTTPS requests by setting the host field when you use HttpClient of Dart or the third-party network framework Dio or HTTP. To initiate HTTPS requests by using IP addresses, you must create a local proxy. When a socket connection is established, the proxy replaces the domain name that is specified by the host field with the IP address that is obtained after the domain name is resolved by using Alibaba Cloud Public DNS SDK.

For more information about the complete code that is used to connect Flutter to Alibaba Cloud Public DNS SDK for Android or iOS, see flutterDNSDemo source code.

Practices

  • Integrate the native Alibaba Cloud Public DNS SDK for Android into a Flutter project

  1. Open the Android file of your Flutter project in Android Studio.

    Start Android Studio and choose File > Open…

    Go to the directory in which your Flutter app is stored, select the Android folder, and then click OK. Open MainActivity.java in the java directory.

  2. Integrate Alibaba Cloud Public DNS SDK for Android into your app.

    For more information about how to integrate Alibaba Cloud Public DNS SDK for Android, see SDK for Android developer guide.

  3. Configure MethodChannel for communication with Flutter in the Android folder. Note that the channel name that you specify must be the same as the channel name of Flutter.

public class MainActivity extends FlutterActivity {

    // Make sure that the channel name that you specify is the same as the channel name of Flutter.
    private static final String CHANNEL = "samples.flutter.io/getIP";
    // Configure channelMethod to call native Android methods on Flutter.
    private static final String channelMethod = "getIP";
    // Pass the host parameter from Flutter to Android.
    private static final String hostArgument = "host";
    // Initialize the DNSResolver object that is provided by Alibaba Cloud Public DNS SDK for Android.
    private DNSResolver dnsResolver = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Initialize DNSResolver.
        dnsResolver = DNSResolver.getInstance();

        new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
                new MethodChannel.MethodCallHandler() {
                    @Override
                    public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
                        // Determine whether to call the getIP method. All methods are called when you use this function.
                        if (methodCall.method.equals(channelMethod)) {
                            if (methodCall.hasArgument(hostArgument)) {
                                try{
                                    // Obtain the host parameter that is specified for Flutter.
                                    String resolverName = methodCall.argument(hostArgument);
                                    String ip = dnsResolver.getIPV4ByHost(resolverName);
                                    if (ip != null) {
                                        result.success(ip);
                                        Toast.makeText(MainActivity.this, "The native domain name of Android is resolved.", Toast.LENGTH_SHORT).show();
                                    } else {
                                        result.success(resolverName);
                                    }
                                }catch (Exception e){
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
        );
    }
}
  • Integrate the native Alibaba Cloud Public DNS SDK for iOS into a Flutter project

  1. Open the iOS project for your Flutter project in Xcode.

    Find the Xcode project file in the iOS directory of your Flutter project and use Xcode to open the file.

  2. Integrate Alibaba Cloud Public DNS SDK for iOS into the Xcode project.

    For more information about how to integrate Alibaba Cloud Public DNS SDK for iOS, see SDK for iOS developer guide.

  3. Configure MethodChannel in iOS for the communication with Flutter.

    FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
    FlutterMethodChannel* getIPChannel = [FlutterMethodChannel
                                                methodChannelWithName:@"samples.flutter.io/getIP"
                                                binaryMessenger:controller];// Make sure that the channel name that you specify is the same as the channel name of Flutter.
    [getIPChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
            if ([@"getIP" isEqualToString:call.method]) {
                NSDictionary *argumentDic = call.arguments;
                result([self getIPWithHostName:argumentDic[@"host"]]); // Obtain the Domain Name System (DNS) resolution result.
            } else {
                result(FlutterMethodNotImplemented);
            }
    }];

    The following sample code shows how to configure a custom method to obtain the IP address that is resolved from a domain name.

    - (NSString *)getIPWithHostName:(NSString *)hostName {// We recommend that you use a fault tolerance mechanism. If no IP address is included in the resolution result, the domain name is returned to ensure that the program runs as expected.
        NSArray *array = [[DNSResolver share] getIpv4ByCacheWithDomain:hostName andExpiredIPEnabled:YES];
        if (array != NULL && array.count > 0) {
            return array[0];
        }else{
            return hostName;
        }
    }
  • Configure MethodChannel and a custom method in a Flutter project to obtain the DNS resolution result

    static const platform = const MethodChannel('samples.flutter.io/getIP');
    final String result = await platform.invokeMethod('getIP', {'host': 'Domain name that you want to resolve'});
  • Initiate HTTPS requests by using IP addresses 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 content of the returned response.
      var responseBody= await response.transform(Utf8Decoder()).join();
      print(responseBody);
    }

    When a socket connection is established, replace the domain name that is specified by the host field with an IP address to initiate an HTTPS request by using the IP address.

    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 Handle 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;
    
      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);
            port = m.group(2) == null ? 443 : int.parse(m.group(2));
            final String resultIP = await platform.invokeMethod(
                'getIP', {'host': host});
            final realHost = resultIP != null? 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('sever 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 packets.
      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 ('Attempt to establish a connection: $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);
      }
    }
Note
  1. This topic provides references only for integrating Alibaba Cloud Public DNS SDK for Android or iOS on Flutter.

  2. For more information about how to integrate and use Alibaba Cloud Public DNS SDK for Android or iOS, see SDK for Android developer guide or SDK for iOS developer guide.

  3. For more information about the complete code that is used to integrate Alibaba Cloud Public DNS SDK for Android or iOS on Flutter, see flutterDNSDemo source code.