This article looks at an interesting phenomenon in the Dart code through a simple example.

We all know that everything in Dart is an object, even the basic types int, double, bool are classes.

When we perform the +, -, *, \ operation on an int or double class, we perform the operator operator of the class and return a new num object.

These operator operations are eventually returned through the VM, and the Dart code is essentially text that needs to be compiled into binary to run.

The following example is based on the DART 2.12.3 test

So what are we talking about here?

First let’s look at a piece of code, as shown in the following code:

  • First of all, we defined something calledidxintType parameters;
  • Then, inforThree are added to the loopInkWellClickable control;
  • Finally, inonTapThere will beidxPrint out;
class MyHomePage extends StatelessWidget {
  var images = ["RRR"."RRR"."RRR",];
  @override
  Widget build(BuildContext context) {
    List<Widget> contents = [];
    int idx = 0;
    for (var imgUrl in images) {
      contents.add(InkWell(
          onTap: () {
            print("# # # # # # # #$idx");
          },
          child: Container(
            height: 100,
            width: 100,
            color: Colors.red,
            child: Text(imgUrl),
          )));
      idx++;
    }
    returnScaffold( appBar: AppBar(), body: Center( child: Column( children: [ ...contents, ], ))); }}Copy the code
  • So the question is, what do you think happens when you click on these three InkWell prints?

  • And the answer is that they print out all 3’s.

Why is that? Print (idx, int*); print (idx, int*); print (idx, int*);

