<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: huangli huang</title>
    <description>The latest articles on DEV Community by huangli huang (@huangli_huang_12f4a0530eb).</description>
    <link>https://dev.to/huangli_huang_12f4a0530eb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3434518%2F475a1684-fd93-4634-8f26-69eee43db6b5.png</url>
      <title>DEV Community: huangli huang</title>
      <link>https://dev.to/huangli_huang_12f4a0530eb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/huangli_huang_12f4a0530eb"/>
    <language>en</language>
    <item>
      <title>The Most Frequently Asked Flutter Engineer Interview Questions(2025)</title>
      <dc:creator>huangli huang</dc:creator>
      <pubDate>Wed, 10 Sep 2025 00:40:29 +0000</pubDate>
      <link>https://dev.to/huangli_huang_12f4a0530eb/the-most-frequently-asked-flutter-engineer-interview-questions2025-45ee</link>
      <guid>https://dev.to/huangli_huang_12f4a0530eb/the-most-frequently-asked-flutter-engineer-interview-questions2025-45ee</guid>
      <description>&lt;p&gt;If you're preparing for a Flutter engineer interview, you might be wondering what kinds of questions hiring managers typically ask. Based on real-world interview patterns, I've compiled the most frequently asked Flutter interview questions along with sample answers so you can study efficiently.&lt;/p&gt;

&lt;h1&gt;
  
  
  Flutter &amp;amp; Dart Fundamentals
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Q: What is the different between StatelessWidget and StatefulWidget?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A: A StatelessWidget has no internal state and always renders the same UI. It's used for static UI like text or icons. A StatefulWidget maintains state across rebuilds and can update its UI dynamically using setState(). Use it for inputs, toggle, or dynamic data.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Q: What's the difference between final and const in Dart?&lt;br&gt;
A: final means the variable is assigned once at runtime but not know at compile-time. const means the value is a compile-time constant and deeply immutable. Example: final date = DateTime.now(); const pi = 3.14;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Q: Explain Hot Reload vs. Hot Restart.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A: Hot reload injects updated code while preserving the app state. Hot Restart restarts the app entirely, losing current state but ensuring a fresh run.&lt;/p&gt;

&lt;h1&gt;
  
  
  State Management
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Q: What are some state management approaches in Flutter?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A: Options include setState, InheritedWidget, Provider, Riverpod, Bloc, GetX, and MobX.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Q: How do you decide which one to use?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A: For simple UI updates, use setState. For medium apps, Provider or Riverpod balances simplicity and scalability. For large or event-driven apps, Bloc provides structure and testability.&lt;/p&gt;

&lt;h1&gt;
  
  
  Navigation &amp;amp; Routing
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Q: What's the difference between Navigator 1.0 and Navigator 2.0?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A: Navigator 1.0 is imperative (push/pop routes directly). Navigator 2.0 is declarative and works better with deep linking, dynamic routes, and web apps.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Q: How do you pass data between? &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A: Pass via constructor arguments when pushing routes, return results using Navigator.pop(context, result), or use shared state with Provider/Bloc for complex cases.&lt;/p&gt;

&lt;h1&gt;
  
  
  Performance &amp;amp; Optimization
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Q: How do you reduce unnecessary widget rebuilds?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A: Use const constructors, avoid rebuilding entire widget trees by splitting widgets, use Keys to preserve state, and optimize rebuild logic with Selector or BlocBuilder.buildWhen.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Q: How do you reduce app size?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A: Use tree shaking to remove unused code, split builds per ABI (--split-per-abi), compress/convert images to WebP, and use --split-debug-info for release builds.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;UI &amp;amp; Animations&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Q: What's the difference between implicit and explicit animations?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A: Implicit animations (e.g., AnimatedContainer) handle transitions automatically and are simple but limited. Explicit animations use AnimationController and provide fine-grained control over timing and curves.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Q: How do you implement a Hero animation?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A: Wrap widgets in Hero(tag: 'id', child: widget). Use the same tag on both source and destination widgets. Flutter automatically animates the transition.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Networking &amp;amp; Data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Q: How do you fetch data from an API?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A: Use the http or dio package. Example with http:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final response = await http.get(Uri.parse('https://api.example.com/data'));
final data = jsonDecode(response.body);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Q: How do you store data locally?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A: Use SharedPreferences for small key-value data, Hive for lightweight NoSQL storage, and Sqflite or Drift for relational databases.&lt;/p&gt;

&lt;h1&gt;
  
  
  Testing
&lt;/h1&gt;

&lt;p&gt;Q: What's the difference between unit, widget , and integration test?&lt;/p&gt;

&lt;p&gt;A: Unit tests check individual functions/classes. Widget tests verify UI behavior in isolation using WidgetTester. Integration tests run on real/simulated devices to validate full app workflows.&lt;/p&gt;

&lt;h1&gt;
  
  
  Platform Integration
&lt;/h1&gt;

&lt;p&gt;Q: How do you call native code in Flutter?&lt;/p&gt;

&lt;p&gt;A: Use MethodChannel to invoke platform-specific APIs (e.g., battery level). Use EventChannel for continuous streams like accelerometer or GPS updates.&lt;/p&gt;

&lt;h1&gt;
  
  
  Advanced Topics
&lt;/h1&gt;

&lt;p&gt;Q: What is an isolate in Flutter?&lt;/p&gt;

&lt;p&gt;A: An Isolate is an independent thread of execution with its own memory. Use it for CPU-heavy tasks like parsing large JSON files to avoid blocking the UI thread. The compute() function is a convenient way to run code in background isolate.&lt;/p&gt;

&lt;p&gt;Q: How do you structure a large Flutter project?&lt;/p&gt;

&lt;p&gt;A: Use feature-first or Clean Architecture. Typical layers: data(repositories, APIs, DB), domain(entities, use cases), presentation (UI, state, management). This separation improves testability and maintainbility.&lt;/p&gt;

&lt;h1&gt;
  
  
  Final Thoughts:
&lt;/h1&gt;

&lt;p&gt;Most Flutter engineer interviews test both fundamentals(widgets, Dart, state) and practical skills (architecture, performance, deployment). The best preparation is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Practicing small coding tasks (e.g., building a Todo app).&lt;/li&gt;
&lt;li&gt;Reviewing state management patterns.&lt;/li&gt;
&lt;li&gt;Understanding app lifecycle, navigation, and optimization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pro Tips: Interviewers often ask why you chose a particular approach. Be ready to explain trade-offs with examples.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
    </item>
    <item>
      <title>How to proxy Flutter's network requests to the native layer</title>
      <dc:creator>huangli huang</dc:creator>
      <pubDate>Tue, 09 Sep 2025 00:39:27 +0000</pubDate>
      <link>https://dev.to/huangli_huang_12f4a0530eb/how-to-proxy-flutters-network-requests-to-the-native-layer-5b3b</link>
      <guid>https://dev.to/huangli_huang_12f4a0530eb/how-to-proxy-flutters-network-requests-to-the-native-layer-5b3b</guid>
      <description>&lt;h1&gt;
  
  
  Preface
&lt;/h1&gt;

&lt;p&gt;Due to the company's product adopting a hybrid development model of Native + Flutter, for Android, network requests are handled through Android's OkHttp and Flutter's Dio respectively. However, this approach has some drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;The process of network request needs to have two sets: one set for the native side and another set for Flutter. The processes I'm referring to here include mechanisms such as retry and caching. For example if i want to implement a function where the user is redirected to the login page when the token expires, I have to write two copies of the code.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;Using the Charles packet capture tool is a bit troublesome. We know that Flutter's Dio ignores the phone's proxy setting. To use a network proxy, it has to be configured in the code. However, it's different with native network components; using Charles for packet capture is very simple.&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Based on the above two considerations, I want to entrust both the existing network requests and the newly added network requests in the current application to the native layer for handling network requests. Flutter only needs to parse the results of the network requests and display the UI.&lt;/p&gt;