@ #C475
    method build(fra2::BuildContext* context)- > fra2: : widgets *<fra2::Widget*> contents = core::_GrowableList::•< FRA2 ::Widget*>(0);
      core::int* idx = 0;
      {
        core::Iterator<core::String*>* :sync-for-iterator = this.{main::MyHomePage::images}.{core::Iterable::iterator};
        for (; :sync-for-iterator.{core::Iterator::moveNext}(); ) {
          core::String* imgUrl = :sync-for-iterator.{core::Iterator::current};
          {
            [@vm.call-site-attributes.metadata=receiverType:dart.core::List<library package:flutter/src/widgets/framework.dart::Widget*>*] contents.{core::List::add}(newInk5 ::InkWell::•(onTap: () → Null {core::print("######## ${idx}");
            }, child: newCon7: : Container: : • (height:100.0, width: 100.0, color: #C40086, $creationLocationd_0dea112b090073317d4: #C66610), $creationLocationd_0dea112b090073317d4: #C66614));
            idx = idx.{core::num::+}(1); }}}Copy the code

So if what we need to print out is eachInkWellTheir ownindex?

As shown in the code below, we added an index parameter to the for loop, assigning each idx to index, so that clicking on the printable result would be clicking on the corresponding index.

class MyHomePage extends StatelessWidget {
  var images = ["RRR"."RRR"."RRR",];
  @override
  Widget build(BuildContext context) {
    List<Widget> contents = [];
    int idx = 0;
    for (var imgUrl in images) {
      int index = idx;
      contents.add(InkWell(
          onTap: () {
            print("# # # # # # # #$index");
          },
          child: Container(
            height: 100,
            width: 100,
            color: Colors.red,
            child: Text(imgUrl),
          )));
      idx++;
    }
    returnScaffold( appBar: AppBar(), body: Center( child: Column( children: [ ...contents, ], ))); }}Copy the code

Why is that?

Let’s look at the newly compiled code as shown below. We can see that core::int* index = idx; This code, and then recall from earlier, is that the base types in Dart are objects, and the operator operator returns a new object.

In this way, it is equivalent to using index to save each operation, and print is naturally the IDX saved each time.

@ #C475
    method build(fra2::BuildContext* context)- > fra2: : widgets *<fra2::Widget*> contents = core::_GrowableList::•< FRA2 ::Widget*>(0);
      core::int* idx = 0;
      {
        core::Iterator<core::String*>* :sync-for-iterator = this.{main::MyHomePage::images}.{core::Iterable::iterator};
        for (; :sync-for-iterator.{core::Iterator::moveNext}(); ) {
          core::String* imgUrl = :sync-for-iterator.{core::Iterator::current};
          {
            core::int* index = idx;
            [@vm.call-site-attributes.metadata=receiverType:dart.core::List<library package:flutter/src/widgets/framework.dart::Widget*>*] contents.{core::List::add}(newInk5 ::InkWell::•(onTap: () → Null {core::print("######## ${index}");
            }, child: newCon7: : Container: : • (height:100.0, width: 100.0, color: #C40086, $creationLocationd_0dea112b090073317d4: #C66610), $creationLocationd_0dea112b090073317d4: #C66614));
            idx = idx.{core::num::+}(1); }}}Copy the code

So let me write it a different way.

As shown in the following code, put InkWell into a getItem function to return InkWell, and then pass in index as a function parameter. You can see the result of this operation. Click InkWell to print the corresponding index.

class MyHomePage extends StatelessWidget {
  var images = ["RRR"."RRR"."RRR",];
  @override
  Widget build(BuildContext context) {
    List<Widget> contents = [];
    int idx = 0;
    getItem(int index, String imgUrl) {
      return InkWell(
          onTap: () {
            print("# # # # # # # #$index");
          },
          child: Container(
            height: 100,
            width: 100,
            color: Colors.red,
            child: Text(imgUrl)));
    }
    for (var imgUrl in images) {
      contents.add(getItem(idx, imgUrl));
      idx++;
    }
    returnScaffold( appBar: AppBar(), body: Center( child: Column( children: [ ...contents, ], ))); }}Copy the code

Why is that?

Getitem.call (IDX) : getitem.call (IDx) : getitem.call (IDX) : getitem.call (IDX) : getitem.call (IDX) So each click also prints the corresponding index.

@ #C475
    method build(fra2::BuildContext* context)- > fra2: : widgets *<fra2::Widget*> contents = core::_GrowableList::•< FRA2 ::Widget*>(0);
      core::int* idx = 0;
      function getItem(core::int* index)- > ink5: : InkWell *{
        return newInk5 ::InkWell::•(onTap: () → Null {core::print("######## ${index}");
        }, child: newCon7: : Container: : • (height:100.0, width: 100.0, color: #C40086, $creationLocationd_0dea112b090073317d4: #C66610), $creationLocationd_0dea112b090073317d4: #C66614);
      }
      {
        core::Iterator<core::String*>* :sync-for-iterator = this.{main::MyHomePage::images}.{core::Iterable::iterator};
        for (; :sync-for-iterator.{core::Iterator::moveNext}(); ) {
          core::String* imgUrl = :sync-for-iterator.{core::Iterator::current};
          {
            [@vm.call-site-attributes.metadata=receiverType:dart.core::List<library package:flutter/src/widgets/framework.dart::Widget*>*] contents.{core::List::add}([@vm.call-site-attributes.metadata=receiverType:library package:flutter/src/material/ink_well.dart::InkWell* Function(dart.core::int*)*] getItem.call(idx));
            idx = idx.{core::num::+}(1); }}}Copy the code

And finally let’s write it a different way.

What happens if you add InkWell and print idX with a basic for loop, as shown in the code below?

class MyHomePage extends StatelessWidget {
  var images = [ "RRR"."RRR"."RRR"];

  @override
  Widget build(BuildContext context) {
    List<Widget> contents = [];
    for (int idx = 0; idx < images.length; idx++) {
      contents.add(InkWell(
          onTap: () {
            print("# # # # # # # #$idx");
          },
          child: Container(
            height: 100,
            width: 100,
            color: Colors.red,
            child: Text(images[idx]),
          )));
    }
    returnScaffold( appBar: AppBar(), body: Center( child: Column( children: [ ...contents, ], ))); }}Copy the code

The answer is: click on InkWell to print the corresponding index.

Why is that?

If we continue to look at the compiled code, we can see that the idX is printed. Why does this work?

The biggest difference here is where idX is declared.

@ #C475
    method build(fra2::BuildContext* context)- > fra2: : widgets *<fra2::Widget*> contents = core::_GrowableList::•< FRA2 ::Widget*>(0);
      for (core::int* idx = 0; idx.{core::num::<}(this.{main::MyHomePage::images}.{core::List::length}); idx = idx.{core::num::+}(1)) {
        [@vm.call-site-attributes.metadata=receiverType:dart.core::List<library package:flutter/src/widgets/framework.dart::Widget*>*] contents.{core::List::add}(newInk5 ::InkWell::•(onTap: () → Null {core::print("######## ${idx}");
        }, child: newCon7: : Container: : • (height:100.0, width: 100.0, color: #C40086, child: newText: : text: : (this.{main::MyHomePage::images}.{core::List::[]}(idx), $creationLocationd_0dea112b090073317d4: #C66607), $creationLocationd_0dea112b090073317d4: #C66613), $creationLocationd_0dea112b090073317d4: #C66617));
      }
Copy the code

At this time, we will readjust, put idX outside for, and click the test to find that the printed results are all 3 again.

class MyHomePage extends StatelessWidget { var images = [ "RRR", "RRR","RRR"]; @override Widget build(BuildContext context) { List<Widget> contents = []; int idx = 0; for (; idx < images.length; idx++) { contents.add(InkWell( onTap: () { print("######## $idx"); }, child: Container( height: 100, width: 100, color: Colors.red, child: Text(images[idx]), ))); } return Scaffold( appBar: AppBar(), body: Center( child: Column( children: [ ...contents, ], ))); }}Copy the code

Why is that?

If you look at the compiled code, the only difference is where core::int* idx is declared.

@ #C475
    method build(fra2::BuildContext* context)- > fra2: : widgets *<fra2::Widget*> contents = core::_GrowableList::•< FRA2 ::Widget*>(0);
      core::int* idx = 0;
      for (; idx.{core::num::<}(this.{main::MyHomePage::images}.{core::List::length}); idx = idx.{core::num::+}(1)) {
        [@vm.call-site-attributes.metadata=receiverType:dart.core::List<library package:flutter/src/widgets/framework.dart::Widget*>*] contents.{core::List::add}(newInk5 ::InkWell::•(onTap: () → Null {core::print("######## ${idx}");
        }, child: newCon7: : Container: : • (height:100.0, width: 100.0, color: #C40086, child: newText: : text: : (this.{main::MyHomePage::images}.{core::List::[]}(idx), $creationLocationd_0dea112b090073317d4: #C66607), $creationLocationd_0dea112b090073317d4: #C66613), $creationLocationd_0dea112b090073317d4: #C66617));
      }
Copy the code

For (core::int* idx = 0; for (core::int* idx = 0; For example, the scope of idX is within the for loop, so there is a corresponding value in onTap after compilation to hold the output.

For core::int* IDx defined outside the for loop, all of the onTaps inside the loop can point to the same address, resulting in the same idX output when clicked.

As to why there is such logic, it is not explored in the deep run-time logic, presumably the compiled binaries at runtime are related to the optimization of parameters outside the loop and parameters inside the loop.

In theory, it should be variable capture:

  • For global variables, they are not captured and accessed through global variables.
  • For local variables, automatic variables will be captured and passed by value.

Finally, if you want to see dill content, you can use XXD on MAC:

xxd /Users/xxxxxxx/workspace/flutter-wrok/flutter_app_test/.dart_tool/flutter_build/bf7ed8e7e7b3e64f28f0af8a89a29ca9/app.dil lCopy the code

But can be by dump_kernel. Dart (in the full version of dart – SDK/Users/guoshuyu/workspace/PKG/vm/dart – SDK directory) perform the following commands, generated app. Dill. TXT view, For example, you can see the difference between final and const compiled.

dart dump_kernel.dart /Users/xxxxxxx/workspace/flutter-wrok/flutter_app_test/.dart_tool/flutter_build/bf7ed8e7e7b3e64f28f0af8a89a29ca9/app.dil l /Users/xxxxxxx/workspace/flutter-wrok/flutter_app_test/.dart_tool/flutter_build/bf7ed8e7e7b3e64f28f0af8a89a29ca9/app.dil l.txtCopy the code