&lt;p&gt;After introducing the general background, we first need to understand Dio. Dio itself provides a way to customize network requests. The so-called customization here means implementing the sending and receiving of network requests by oneself.&lt;/p&gt;

&lt;h1&gt;
  
  
  Regarding the Dio network request process
&lt;/h1&gt;

&lt;p&gt;First, we create a very simple get request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3typh4dqub897td5id63.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3typh4dqub897td5id63.png" alt=" " width="800" height="32"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since there is a lot of code inside "get", only a few representative methods will be mentioned here.&lt;/p&gt;

&lt;p&gt;The "get" will eventually call the fetch method in dio_mixin.dart. As the name suggests, this is where network requests are sent, and there is a _dispatchRequest inside the method.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0tqsjxo6jbf5d9xoo7m5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0tqsjxo6jbf5d9xoo7m5.png" alt=" " width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Understanding the content within _dispatchRequest is crucial for our requirement because it is here that the actual sending of network requests(including those related to sockets) is completed. If we want to replace the actual network requests with native ones, we need to start from here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Future&amp;lt;Response&amp;lt;dynamic&amp;gt;&amp;gt; _dispatchRequest&amp;lt;T&amp;gt;(RequestOptions reqOpt) async {
   //Focus 1
   final responseBody = await httpClientAdapter.fetch(
        reqOpt,
        stream,
        cancelToken?.whenCancel,
      )
   final headers = Headers.fromMap(responseBody.headers);
      // Make sure headers and responseBody.headers point to a same Map
      //Focus 2
      responseBody.headers = headers.map;
      final ret = Response&amp;lt;dynamic&amp;gt;(
        headers: headers,
        requestOptions: reqOpt,
        redirects: responseBody.redirects ?? [],
        isRedirect: responseBody.isRedirect,
        statusCode: responseBody.statusCode,
        statusMessage: responseBody.statusMessage,
        extra: responseBody.extra,
      );
  if (statusOk || reqOpt.receiveDataWhenStatusError == true) {
        //Focus 3
        Object? data = await transformer.transformResponse(
          reqOpt,
          responseBody,
        );
        // Make the response as null before returned as JSON.
        if (data is String &amp;amp;&amp;amp;
            data.isEmpty &amp;amp;&amp;amp;
            T != dynamic &amp;amp;&amp;amp;
            T != String &amp;amp;&amp;amp;
            reqOpt.responseType == ResponseType.json) {
          data = null;
        }
        ret.data = data;
      }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three points worthy of attention are marked above, and these are the points i think are more worthy of attention.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Focus 1&lt;br&gt;
Obtain the responseBody object through httpClientAdapter. Here, i will elaborate on the role of httpClientAdapter later. For now, it can be understood that it internally implements the logic of HTTP network requests, and after execution, it will return a responseBody.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Focus 2&lt;br&gt;
Create a response object. In fact, responseBody is not yet the final request response to be returned, because it only represents the response when the network connection is established. It contains a stream, which is used to read the body returned by server after the socket connection is established. We need to obtain the content of the stream and then close the connection. Here, we first create the response object and assign some known response fields to it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Focus 3&lt;br&gt;
As mentioned earlier, this is actually reading the content of the stream(through a transformer) and using it as the data content for our actual network response. This data is the body returned by the server, and the protocol contents such as code, msg, and json that we usually use in request results are all here.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;At this point, we actually have a general idea of how to fulfill this requirement. If we want to proxy the actual network requests to the native side, we need to focus on replacing the network request process so that actual network requests occur on the native side and return a response. Here, dio supports providing custom httpClientAdapter and transformer to connect to the native side?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;However, before starting the hands-on development, we must first clarify HttpClientAdapter and HttpClient.&lt;/p&gt;

&lt;h1&gt;
  
  
  HttpClientAdapter and HttpClient
&lt;/h1&gt;

&lt;p&gt;HttpClientAdapter is a bridge between Dio and HttpClient.&lt;/p&gt;

&lt;p&gt;Dio: it is used to implement standard and user-friendly API for developers.&lt;/p&gt;

&lt;p&gt;HttpClient: it is used to actually send and receive network requests in Dio.&lt;/p&gt;

&lt;p&gt;We can provide our own HttpClient through HttpClientAdapter instead of using the default one. HttpClientAdapter is actually an abstract class and does not provide specific implementations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpg5oyq2ikwodh6ttrydu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpg5oyq2ikwodh6ttrydu.png" alt=" " width="800" height="66"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, the factory method here creates an IOHttpClientAdapter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zd38515ck595ir2on7c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zd38515ck595ir2on7c.png" alt=" " width="800" height="35"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looking back, there is a fetch method in HttpClientAdapter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7oxo2keizupxalbfzqg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh7oxo2keizupxalbfzqg.png" alt=" " width="800" height="721"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;According to the comments, the fetch method is where the actual network request is sent. So how is it implemented in IOHttpClientAdapter? Here, I'll skip details and look at the key code. Its fetch method will call a _fetch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fli05tjdy0x8y6nk05062.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fli05tjdy0x8y6nk05062.png" alt=" " width="800" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;_configHttpClient will be used to create an HttpClient, which is used to actually send and receive network requests in Dio.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4468d3jtzpowhmk81cp9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4468d3jtzpowhmk81cp9.png" alt=" " width="800" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looking at the implementation, it is divided into two steps:&lt;br&gt;
1, If i have the assignment of the createHttpClient, I directly use the createHttpClient method to create an HttpClient here.&lt;/p&gt;

&lt;p&gt;2, If not, I directly call the constructor of HttpClient, and then what is created is _HttpClient.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs1tl9f4grtnbsqjeyp8n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs1tl9f4grtnbsqjeyp8n.png" alt=" " width="800" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;_HttpClient is actually the default implementation of Http provided by Dart io. HttpClient is used to exchange data with the Http server, send Http requests to the Http server, receive responses, and also maintain some states, such as containing session cookies.&lt;/p&gt;

&lt;p&gt;HttpClient involves sending HttpClientRequest to the Http server and then receiving HttpClientResponse. As for the internal details of HttpClient, I will not elaborate on them. It is nothing more than implementing the http protocol, which is not the focus of this article.&lt;/p&gt;
&lt;h1&gt;
  
  
  General Solution
&lt;/h1&gt;

&lt;p&gt;In fact, understanding the flow of Dio, the idea becomes quite clear. Dio provides a method to customize the HttpClientAdapter. I have customized a NativeClientAdapter here, which overrides the implementation of fetch &lt;em&gt;(the part where network request are sent)&lt;/em&gt;. In fetch, instead of using the default http client, it directly sends the request parameters to the native side via method channel(method channel is one of the official was to communicate between native and Flutter). After the native side receives them, it sends the network request, return the response to Flutter, and then performs data conversion for the upper layer of Flutter.&lt;/p&gt;
&lt;h1&gt;
  
  
  Code implementation
&lt;/h1&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class NativeNetDelegate {

  final dio = Dio();

  NativeNetDelegate(String gateway) {
    dio.httpClientAdapter = NativeClientAdapter();
    dio.transformer = NativeTransformer();
    dio.options.baseUrl = Base.baseUrl + gateway;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here, I customized a Dio, using my own NativeClientAdapter and NativeTransformer. NativeTransformer is used to convert the data returned natively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class NativeClientAdapter implements HttpClientAdapter {

  static const _tag = "NativeClientAdapter";

  /// Here, the fetch method is rewritten. It does not follow the implementation logic of the default http client, and directly sends the request parameters to the native for processing.
  @override
  Future&amp;lt;ResponseBody&amp;gt; fetch(
      RequestOptions options,
      Stream&amp;lt;Uint8List&amp;gt;? requestStream,
      Future&amp;lt;void&amp;gt;? cancelFuture,
      ) async {
    NativeRequestOption nativeRequestOption =
        NativeRequestOption().compose(options);
    vlog.d("$_tag fetch request $nativeRequestOption");
    dynamic result =
        await FlutterMethodHelper.instance.sendHttpRequest(nativeRequestOption.toJson());
    vlog.d("$_tag fetch response result $result");
    /// http code
    int httpCode = result['httpCode'];

    /// protocol json data
    String data = result['data'];

    return NativeResponseBody(
      fakeStream(),
      httpCode,
    )..data = data;
  }

  /// Because a proxy is used here, the data part of the response is returned directly, so there is no need to read data using a stream.
  Stream&amp;lt;Uint8List&amp;gt; fakeStream() async* {

  }

  /// Called when Dio is closed. Since network requests are forwarded to the native for processing, there is no need to release resources here.
  /// For details, refer to the [Dio] close method
  @override
  void close({bool force = false}) {

  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class NativeTransformer implements Transformer {

  static const String _tag = "NativeTransformer";

  ///Here, because it is proxied to the native, no Stream is used to read the content of the Request.
  @override
  Future&amp;lt;String&amp;gt; transformRequest(RequestOptions options) async {
    return "";
  }

  ///Here, directly return the protocol json returned by the original network request, which is the body of the response.
  @override
  Future&amp;lt;dynamic&amp;gt; transformResponse(RequestOptions options, ResponseBody responseBody) async {
    if (responseBody is NativeResponseBody) {
      if (responseBody.data != null) {
        /// convert this to json for upper-level use
        Map&amp;lt;String, dynamic&amp;gt; data = jsonDecode(responseBody.data!);
        vlog.d("$_tag $data");
        return data;
      } else {
        return "";
      }
    } else {
      throw DioException(
          requestOptions: options,
          message:
              "no support responseBody type, only support NativeResponseBody in NativeTransformer");
    }
  }
}

class NativeResponseBody extends ResponseBody {
  String? data;
  NativeResponseBody(super.stream, super.statusCode);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the previous code, we used a NativeRequestOption, which is actually a protocol content class. We can forward this object to the native layer to parse and send network requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class NativeRequestOption {

  String? baseUrl;
  String? path;
  String? method;
  String? data;
  Map&amp;lt;String, dynamic&amp;gt;? queryParameters;

  NativeRequestOption();

  factory NativeRequestOption.fromJson(Map&amp;lt;String, dynamic&amp;gt; json) =&amp;gt; _$NativeRequestOptionFromJson(json);

  Map&amp;lt;String, dynamic&amp;gt; toJson() =&amp;gt; _$NativeRequestOptionToJson(this);

  NativeRequestOption compose(RequestOptions options) {
    baseUrl = options.baseUrl;
    path = options.path;
    method = options.method;
    data = options.data?.toString();
    queryParameters = options.queryParameters;
    return this;
  }

  @override
  String toString() {
    return 'NativeRequestOption{baseUrl: $baseUrl, path: $path, method: $method, data: $data, queryParameters: $queryParameters}';
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, I won't paste the code of the native layer. It's nothing more than sending a network request after receiving the NativeRequestOption, and then sending the network result back to Flutter for parsing into a structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
“httpCode“ : 1001
“data” : Content of business agreement（i.e., code, data, msg）
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;The most important thing about this article is to provide an idea. The code implementation varies form person to person and from requirement to requirement. You can customize the HttpClientAdapter according to your own needs. Of course, Dio also provides an interceptor function. It is also feasible for us to intercept requests directly in the interceptor and forward them to the native side, but we will not discuss this scheme here. if you find it useful, don't forget to thumb up, comment and collect it.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>android</category>
      <category>dart</category>
      <category>mobile</category>
    </item>
  </channel>
</rss>
