One, foreword

The first day I set up the Flutter environment and simply ran my first Flutter project. I felt great. Some of the basic operations were similar to the native experience. Developers who have written App projects with the Flutter framework know that Flutter is a cross-platform mobile UI framework developed using the Dart language that enables high-performance, high-fidelity Android and iOS development with its own rendering engine. A lot of developers, like me, have a question, right? Why did Flutter choose Dart? I specially checked the online information, summed up the following six points:

  • Dart is compiled under release by AOT(Ahead Of Time, such as normal static compilation) into fast and predictable native code, so that Flutter can almost all be written using Dart. Dart is a jUST In Time compilation with a fast development cycle, jUST like the subthermal overload of Flutter.
  • Dart makes it easier to create smooth animations and transitions that run at 60fps. What is 60fps? I believe that as you all know, The frame rate review report for King of Glory games is between 40fps and 50fps, and the higher the FPS, the faster the control flow and the more comfortable the experience.
  • Dart can do object allocation and garbage collection without locking. Dart, like JavaScript, avoids preemptive scheduling and shared memory. Most computer languages that support concurrent threads (Java, Kotlin, Swift) use preemption to switch threads. Preemption creates race conditions that can lead to serious errors. Dart uses ISOLATE to solve this problem. Threads in Dart are called ISOLATE and do not share memory. Dart uses ISOLATE to solve this problem. Dart is single-threaded, meaning preemption is not allowed at all. Single threading helps developers ensure that key functions (animations and transitions) are completed without preemption. Flutter applications are compiled to native code, so they do not need to build slow Bridges between domains, so start up much faster.
  • Dart enables Flutter to eliminate the need for a separate declarative layout language, such as JSX or XML, or a separate visual interface builder. In fact, I find Android’s XML interface builder straightforward to read at this point.
  • Dart is easy to learn and has features familiar to users of both static and dynamic languages.
  • Easy to portable, Dart compiles to ARM and X86 code so Dart mobile applications can run on iOS, Android, and elsewhere.

Now that you know why Flutter chose Dart, it is necessary to learn Dar well as part of the development of the Flutter project. Here is a primer on Dart from the basic syntax.

Dart environment

1. Local environment

I use IntelliJ IDEA to write the Dart project locally. I will briefly describe the environment configuration process here:

  1. Download the Flutter SDK before downloading it, and configure the Android Stdio environment on the first day.
  2. Dart plug-in installation, select in WindowsFileSettingPluginsSearch for Dart downloads while selecting the top left corner of the screen under MacIntelliJ IDEAPerferencesPlugins, as shown below:

  1. configurationDart SDKThe location is selected under WindowsFileSettingLanguages & FrameworksDartDart SDK PathConfig, while Mac selects the top left corner of the screenIntelliJ IDEAPerferencesLanguages & Frameworks, as shown below:

File
New
Project
Dart
Dart

  1. Create the project name and set the project path as shown below:

Finish

2. Web environment

Dartpad.dartlang.org/ is an example of looping 5 hello’s. If you want to set up the Dart environment in IntelliJ IDEA, you can set up the Dart environment on the web. The schematic diagram is as follows:

Dart some concepts

  • In Dart, everything is an object, and all objects are inheritedObjectThat is, all objects that can be referenced by variables are objects, and each object is an instance of a class. Even numbers, methods, and NULL are objects in Dart.
  • Any variable that has no initial value will have a default valuenull
  • Identifiers can be letters or_It starts with an underscore and can be followed by any combination of other characters and numbers.
  • Dart supports top-level methods such asmainDart also supports defining functions (static and instance) in a class, and methods in a method. Dart supports top-level variables, as well as class or object variables.
  • The Dart is notpublic,protected,privateThe keyword. If a variable is underlined_Start, indicating that the variable is private in the library.
  • Dart’s classes and interfaces are unified. A class is an interface, you can inherit a class, you can implement a class, and it also includes good object-oriented and concurrent programming support.
  • finalCan only be set once.constIs a compile-time constant that can be passedconstTo create constant values,var n = const[], where n is still a variable, but is assigned a constant value, it can be any other value. Instance variables can be final, but not const.
  • Dart is a strongly typed language, but can be usedvarordynamicTo declare a variable, Dart will automatically infer its data type,dynamicSimilar to c #
  • Using static typing makes your intentions clearer and allows static analysis tools to analyze your code.
  • Dart parses your code before running it. You can help Dart catch exceptions and run code more efficiently by using types or compile-time constants.
  • The Dart tool can point out two types of problems: warnings and errors. Warnings just say that your code may have a problem, but they don’t stop your code from executing. Errors can be either compile-time errors or run-time errors. When a compile-time error occurs, the code will not execute; A runtime error will cause an exception when running the code.

Dart syntax

1. The keywords

The following table is the Dart language keywords

const null class new this
as default final continue throw
assert deferred finally operator true
async do for part try
async* dynamic get rethrow typedef
await else if return var
break enum implements set void
case export import static while
catch external in super with
false extends is switch yield
abstract factory library sync* yield*

2. A basic Dart program

// Define a method for printing numbers
printNumber(num Number){
  print('The number is $Number');
}

// Execute the program entry
void main(a){
  // define a variable to initialize
  var number = 6.76;
  // Call the print number method
  printNumber(number);
}
Copy the code

Here is a simple and basic Dart application that uses many of Dart’s features:

  • //This is a comment notation, but you can use it/ *... * /This is a comment symbol in many languages
  • numThis is numerical,String.int.boolThere are several other types. Note: numerical typenumContain plasticintAnd floating-pointdouble.
  • 6.76 is a numeric constant, and numeric constants are compile-time constants.
  • print()Method of printing content
  • "..."Or is it'... 'Represents string constants
  • $variableNameOr is it${expression}String interpolation: referencing variables or expressions in string constants
  • varA way to declare a variable without specifying a type
  • main()Is the entry method for the Dart program, and each program requires one of these scoring methods

3. The Variables Variables

var name = ‘knight’; Above is an example of declaring a variable and assigning a value. The variable is a reference. The above variable named name refers to a String with the content “Knight”.

4.Default value

Variables that are not initialized automatically get a default value of NULL. The default value of a number variable is null if it is not initialized, because the number type is also an object.

// Define a method for printing numbers
printNumber(num Number){
  print("The number is $Number");
}

// Execute the program entry
void main(a){
  // define a variable to initialize
  var number;

  // Call the print number method
  printNumber(number);
}
Copy the code

The result printed above is The number is null.

5.Optional types

When declaring a variable, you can optionally add a specific type, as follows:

  // define a variable to initialize
  double number = 6.666;
Copy the code

Add a type to make your intentions clearer. Tools such as IDE compilers have the ability to use types to help better, providing hints for code completion, finding bugs ahead of time, and so on.

6.Final and const

If you do not intend to modify a variable later, use final or const. A final variable can only be assigned once; A const variable is a compile-time constant. Note that const variables are also final. Instance variables can be final but not const. Go straight to the example:

// define a variable to initialize
  final double number = 6.666;
  number = 6.667;
  // Call the print number method
  printNumber(number);
Copy the code

Final = 6.67 number = 6.67 number = 6.67 final = 6.67 number = 6.67 Number,a final variable,can only be set once. We can modify number to const instead:

  // define a variable to initialize
  const double number = 6.666;
  number = 6.667;
  // Call the print number method
  printNumber(number);
Copy the code

Constant variables can not be assigned a value Constant variables can not be assigned a value, because const variables are final. If const is in a class, define it as static const. We can define const and flag initializers directly, or we can define a const variable that uses the value of another const variable to initialize its value, as follows:

  // define a variable to initialize
  const double number = 6.66;
  const double number1 = 2 * number;
Copy the code

Number1 in the example above uses number to initialize itself. The const keyword is not just used to define constants. You can use it to create immutable values. You can also define constructors of const types. Objects created by constructors of these types are immutable.

7.Built-in types

Dart has several built-in data types: numeric-number, Boolean, key-value pair-map, string-string, list-list, other types — Runes, and Symbols

7.1 Numeric -Number

Dart provides two types:

// Execute the program entry
void main(a){


  // Integer, usually between -2 ^ 53 and 2 ^ 53.
  num x = 777;
  // Float 64 bits
  x = 777.7;

  int y = 777;
  y = 777.7;       // This line is an error because int is converted to double

  double n = 77.7;
  d = 77;          // This is an error because we are converting data from double to int

  int x1 = 7;
  int x2 = 77;
  int x3 = 777;
  int x4 = 7777;

  print('${x1.bitLength}'); // Take 3 bits equivalent to 00000000 00000111
  print('${x2.bitLength}'); // Take up 7 bits equivalent to 00000000 01001101
  print('${x3.bitLength}'); // 10 bits equal to 00000011 00001001
  print('${x4.bitLength}'); // Take 13 bits equivalent to 00011110 01100001


}
Copy the code

You can see three points in the above example:

  • usenumDeclared variables can be converted to any type if usedintordoubleExplicit declaration, then you can’t convert
  • Judge aintCan be used when the value needs to be how many bitsbitLength

8. Numerical operations

Operators: +, -, *, /, ~/, % Common attributes: isNaN, isEven, isOdd Common methods: ABS (), round(), floor(), ceil(), toInt(), toDouble()

// Execute the program entry
void main(a){


  int i =7;
  double d = 10.1;

  print(i / d);               / / 0.6930693069306931
  print(i ~/ d);              //0 is a quotient

  print(i.isOdd);             // It is odd
  print(i.isEven);            // It is even


  //String -> int
  var x1 = int.parse("Seven");
  print(x1 == 7);              / / output true

  //Sting -> double
  var x2 = double.parse("7.7");
  print(x2 == 7.7);             / / output true

  //int -> String
  var x3 = 7.toString();
  print(x3 == '7');             / / output true

  //double -> String
  var x4 = 7.1234.toStringAsFixed(2);
  print(x4 == '7.12');          / / output true

  // find the absolute value
  var x5 = (-7).abs();
  print(x5 == 7);

  // Round to 1
  var x6 = (7.7).round();
  print(x6);                   / / output 8

  // Round to 2
  var x7 = (7.3).round();
  print(x7);                   / / output 7

  // Find the largest integer less than it
  var x8 = (7.7).floor();
  print(x8);                   / / output 7

  // Find the smallest integer greater than it
  var x9 = (7.7).ceil();
  print(x9);                   / / output 8

  double num1 = 7.77;
  print(num1);                // The result is 7.77
  double num2 = 7;
  print(num2);                // The result is 7.0
  int num3 = 7;
  print(num3.toDouble());     // double from int is 7.0
  double num4 = 7.77;
  print(num4.toInt());        //double to int is 7
  
}
Copy the code

Listed above are some of the most common operations encountered, such as remainder, integer, type conversion, etc.

9.Strings

Dart strings are UTF-16-encoded sequences of characters that can be created using either single or double quotation marks:

// Execute the program entry
void main(a){

  String m_str1 = 'Single quoted string';
  String m_str2 = "Double quoted string";

  print(m_str1);        // Output: single quoted string
  print(m_str2);        // Output: double quoted string

}
Copy the code

Nesting of single and double quotation marks in String

// Execute the program entry
void main(a){

  String m_str1 = 'Double quote' string in single quote ';
  String m_str2 = "' Single quote 'string in double quotes";

  print(m_str1);        // Output: string of "double quotes" in single quotes
  print(m_str2);        // Output: 'single quote' string in double quotes

  // Single quotes contain single quotes, which must be preceded by a backslash
  String m_str3 = 'in single quotation marks' single quotation marks';
  String m_str4 = "Double quotes inside double quotes,\" double quotes \"";
  print(m_str3);        // Output: 'single quotes' in single quotes
  print(m_str4);        // Output: double quotes inside double quotes," double quotes"

}
Copy the code

Empty strings (not Spaces) are not allowed between single quotation marks, and empty strings are not allowed between double quotation marks:

 //String m_str5 = 'single quotes '''' single quotes '; / / an error
  String m_str6 = 'Single quotes'' ''Single quotes';
  print(m_str6);        // Output: single quotes single quotes

  String m_str7 = 'Single quotes'The '*''Single quotes';
  print(m_str7);        // Output: single quotes * single quotes

  //String m_str8 = "double quotation marks """" double quotation marks"; / / an error

  String m_str9 = "Double quotes""""Double quotes";
  print(m_str9);        // Output: double quotes double quotes

  String m_str10 = "Double quotes""*""Double quotes";
  print(m_str10);       // Output: double quotes * double quotes
Copy the code

It is possible to mix single and double quotation marks to nest empty strings as follows:

 String m_str11 = 'Single quotes """" single quotes';
  print(m_str11);       // Output: single quotes """" single quotes

  String m_str12 = 'Single quotes "" "" single quotes';
  print(m_str12);       // Output: single quotes "" "" single quotes

  String m_str13 = 'single quote' "*""" single quote ';
  print(m_str13);       // Output: single quote ""*""" single quote

  String m_str14 = "Double quotes '''' double quotes";
  print(m_str14);       // Output: double quotes '''' double quotes

  String m_str15 = "Double quotes '' '' double quotes";
  print(m_str15);       // Output: double quotes '' '' double quotes

  String m_str16 = "Double quotes" "*" "double quotes";
  print(m_str16);       // Output: double quotation marks "*" "double quotation marks
Copy the code

String concatenation as follows:

String m_str1 = 'single quoted String' 'concatenated' '-- '; print(m_str1); // Output: String concatenation of single quotation marks -- // Use newline character and space String m_str2 = "single quotation String" "newline" "plus space" "; print(m_str2); // Output: single quote String newline with space // Single double quote space concatenation String m_str3 = "Single double quote String with space" 'concatenation' "----"; print(m_str3); // Output: double quotation String with space concatenation ---- // Single and double quotation String space String m_str4 = "Single and double quotation String" 'newline' 'space' '***'; print(m_str4); String m_str5 = "" three single quotes + concatenation" "; print(m_str5); String m_str6 = """ "three double quotes + concatenation """; print(m_str6); String m_str7 = "normal concatenation "+", use the plus sign to concatenate "; print(m_str7); // Output: normal concatenation, with a plus sign to concatenationCopy the code

We can avoid escaping \ by providing an r prefix to create a “raw” string, adding a character to the string, or preceded by \, as follows:

  String m_str1 = r"sdsdsds";
  print(m_str1);  / / output SDSDSDS

  print("Newline: \n"); // Output: newline

  print(r"Newline: \n"); // Output: newline: \n

  print("Newline: \\n"); // Output: newline: \n
Copy the code

${expression}, similar to JS ES6 expression use, use $can get the content of the string, use ${expression} can put the value of the expression into the string, use ${expression} can also use string splicing. Here is a direct example:

bool flag = true;
  String m_str1 = "String";
  print(Look at this value: ${m_str1}Look at this value flag: ${flag}); // Output: string: string look at the value flag: true

  // Use the $+ string
  String name = "knight";
  print("$name" + "CTO");     // Output: knightCTO;

  // use String concatenation, using the toUpperCase function of the String class, toUpperCase the letter
  String m_str = "Android";
  assert('${m_str.toUpperCase()} is very good'= ='ANDROID is very good');
  
Copy the code

The == operator determines whether the contents of two objects are the same, and if so; If two strings contain the same character encoding sequence, they are equal. The assert() statement is ignored in production mode. Assert (condition) executes in check mode and throws an exception if the condition is not true.

10.Boolean (Boolean)

Dart represents a Boolean value with bool. Only two objects are Boolean: objects created by true and false, both of which are compile-time constants. When Dart requires a Boolean value, only true objects are considered true and all other values are false. Look at the following example:

  String name ="knight";
  // Error because name is not a bool
  if(name){
    print(name);

  }
Copy the code

Dart raises an exception indicating that name is not a Boolean value and Dart uses the preceding check value, as shown in the following figure:

  // Check for an empty string
  var fullName = ' ';
  assert(fullName.isEmpty);

  // Check whether the value is less than or equal to 0
  var hitPoints = 0;
  assert(hitPoints <= 0);

  // Check for null.
  var unicorn;
  assert(unicorn == null);

  // Check if it is NaN.
  var iMeantToDoThis = 0 / 0;
  assert(iMeantToDoThis.isNaN);
Copy the code

Assert is a function of the language’s built-in assertions that are valid only in the checking mode and, during development, will throw an exception unless the condition is true. (The program terminates immediately if the assertion fails)

11. A list of Lists

Array is the most common collection type in programming languages, and I’m sure you’ve all used it as a developer. Dart array is a List object, so it’s called Lists. Here’s a Dart List:

// create a list of type int and assign 0,1,2,3,4
  List list =  [0.1.2.3.4];

  // Create a list using a build
  List list1 = new List();

  // Create a List of constants that cannot be changed
  List list2 = const[0.1.2.3];

  // Add generics
  List list3 = new List<String>();

  // Create a fixed length arraylist that cannot be removed or added
  List list4 = new List(5);

  // Create a list of mutable lengths containing all of the following elements
  List list5 = new List.from([0.1.2.3]);

  // Create a list that changes length within a fixed range
  List list6 = newList().. length =10;

  // Create a fixed-length list of all elements
  List list7 = new List.unmodifiable([0.1.2]);

  // Initialize all elements with a generator
  List list8 = new List<int>.generate(5, (int i){
    return i + i;

  });

Copy the code

List API:

 // Store different types of objects in the list
  List list = [1.2.3.false."Kinght"];
  print(list);          // Output: [1, 2, 3, false, Kinght]

  // Add elements to the list
  list.add(7);
  print(list);          // Output: [1, 2, 3, false, Kinght, 7]

  // Change the subscript 1 value of the list
  list[1] = "paul";
  print(list);          // Output: [1, Paul, 3, false, Kinght, 7]

  // Remove the specified worthy element from the list
  list.remove("paul");
  print(list);          // Output: [1, 3, false, Kinght, 7]

  // Removes the element under the specified subscript in the list
  list.removeAt(0);
  print(list);          // Output: [3, false, Kinght, 7]

  // Get the length of the list
  print(list.length);   // Output: 4

  // Add the element to the specified position in the list
  list.insert(0."Android");
  print(list);          // Output: [Android, 3, false, Kinght, 7]

  // Check if there is an element in the array
  print(list.indexOf("Android")); // If it exists, print the corresponding subscript. If it does not, print -1

  / / sorting
  List list1 = [3.1.2.6.7];
  List.sort([(int, int) → int compare]) → void
  list1.sort((a,b) => a.compareTo(b));
  print(list1);           // Output: [1, 2, 3, 6, 7]
Copy the code

Summary:

  1. The subscript index starts at 0.
  2. You can print elements of a List, which is also an object.

12.Maps

In general, a Map is a key-value pair object, and the keys and values can be any type of object. Each key appears only once, while a value can appear multiple times. Create a Map set directly above:

  //1. Use the builder to create the Map
  Map map1 = new Map();
  // Add a value to assign a value
  map1["one"] = 'Android';
  map1["two"] = 'IOS';
  map1["three"] = 'Flutter';
  print(map1);              {one: Android, two: IOS, three: Flutter}

  //2
  Map map2 = Map.of(map1);
  print(map2);              {one: Android, two: IOS, three: Flutter}

  //3. The object.fromentries () function passes in a list of key-value pairs and returns a new Object with those key-value pairs.
  // The iterator argument should be an object that implements the @iterator method and returns an iterator object. it
  // Generates an array-like object with two elements, the first being the value to be used as the property key, and the second being the value associated with the property key.
  Map map3 = Map.fromEntries(map1.entries);
  print(map3);

  //4. Directly declare, directly assign key to map of type String
  Map map4 = {'one':'Android'.'two':'IOS'.'three':'Flutter'};
  print(map4);              {one: Android, two: IOS, three: Flutter}

  Create an empty Map
  Map map5 = Map.identity();
  print(map5);              // Output: {}


  //6. Create immutable Map
  Map map6 = const {'one':'Android'.'two':'IOS'.'three':'flutter'};
  print(map6);              {one: Android, two: IOS, three: flutter}

  //7. Create (copy) a new unmodifiable map7 on the target map6
  Map map7 = Map.unmodifiable(map6);
  print(map7);              {one: Android, two: IOS, three: flutter}

  //8. Create a map whose key is int
  Map map8 = {1:'Android'.2:'IOS'.3:'Flutter'};
  print(map8);              {1: Android, 2: IOS, 3: Flutter}

  //9. Create a map based on the key value provided by list
  List<String> keys = ['one'.'two'];
  List<String> values = ['Android'.'IOS'];
  Map map9 = Map.fromIterables(keys, values);
  print(map9);               {one: Android, two: IOS}
  
   // Use the builder to create the Map
   Map map10 = new Map();
   // Add value assignment to assign different types of maps
   map10["one"] = 'Android';
   map10["two"] = 'IOS';
   map10["three"] = 'Flutter';
   map10[4] = 'RN';
   print(map10);              {one: Android, two: IOS, three: Flutter, 4: RN}
Copy the code

Some apis commonly used by Map:

 // Create a Map. The key is an int and the value is a String
   var  map1 = new Map<int,String>();

   // Assign a value to the first position in the Map
   map1[0] = 'Android';
   // Assign to the second position of Map
   map1[1] = 'IOS';
   // Assign to the third value of Map
   map1[2] = 'flutter';
   // Assign a null value to Map
   map1[3] = null;
   // Since the key Value in the Map is unique, if the second key is entered, the Value will override the previous one
   map1[2] = 'RN';
   print(map1);                //{0: Android, 1: IOS, 2: RN, 3: null}

   // Get the length of the Map
   print(map1.length);         // Output: 4

   // Check whether Map is empty
   print(map1.isNotEmpty);     // Output result: true

   // Check whether Map is not empty
   print(map1.isEmpty);        // Output result: false

   // Retrieves whether the Map contains a Key
   print(map1.containsKey(1)); // Output: true

   // Retrieves whether the Map contains a Value
   print(map1.containsValue('Android'));  // Output: true

   // Delete a key-value pair
   map1.remove(0);
   print(map1);                {1: IOS, 2: RN, 3: null}

   // Get all keys
   print(map1.keys);           // output :(1, 2, 3)

   // Get all values
   print(map1.values);         // output :(IOS, RN, null)

   // Loop print
   /* key:1, value:IOS key:2, value:RN key:3, value:null */
     map1.forEach((key,value) {
     print("key:${key}, value:${value}");
   });

Copy the code

Summary:

  1. If the Map Key type is not specified, no error is reported if the Key type is inconsistent.
  2. The keys in the Map must be different. But value can be the same. Value can be an empty string or null.
  3. There are two ways to create a Map: through a constructor (new) and direct assignment.

13.Runes

In Dart, runes represents utF-32 code points for strings. Unicode defines a unique numeric value for each character, punctuation mark, emoji, and so on. What does that mean? In a writing system, each letter and number has a unique value. Since the Dart string is a sequence of CHARACTERS for UTF-16 code Units, a new syntax is required to express 32-bit Unicode values in strings, usually in the form of \uXXXX, where XXXX is four hexadecimal numbers. For example, the heart symbol 💗 is \u2665. For non-four values, place the number in braces. For example, the smiley face (😁) is \u{1f600}. The String class has properties that extract rune information. The codeUnitAt and codeUnit properties return 16-bit code units. Use the runes attribute to get the runes information for the string. The following example illustrates the relationship between RUNes, 16-bit code units, and 32-bit code points:

// Execute the program entry
void main(a){

  var clapp = '\u{1f44f}';
  print(clapp);                  // Output: 👏

  print(clapp.codeUnits);        // Output: [55357, 56399]
  print(clapp.runes.toList());   // Output: [128079]

  // Use the string. fromCharCodes method to display character graphs
  Runes input = new Runes(
      '\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}');
  print(new String.fromCharCodes(input));   ♥ 😅 😎 👻 🖖 😎

}
Copy the code

14.Symbols

A Symbolobject represents the operator or identifier declared by the Dart program. You may never use Symbol, but this feature is extremely valuable when referring to an identifier by name, especially if the code is obfuscated. The name of the identifier is obfuscated, but the name of the Symbol does not change.

#radix //#bar print(#n == new Symbol('n')); // Output: true var name = 'knight'; Symbol symbol = #name; print(symbol); Symbol("name") print(#name); // print: Symbol("name")Copy the code

15. The function method

Dart is a true object-oriented language where methods are also objects and have a type, Function. This means that methods can be assigned to variables or treated as arguments to other methods. You can also call an instance of the Dart class as a method. Here is an example of defining a method:

// define a method to determine whether the subscript of the list is null
bool isNoble(int atomicNumber) {
  returnlist[atomicNumber] ! =null;
}
Copy the code

While Effective Dart recommends using static typing on public APIs, it is of course possible to ignore type definitions:

// Define a method to determine whether the subscript of the list is null regardless of the type definition
isNoble(int atomicNumber) {
  returnlist[atomicNumber] ! =null;
}
Copy the code

For methods that have only one expression, you can choose to define them using abbreviated syntax:

// Define a method to determine if the subscript of a list is null
bool isNoble(int atomicNumber) => list[atomicNumber] ! =null;
Copy the code

=> expr is syntax {return expr; } form. The => form is also called fat arrow syntax. Note: in arrows (=>) and colons (;) Only one expression can be used, not statements. Methods can have two types of parameters: required and optional. Required parameters precede the parameter list, followed by optional parameters.

15.1.Optional Parameters

What are optional parameters? When you define a function, the parameter can be defined as an optional parameter, and when the method is called, the optional parameter may not be passed. Optional parameters can be named or location-based, but neither can be considered optional at the same time.

15.1.1.Optional named Parameters (Optional named parameters)

When calling a method, you can use the form paramName:value to specify named parameters. Such as:

enableFlags(bold: true, hidden: false);
Copy the code

To define a method, use {param1,param2… } to specify optional named arguments:

enableFlags({bool bold, bool hidden}) {
  // ...
}
Copy the code
15.1.2.Optional Positional Parameters

Some method arguments become optional positional arguments by placing them in [], as in the following example:

// Define a method where [String device] is an optional positional argument, meaning that the method can be called without passing this argument
String say(String from, String msg, [String device]) {
  var result = '$from says $msg';
  if(device ! =null) {
    result = '$result with a $device';
  }
  return result;
}

// Call a method without optional arguments
assert(say('Bob'.'Howdy') = ='Bob says Howdy');

// Call the method with optional arguments
assert(say('Bob'.'Howdy'.'smoke signal') = ='Bob says Howdy with a smoke signal');
Copy the code
15.1.3.Default Parameter Value

When defining methods, you can use = to define default values for optional arguments. The default value can only be compile-time constants. If no default value is provided, the default value is null. Here is an example of setting the default values for optional parameters:

// Define a method whose return type is null. Hidden is false by default
void enableFlags({bool bold = false,bool hidden = false}){}// If the calling method does not pass hidden, the default value is false
enableFlags(bold:true);
Copy the code

The following example shows how to set the default value of the location parameter:

// Define a method whose location the default of the optional location parameter device is Carrier Pigon
// When this method is called without passing the parameter, the default value is taken
String say(String from, String msg,
    [String device = 'carrier pigeon', String mood]) {
  var result = '$from says $msg';
  if(device ! =null) {
    result = '$result with a $device';
  }
  if(mood ! =null) {
    result = '$result (in a $mood mood)';
  }
  return result;
}

// Call the above method
assert(say('Bob'.'Howdy') = ='Bob says Howdy with a carrier pigeon');
Copy the code

You can also use list or map as defaults. The following example defines a method doStuff() and specifies default values for the list and Gifts arguments, respectively:

// Execute the program entry
void main(a){

  // Call to determine whether the value of the corresponding subscript is null
 // print(isNoble(1)); // Output: true


  doStuff();    List: [1, 2, 3]
                // gifts: {first: paper, second: cotton, third: leather}


}

// Both List and Map take their default values
void doStuff(
    {List<int> list = const [1.2.3],
      Map<String, String> gifts = const {
        'first': 'paper'.'second': 'cotton'.'third': 'leather'
      }}) {
  print('list: $list');
  print('gifts: $gifts');
}
Copy the code

15.2.The main() function

Every application needs a top-level main() entry method to execute. The main() method returns void and has an optional List

argument. Here is a web application’s main() method:

void main(a) {
  querySelector("#sample_text_id")
    ..text = "Click me!"
    ..onClick.listen(reverseText);
}
Copy the code

In the above code.. Y syntax is a cascading call. With cascading invocation, you can perform multiple operations on a single object. Here is a command line application of the main() method with method arguments as input arguments:

// Run the app like this: dart args.dart 1 test
void main(List<String> arguments) {
  print(arguments);

  assert(arguments.length == 2);
  assert(int.parse(arguments[0= =])1);
  assert(arguments[1] = ='test');
}
Copy the code

Functions as first-class objects(Functions as first-class objects)

You can call another method as an argument. Such as:

printElement(element) {
  print(element);
}

var list = [1.2.3];

// iterate over the collection
list.forEach(printElement);
Copy the code

Methods can also be assigned to a variable:

var loudify = (msg) => '!!!!!! ${msg.toUpperCase()} !!! ';
assert(loudify('hello') = ='!!!!!! HELLO !!! ');
Copy the code

15.4.Anonymous Functions

Most methods have names, such as main() or printElement(). You can also create a nameless method called an anonymous method, sometimes called a lambda or clourse closure. An anonymous method can be assigned to a variable and then used, for example, to add or remove a collection. Anonymous functions are similar to named functions in that arguments can be defined between parentheses, separated by commas, or can be optional. The following code in braces is the function body:

([[Type] param1[, …]]) { 
  codeBlock; 
}; 
Copy the code

The following code defines an anonymous function that takes I. This function is called to print out each element in the list and calculate the index position of each element in the list.

var list = ['apples'.'oranges'.'grapes'.'bananas'.'plums'];
list.forEach((i) {
  print(list.indexOf(i).toString() + ':' + i);
});
/ / output:

0: apples
1: oranges
2: grapes
3: bananas
4: plums
Copy the code

If the method contains only one statement, you can use the fat arrow syntax abbreviation to change the above code to the same meaning as the following:

list.forEach((i) => print(list.indexOf(i).toString() + ':' + i));
Copy the code

15.5. Static scope

Dart is a statically scoped language, and variable scoping is missing when writing code. Basically, variables defined inside curly braces can only be accessed inside curly braces, similar to Java scopes.

var topLevel = true;

main() {
  var insideMain = true;

  myFunction() {
    var insideFunction = true;

    nestedFunction() {
      var insideNestedFunction = true;

      assert(topLevel);
      assert(insideMain);
      assert(insideFunction);
      assert(insideNestedFunction); }}}Copy the code

Note that nestedFunction can access all variables, including top-level variables.

Lexical closures 15.6.Lexical closures

A closure is a method object that can access variables in its scope no matter where the object is called, and a method can enclose variables defined in its scope. Methods can enclose variables defined in their scope. In the following example, makeAdder() captures the variable addBy. The addBy argument can be used wherever the function returned by makeAdder() is executed:

Function makeAdder(num addBy) {
  return (num i) => addBy + i;
}

main() {
  // Create a function that adds 2.
  var add2 = makeAdder(2);

  // Create a function that adds 4.
  var add4 = makeAdder(4);

  assert(add2(3) = =5);
  assert(add4(3) = =7);
}
Copy the code

15.7.Testing functions for equality

Here is an example of testing the equality of top-level methods, static functions, and instance functions:

foo() {}               // Top-level methods

class A {
  static void bar(a) {} // Static method
  void baz(a) {}        // Instance method
}

void main(a) {
  var x;

  // Compare top-level functions
  x = foo;
  print(foo == x);    // Output: true

  // Compare static methods
  x = A.bar;
  print(A.bar == x);  // Output: true

  // Compare instance methods
  var v = new A(); 1 / / instance
  var w = new A(); 2 / / instance
  var y = w;
  x = w.baz;

  // These closures refer to the same instance
  // All are equal
  print(y.baz == x);   // Output: true

  // Closures refer to different instances, so they are not equalprint(v.baz ! = w.baz);// Output: true
}
Copy the code

15.8.Return values

All functions return a value. If no return value is specified, the statement returns NULL by default. As the end of the function ☝ a statement is executed.

16. Operators Operators

%
= =
&&

void main(a) {
   var n = 2;
   var i = 2;
   var d = 7;
   if((n % i == 0) && (d % i == 0)){
     print('Eligible');
   }else{
     print('Not eligible');       // Enter here
   }

   if(n % i == 0 && d % i == 0){
     print('Eligible');
   }else{
     print('Not eligible');       // Enter here}}Copy the code

16.1. Arithmetic operators

16.2. Equal and related operators

16.3. Type determination operators

obj
T
obj is T
obj is Object
as
is

if (emp is Person) { // Type check
  emp.firstName = 'Bob';
}
Copy the code

Using the AS operator simplifies the above code:

(emp as Person).firstName = 'Bob';
Copy the code

Note: The above two code effects are different. If emP is null or not of type Person, the first example using IS does not execute the code in the condition, while the second case using AS raises an exception.

16.4. Assignment operators

Assign using the = operator. But there’s another…? = operator to specify the value of a variable with a value of null.

a = value;   // Assign a value to variable Ab ?? = value;// If b is null, assign to b;
             // If not null, the value of b remains unchanged
Copy the code

There are also compound assignments such as += :

var a = 2;           // Assign using =
a *= 3;              // Assign and multiply: a = a * 3
assert(a == 6);
Copy the code

Here’s how the compound assignment operator works:

16.5. Logical operators

16.6. Bit and shift operators

Dart can operate on a single digit. The following operator also applies to integers:

16.7. Conditional expressions

Dart has two special operators that can be used to replace if-else statements: condition? Expr1: expr2 If condition is true, expr1 is executed (and the result of execution is returned); Otherwise, expr2 is executed and the result is returned. expr1 ?? Expr2 If expr1 is non-null, return its value. Otherwise, exPR2 is executed and the result is returned. If the assignment is based on a Boolean expression, consider using? :

var finalStatus = m.isFinal ? 'final' : 'not final';
Copy the code

If the value is null based on a Boolean expression, consider using??

String toString(a) => msg ?? super.toString();

// The above code can be represented by the following code, the meaning is the same, the code is easy to understand but not concise
String toString(a) => msg == null ? super.toString() : msg;

String toString(a) {
  if (msg == null) {
    return super.toString();
  } else {
    returnmsg; }}Copy the code

16.8. Cascade operators

Cascade operators (..) Multiple functions can be called consecutively on the same object and member variables can be accessed. Using cascade operators avoids creating temporary variables and makes written code look smoother, such as:

querySelector('#button') // Get an object.
  ..text = 'Confirm'   // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed! '));
Copy the code

The first method, quertSelector, returns a Selector object. The subsequent cascade operators are members that call this object and ignore the value returned by each operation. The above code has the same function as the following code:

var button = querySelector('#button');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed! '));
Copy the code

Cascading calls can also be nested:

final addressBook = (newAddressBookBuilder() .. name ='jenny'
      ..email = '[email protected]'
      ..phone = (newPhoneNumberBuilder() .. number ='415-555-0100'
            ..label = 'home')
          .build())
    .build();
Copy the code

You need to be careful when using cascade operators on methods. For example, the following is illegal:

var sb = new StringBuffer();
sb.write('foo').. write('bar');
Copy the code

The sb.write function returns a void, and the cascade operator cannot be used on void. Note that concatenation is not an operator, just a syntax!

17.Control flow statements

Dart’s control flow statements are similar to the Java language:

17.1. If the else

if (isRaining()) {// Conditional statement
  you.bringRainCoat();/ / content
} else if (isSnowing()) {// Conditional statement
  you.wearJacket();/ / content
} else {
  car.putTopDown();/ / content
}
Copy the code

17.2. The for loop

You can use the standard for loop:

var message = new StringBuffer("Dart is fun");
for (var i = 0; i < 5; i++) {
  message.write('! ');
}

// Use foreach loops for lists and sets
List numbers = [1.2.3.4.5.6.7.8.9.10];
numbers.foreach((number)=> print(number));

// Use the for in loop, which is usually used for lists and sets
List numbers = [1.2.3.4.5.6.7.8.9.10];
for(var number in numbers){
     print(number);
}
Copy the code

17.3. While the and do – the While

The while determines whether the condition is met before executing the loop:

// Determine the condition
while(! isDone()) {/ / content
  doSomething();
}

/ / case
var i = 0;
while(i > 5){
    i++;
}
Copy the code

A do-while loop evaluates the condition after executing the loop code:

do {
  printLine();/ / content
} while(! atEndOfPage());// Conditional judgment
/ / case
var i = 0;
do{
    i++;
}while(i > 7);
Copy the code

17.4. Break and continue

Use break to terminate the loop:

while (true) {
  if (shutDownRequested()) break;
  processIncomingRequests();
}
Copy the code

Use continue to start the next loop:

for (int i = 0; i < candidates.length; i++) {
  var candidate = candidates[i];
  if (candidate.yearsExperience < 5) {
    continue;
  }
  candidate.interview();
}
Copy the code

The above code implements the Iterable interface objects (List and Map) as follows:

candidates.where((c) => c.yearsExperience >= 5)
          .forEach((c) => c.interview());
Copy the code

17.5. The Switch and the case

The Switch statement in Dart uses == to compare INTEGER, String, or compile-time constants. The comparisons must both be instances of the same class (and not like it), calss must have no overridden == operator, and every non-empty case statement must have a break statement. Non-null case statements can also be introduced with continue, throw, and return. When no case statement matches, the default statement can be used to match the default.

var command = 'OPEN';
switch (command) {
  case 'CLOSED':
    executeClosed();
    break;
  case 'PENDING':
    executePending();
    break;
  case 'APPROVED':
    executeApproved();
    break;
  case 'DENIED':
    executeDenied();
    break;
  case 'OPEN':
    executeOpen();
    break;
  default:
    executeUnknown();
}
Copy the code

The following example code omits the break statement in case and will compile with an error:

void main(a) {

  var number = 1;
  var i = 0;
  switch(number){
    case 1;
    i++;
    case 2:
      i--;
      break; }}Copy the code

Dart can omit the break statement in an empty case statement:

void main(a) {

  var number = 1;
  var i = 0;
  switch(number){
    case 1;
    i++;
    case 2:
      i--;
      break; }}Copy the code

If you want to continue to the next case statement, you can use the continue statement to jump to the corresponding label to continue execution:

void main(a) {

  var number = 1;
  var i = 0;
  switch(number){
    case 1;
    i++;
    case 2:
      i--;
      break; }}Copy the code

Each case statement can have local variables that are visible only within the statement.

17.6. The Assert (claim)

If the result of a condition expression does not meet your needs, you can use assert statements to interrupt code execution. The following is an example:

// Make sure the variable is non-null
assert(text ! =null);
// Make sure the value is less than 100
assert(number < 100);
// Make sure this is an HTTPS address
assert(urlString.startsWith('https'));
Copy the code

Assert method arguments can be any Boolean expression or method. If the value returned is true, the assertion execution passes, and execution ends. If the return value is false, the assertion fails and an exception is thrown. The assertion is only valid if it is running in check mode and not if it is running in production mode.

18. Exceptions (abnormal)

Exceptions can occur and catch exceptions in code. Exceptions represent unknown error conditions. If the exception is not caught, it is thrown, causing the code that threw it to terminate. Unlike Java, all Dart exceptions are non-checked exceptions. Methods don’t have to declare the exceptions they throw, and you don’t need to catch exceptions. Dart provides the Exception and Error types, as well as several subtypes. You can also define your own exception types. However, Dart code can throw any non-NULL object as an Exception, not just Exception and Error objects.

18.1. Throw

Here is an example of throwing or throwing an exception:

thow new FormatException('Expected at least 1 section');
Copy the code

We can also throw arbitrary objects:

throw 'Out of llamas! ';
Copy the code

Because an exception is an expression, it can be used in => statements and can be thrown anywhere else where an expression can be used.

distanceTo(Point other) =>
    throw new UnimplementedError();
Copy the code

18.2. The Catch

Catching an exception prevents the exception from being passed (rethrown). Catching an exception gives you an opportunity to handle it:

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  buyMoreLlamas();
}
Copy the code

For code that can throw multiple types of exceptions, you can specify multiple catch statements. Each statement corresponds to an exception type. If the catch statement does not specify an exception type, it can catch any exception type:

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // A specific exception
  buyMoreLlamas();
} on Exception catch (e) {
  // Anything else that is an exception
  print('Unknown exception: $e');
} catch (e) {
  // No specified type, handles all
  print('Something really unknown: $e');
}
Copy the code

As shown in the previous code, you can declare a catch statement using either on or catch, or both. Use on to specify the exception type and catch to catch the exception object. The catch() function can take one or two arguments, the first being the exception object thrown and the second being the stack information.

. }on Exception catch (e) {
  print('Exception details:\n $e');
} catch (e, s) {
  print('Exception details:\n $e');
  print('Stack trace:\n $s');
}
Copy the code

The rethrow keyword is used to rethrow caught exceptions:

final foo = ' ';

void misbehave(a) {
  try {
    foo = "You can't change a final variable's value.";
  } catch (e) {
    print('misbehave() partially handled ${e.runtimeType}.');
    rethrow; // Allow callers to see the exception.}}void main(a) {
  try {
    misbehave();
  } catch (e) {
    print('main() finished handling ${e.runtimeType}.'); }}Copy the code

18.3. The Finally

To ensure that some code executes, whether or not an exception is thrown, use the finally statement. If there is no catch statement to catch the exception, then after the finally statement is executed, the exception is thrown:

try {
  breedMoreLlamas();
} finally {
  // The exception will be executed even if it is thrown
  cleanLlamaStalls();
}
Copy the code

A finally statement is defined to execute after any matching catch statement:

try {
  breedMoreLlamas();
} catch(e) {
  print('Error: $e');  // Handle exceptions first
} finally {
  cleanLlamaStalls();  // Then execute
}
Copy the code

19.Classes

Dart is an object-oriented programming language that also supports mixin-based inheritance. Each object is an instance of a class, and all classes inherit from Object. Mixin-based inheritance means that each class (except Object) has only one superclass, and the code of one class can be reused in multiple other class inheritance. Use the new keyword and constructor to create a new object. The constructor name can be ClassName or classname.identifier. Such as:

var jsonData = JSON.decode('{"x":1, "y":2}');

Create an object of type Point
var p1 = new Point(2.2);

// Create a Point object based on json
var p2 = new Point.fromJson(jsonData);
Copy the code

The members of an object include methods and data (functions and sample variables). When you call a function, you are calling it on an object: the function needs to access the object’s methods and data, using (.). To refer to a variable or method of an object:

var p = new Point(2.2);

// Assign the variable y to the instance object
p.y = 3;

// Get the value from the member variable
assert(p.y == 3);

// Copy the instance object from p
num distance = p.distanceTo(new Point(4.4));
Copy the code

Use? To avoid throwing an exception when the left object is null:

// If P is not an empty object, then y is assigned to itp? .y =4;
Copy the code

Some classes provide constant constructors. To use the constant constructor to create compile-time constants, we simply replace new with const:

var p = const ImmutablePoint(2.2);
Copy the code

Two identical compile-time constants are the same object:

var a = const ImmutablePoint(1.1);
var b = const ImmutablePoint(1.1);

print(identical(a, b)); // They are the same instance, print true
Copy the code

The Type of an instance can be determined using the runtimeType property of Object, which returns a Type Object.

print('The type of a is ${a.runtimeType}');
Copy the code

19.1. The Instance variables

Here is an example of how to define instance variables:


class Point{
  num x;// Declare the instance variable x, initialize to null
  num y;// Declare the instance variable y to be empty
  num z = 0;// declare the instance variable z, initialized to 0
}
Copy the code

All uninitialized time-varying values are null. Each instance variable automatically generates a getter method. Non-final instance variables also generate a setter method:

class Point{
  num x;// Declare the instance variable x, initialize to null
  num y;// Declare the instance variable y to be empty
  num z = 0;// declare the instance variable z, initialized to 0
}
void main(a) {
  var point = new Point();
  point.x = 4;              // Assign to the x variable using setter methods
  print(point.x == 4);      // Output true uses the getter to get the value of the x variable
  print(point.y == null);   / / output true

}
Copy the code

If the variable is initialized when the instance variable is defined (not in a constructor or other method), the value is initialized when the instance object is instantiated, before the constructor and initialization parameter list are executed.

19.2. Constructors

Defining a method with the same name as the class defines a constructor that can also have other optional identifiers. Common constructors generate a new instance of an object:

class Point {
  num x;
  num y;

  Point(num x, num y) {
    this.x = x;
    this.y = y; }}Copy the code

This keyword refers to the current instance. Use this only when names conflict. Because scenarios where constructor parameters are assigned to instance variables are all too common, Dart provides a syntactic sugar to simplify this operation:

class Point {
  num x;
  num y;

  // Set the syntax sugar for x and y
  // Before the constructor body runs
  Point(this.x, this.y);
}
Copy the code
19.2.1.Default copnStructors (Default constructor)

If no constructor is defined, there is a default constructor. The default constructor has no arguments and calls the superclass’s no-argument constructor

Inherited 19.2.2.Constructors aren’t inherited

Subclasses do not inherit the constructor of a superclass. A subclass has only one default constructor if it does not define a constructor.

19.2.3.Named Constructors

You can use the named constructor to implement multiple constructors for a class, or you can use the named constructor to make your intentions clearer:

class Point {
  num x;
  num y;

  Point(this.x, this.y);

  // Name the constructor
  Point.fromJson(Map json) {
    x = json['x'];
    y = json['y']; }}Copy the code

Constructors cannot be inherited, so the superclass’s named constructor is not inherited. If a subclass has a superclass named constructor, it must implement the constructor itself in the subclass.

Invoking a non-default superclass constructor

By default, the subclass’s constructor automatically calls the nameless, no-argument default constructor of the superclass. The constructor of the superclass is called at the point where the subclass constructor body begins execution. If an Initializer list is provided, the initializer list is executed before the superclass constructor is executed. Here is the order in which constructors are executed:

  1. Initializer List (List of initialization parameters)
  2. Superclass’s no-arg constructor (nameless constructor for superclass)
  3. Main class’s no-arg constructor main class’s no-arg constructor

If the superclass has no nameless, no-parameter constructor, you need to manually call the other constructors of the superclass. Using a colon after the constructor argument: The superclass constructor can be called. In the following example, the Employee constructor calls the named constructor of the superclass Person:

// Define the Person class
class Person {
  String firstName;

  Person.fromJson(Map data) {
    print('in Person'); }}class Employee extends Person {
  // Person has no default constructor
  // you must call super.fromJson(data).
  Employee.fromJson(Map data) : super.fromJson(data) {
    print('in Employee');
  }
}

main() {
  var emp = new Employee.fromJson({});

  // Print the output
  // in Person
  // in Employee

}
Copy the code

Since the arguments to the superclass constructor are executed before the constructor is executed, the wipe can be either an expression or a method call:

class Employee extends Person {
  // ...
  Employee() : super.fromJson(findDefaultData());
}
Copy the code

If you use super() in the constructor initializer list, you need to put it last. Arguments calling the superclass constructor do not have access to this. For example, arguments can be static functions but not instance functions.

19.3.Initializer List

In addition to calling the superclass constructor, instance parameters can also be initialized before the constructor body is executed. Initialize expressions using commas:

class Point {
  num x;
  num y;

  Point(this.x, this.y);

  // The initializer sets the instance variable before the constructor body is run.
  Point.fromJson(Map jsonMap)
      : x = jsonMap['x'],
        y = jsonMap['y'] {
    print('In Point.fromJson(): ($x, $y)'); }}Copy the code

The part to the right of the equals sign of the initialization expression cannot access this. Initializer lists are great for setting the values of final variables. The initializer list in the following sample code sets the values of three final variables:

import 'dart:math';

export 'src/DartProject_base.dart';

// TODO: Export any libraries intended for clients of this package.


class Point {
 final num x;
 final num y;
 final num distanceFromOrigin;

 Point(x, y)
     : x = x,
       y = y,
       distanceFromOrigin = sqrt(x * x + y * y);
}

main() {
 var p = new Point(2.3);
 print(p.distanceFromOrigin);// Output: 3.605551275463989
}
Copy the code

19.4.Redirecting Constructors (Redirecting Constructor)

Sometimes a constructor invokes other constructors in the class. A redirection constructor has no code. After the constructor is declared, other constructors are called with colons.

class Point {
  num x;
  num y;

  // The main constructor
  Point(this.x, this.y);

  // Call the main constructor
  Point.alongXAxis(num x) : this(x, 0);
}
Copy the code

19.5.Constant Constructors

If your class provides a state invariant object, you can define these objects as compile-time constants. To do this, we need to define a const constructor and declare all class variables final.

class ImmutablePoint {
  final num x;
  final num y;
  const ImmutablePoint(this.x, this.y);
  static final ImmutablePoint origin =
      const ImmutablePoint(0.0);
}
Copy the code

19.6.Factory Constructors (Factory method constructors)

If a constructor does not always return a new object, use factory to define the constructor. For example, a factory constructor might take an instance from the cache and return it, or return an instance of a subtype. Example:

class Logger {
  final String name;
  bool mute = false;

  static final Map<String, Logger> _cache =
      <String, Logger>{};

  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final logger = new Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  Logger._internal(this.name);

  void log(String msg) {
    if(! mute) { print(msg); }}}Copy the code

Use the new keyword to call the factory constructor

var logger = new Logger('UI');
logger.log('Button clicked');
Copy the code

19.7. The function

A function is a method defined in a class and is the behavior of a class object.

19.7.1.Instance Methods

The distanceTo function is called in the following example:

import 'dart:math';

class Point {
  num x;
  num y;
  Point(this.x, this.y);

  num distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    returnsqrt(dx * dx + dy * dy); }}Copy the code

Getters and setters are special functions used to set and access object properties. Each instance variable implicitly has a getter, and a setter if the variable is not final. You can create new properties by implementing getters and setters, defining getters and setters using the get and set keywords:

class Rectangle {
  num left;
  num top;
  num width;
  num height;

  Rectangle(this.left, this.top, this.width, this.height);

  // Define two calculated properties: right and down.
  num get right             => left + width;
      set right(num value)  => left = value - width;
  num get bottom            => top + height;
      set bottom(num value) => top = value - height;
}

main() {
  var rect = new Rectangle(3.4.20.15);
  assert(rect.left == 3);
  rect.right = 12;
  assert(rect.left == -8);
}
Copy the code

The nice thing about getters and setters is that you can start using instance variables, and then you can wrap the instance variables in functions without changing where the code is called.

19.7.2.Abstract Methods

Instance functions, getters, and setters can be abstract functions. Abstract functions are functions that define a function interface but have no implementation, and are implemented by subclasses. A function is abstract if its body is replaced by a semicolon.

abstract class Doer {
  / /... Define instance variables and methods...

  void doSomething(a); // Define an abstract method.
}

class EffectiveDoer extends Doer {
  void doSomething(a) {
    / /... Provide the implementation, so the methods here are not abstract...}}Copy the code

Calling an unimplemented abstract function causes a runtime exception.

19.8.Overridable Operators

The operators in the following table can be overridden. For example, if you define a Vector class, you can define a + function to add two vectors.

+
-

class Vector {
  final int x;
  final int y;
  const Vector(this.x, this.y);

  // overwrite + (a + b).
  Vector operator +(Vector v) {
    return new Vector(x + v.x, y + v.y);
  }

  /// overwrite - (a-b).
  Vector operator -(Vector v) {
    return new Vector(x - v.x, y - v.y);
  }
}

main() {
  final v = new Vector(2.3);
  final w = new Vector(2.2);

  // v == (2, 3)
  assert(v.x == 2 && v.y == 3);

  // v + w == (4, 5)
  assert((v + w).x == 4 && (v + w).y == 5);

  // v - w == (0, 1)
  assert((v - w).x == 0 && (v - w).y == 1);
}
Copy the code

19.9.Abstract Classer

Use the abstract modifier to define an abstract class, one that cannot be instantiated. Abstract classes are usually used to define interfaces, as well as partial implementations. If the abstract class is instantiable, define a factory constructor. Abstract classes usually have abstract functions.

// This class is abstract and cannot be instantiated
abstract class AbstractContainer {
  / /... Define constructors, variables, methods...

  void updateChildren(a); // Abstract methods.
}
Copy the code

The following class is not abstract, but defines an abstract function so that such columns can be instantiated:

class SpecializedContainer extends AbstractContainer {
  / /... Define more constructors, methods...

  void updateChildren(a) {
    / /... Implement updateChildren ()...
  }

  //Abstract method causes a warning but doesn't prevent instantiation.Abstract methods cause warnings, but do not prevent instantiation.void doSomething(a);
}
Copy the code

19.10.Implicit interfaces

Each class implicitly defines an interface containing all instance members, and the class implements the interface. If you want to create class A to support the API of class B, but do not want to inherit the implementation of B, then class A should implement the interface of B:

// A person. An implicit interface contains greet().
class Person {
  // In the interface, but only visible in the library
  final _name;

  // Not in the interface, because this is a constructor
  Person(this._name);

  // In the interface
  String greet(who) = >'Hello, $who. I am $_name.';
}

// Implement the Person interface
class Imposter implements Person {
  // We have to define this, but we don't use it.
  final _name = "";

  String greet(who) = >'Hi $who. Do you know who I am? ';
}

greetBob(Person person) => person.greet('bob');

main() {
  print(greetBob(new Person('kathy')));
  print(greetBob(new Imposter()));
}
Copy the code

Here is an example of implementing multiple interfaces:

class Point implements Comparable.Location {
  // ...
}
Copy the code

Extending a class

Uses extends to define subclasses, and supper refers to superclasses:

class Television {
  void turnOn(a) {
    _illuminateDisplay();
    _activateIrSensor();
  }
  // ...
}

class SmartTelevision extends Television {
  void turnOn(a) {
    super.turnOn();
    _bootNetworkInterface();
    _initializeMemory();
    _upgradeApps();
  }
  // ...
}
Copy the code

Subclasses can override instance functions, getters, and setters. The following is an example of a noSuchMethod() function that overrides the Object class. The noSuchMethod() function is fired if a function is called that does not exist on the Object.

class A {
  // Using a nonexistent function will result in a NoSuchMethodError unless NoSuchMethod is overridden.
  void noSuchMethod(Invocation mirror) {
    print('You tried to use a non-existent member:' +
          '${mirror.memberName}'); }}Copy the code

We can also use the @Override annotation to indicate that the function is a function that wants to override the superclass:

class A {
  @override
  void noSuchMethod(Invocation mirror) {
    // ...}}Copy the code

If you use the noSuchMethod function to implement every possible getter, setter, and other type of function, you can use the @proxy annotation to avoid warning messages:

@proxy
class A {
  void noSuchMethod(Invocation mirror) {
    // ...}}Copy the code

If you want to know the specific type at compile time, you can implement these classes to avoid warnings, just like using @proxy:

class A implements SomeClass.SomeOtherClass {
  void noSuchMethod(Invocation mirror) {
    // ...}}Copy the code

19.12.Enumerated types(Enumeration types)

Enumeration types, commonly called Enumerations or enums, are special classes that represent a fixed number of constants. Use the enum keyword to define enumeration types:

enum Color {
  red,
  green,
  blue
}
Copy the code

Each value in an enumeration type has an IndexGetter function that returns the position of the value in the enumeration type definition (starting at 0), for example, the position of the first enumeration value is 0 and the position of the second is 1:

print(Color.red.index == 0); // Output: true
print(Color.green.index == 1);// Output: true
print(Color.blue.index == 2);// Output: true
Copy the code

The enumeration values constant returns all enumerated values

List<Color> colors = Color.values;
print(colors[2] == Color.blue);// Output: true
Copy the code

You can use enumerations in switch statements. If e in switch(e) is of type enumeration, a warning is raised if all values of that enumeration type are not processed:

enum Color {
  red,
  green,
  blue
}
// ...
Color aColor = Color.blue;
switch (aColor) {
  case Color.red:
    print('Red as roses! ');
    break;
  case Color.green:
    print('Green as grass! ');
    break;
  default: // If this is not available, you will see a warning
    print(aColor);  // 'Color.blue'
}
Copy the code

Enumerated types have the following restrictions:

  1. Cannot inherit enumeration type and cannot be usedmixinCannot implement an enumerated type
  2. Unable to initialize an enumerated type for display

19.13.Adding features to a class: Mixins

Mixins are a method of reusing the code of a class in multi-class inheritance. Mixins are used by using one or more Mixins names after the with keyword.

class Musician extends Performer with Musical {
  // ...
}

class Maestro extends Person
    with Musical.Aggressive.Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true; }}Copy the code

Define a class that inherits Object, has no constructor, and cannot call super, and that class is a mixin. Here’s an example:

abstract class Musical {
  bool canPlayPiano = false;
  bool canCompose = false;
  bool canConduct = false;

  void entertainMe(a) {
    if (canPlayPiano) {
      print('Playing piano');
    } else if (canConduct) {
      print('Waving hands');
    } else {
      print('Humming to self'); }}}Copy the code

Starting from Dart1.13, Mixins can inherit from other classes. Instead of being limited to Object, Mixins can call super().

Variables and methods(Class variables and functions)

Use the static keyword to implement level variables and functions.

19.14.1.Static variables

Static variables are useful for the state of categories:

class Color {
  static const red =
      const Color('red'); // Statically construct variables.
  final String name;      // Static instance variables.
  const Color(this.name); // constructor.
}

main() {
  print(Color.red.name == 'red'); // Output: true
}
Copy the code

Static variables are initialized the first time they are used.

19.14.2.Static Methods

Static functions are no longer instantiated, so this cannot be accessed:

import 'dart:math';

class Point {
  num x;
  num y;
  Point(this.x, this.y);

  static num distanceBetween(Point a, Point b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
  }
}

main() {
  var a = new Point(2.2);
  var b = new Point(4.4);
  var distance = Point.distanceBetween(a, b);
  assert(distance < 2.9 && distance > 2.8);
}
Copy the code

For generic or frequently used static functions, consider using top-level methods rather than static functions, which can also be used as compile-time constants, e.g. as arguments to constant constructors.

20. Generics

When you look at the API documentation for the List type, you can see that the actual type is defined as List

. The <.. > declare list to be a generic (or parameterized) type. Typically, a single letter is used to represent type parameters, such as E, T, S, K, and V.

20.1. According to the use of generics? (Why generics?)

Types are optional in Dart, and you can opt out of generics. There are many situations where you can use types to make your intentions clear, whether you’re using generics or concrete types. For example, if you want your List to contain only string objects. List

represents (” List of String “). Developers or their colleagues can help check if their code places non-string objects in the list, as shown below:

main() {
   List Tech = new List<String>();
   Tech.addAll(['Android'.'IOS'.'Flutter']);
   Tech.add(42);// Run an error
}
Copy the code

Another reason to use generics is to reduce code duplication. Generics can define the same implementation across multiple types, while continuing to use the code analysis capabilities provided by check patterns and static analysis tools. For example: create an interface to store cached objects:

abstract class ObjectCache {
  Object getByKey(String key);
  setByKey(String key, Object value);
}
Copy the code

It turns out that we need an implementation for caching strings, which in turn defines an interface:

abstract class StringCache {
  String getByKey(String key);
  setByKey(String key, String value);
}
Copy the code

However, one implementation was needed to cache numbers, another type of cache implementation was needed later, and so on… This is where generics come in. Generics can avoid code duplication, for example:

abstract class Cache<T> {
  T getByKey(String key);
  setByKey(String key, T value);
}
Copy the code

In the code above, T is an alternate type, which is a type placeholder that is specified when you call the interface yourself.

20.2.Using collection literals

List and Map literals can also be parameterized. To parameterize a list, add

between the braces, and to define a map, add

before the braces. If you need more secure type checking, you can use parameterized definitions. Examples are as follows:
,valuetype>

var names = <String>['Seth'.'Kathy'.'Lars'];
var pages = <String, String>{
  'index.html': 'Homepage'.'robots.txt': 'Hints for web robots'.'humans.txt': 'We are people, not machines'
};
Copy the code

20.3.Using parameterized types with constructors

When calling the constructor, use Angle brackets after the class name (<… >) to specify generic types. Such as:

var names = new List<String>();
names.addAll(['Seth'.'Kathy'.'Lars']);
var nameSet = new Set<String>.from(names);
Copy the code

Create a map with key integer and value View:

var views = new Map<int, View>();
Copy the code

20.4.Generic Collections and the types they contain

Dart’s generic types are fixed, and specific types can be determined at run time. For example, object types in collections can be detected at run time:

var names = new List<String>();
names.addAll(['Seth'.'Kathy'.'Lars']);
print(names is List<String>); // Output: true
Copy the code

The IS expression only determines the type of the collection, not the type of the specific objects in the collection. In production mode, the List

variable can contain non-string objects. In this case, you can either subcontract to determine the type of each object or handle type conversion exceptions. Generics information in Java is compile-time, and generics information is not pure at runtime. In Java, you can test whether an object is a List, but you cannot test whether an object is a List

.

Restricting the action to terrized Type (Restrict generic types)

When using generic types, you might want to restrict the specific type of the generic. Extends provides this functionality:

// T must be SomebaseClass or one of its descendants.
class Foo<T extends SomeBaseClass> {... }class Extender extends SomeBaseClass {... }void main(a) {
  // You can use someBaseclass or any of its subclasses in <>.
  var someBaseClassFoo = new Foo<SomeBaseClass>();
  var extenderFoo = new Foo<Extender>();

  // You can also use no<>
  var foo = new Foo();



   // Specifying any non-someBaseclass type will result in a warning, run-time error in Debug check mode.
  // var objectFoo = new Foo<Object>();
}
Copy the code

20.6.Using generic methods

Initially, generics can only be used in Dart classes. The new syntax also supports the use of generics for functions and methods.

T first<T>(List<T> ts) {
  / /... Do some initialization or error checking...T tmp ? = ts[0];
  / /... Do some extra tests or treatments...
  return tmp;
}
Copy the code

Here the first(

) generic can use the argument T as follows:

  • Function return value type (T)
  • Parameter type (List<T>)
  • Type of local variable (T tmp)

Start using generic functions in Dart1.21. If you want to use generic functions, you need to set the SDK version to 1.21 or later.

21.Libraries and visibility

Use import and library directives to help create modular shareable code. Libraries not only provide apis, they are also private units: identifiers starting with an underscore (_) are visible only inside the library. Each Dart app is a library, even without using the library command. The library here is similar to the Android library, which is simply to use someone else to write the library’s API. For example: photo library, network request library and so on.

21.1.Using libraries

Dart Web applications typically use the Dart: HTML library, and you can then import the library as follows:

import 'dart:html';
Copy the code

Import must take the URL of the library. For built-in libraries, the URIs use the special DART :scheme. For other libraries, you can use file system paths or package:scheme. Package: Scheme-specified libraries are provided through package managers, such as the pub utility, such as:

import 'dart:io';
import 'package:mylib/mylib.dart';
import 'package:utils/utils.dart';
Copy the code

21.2.Specify a library prefix

If the imported libraries have conflicting identifiers, which is often the case, you can use the library prefix to distinguish them. For example, if library1 and library2 both have a class named Element, we can use it like this:

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// ...
Element element1 = new Element();           // 使用 lib1 中的 Element .
lib2.Element element2 = new lib2.Element(); // Use Element in lib2.
Copy the code

21.3.Importing only Part of a Library

If only part of the library is used, you can select what you want to import, for example:

// Import only foo
import 'package:lib1/lib1.dart' show foo;

// Import all names except foo.
import 'package:lib2/lib2.dart' hide foo;
Copy the code

21.4.Lazily loading a library

Deferred loading allows applications to load libraries when needed. Here are some scenarios for using lazy-loaded libraries:

  • Reduce APP startup time
  • Perform A/B testing, such as trying different implementations of various algorithms
  • Load little-used features, such as optional screens and dialogs

Deferred as is used to load a library lazily:

import 'package:deferred/hello.dart' deferred as hello;
Copy the code

When needed, call the loadLibrary() function with the library identifier to load the library:

greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}
Copy the code

In the code above, use the await keyword to suspend code execution until the library is loaded. The loadLibrary() function can be called multiple times on a library. But the library is only loaded once. When using lazy-loaded libraries, note:

  • Lazy-loaded library constants are not available at import time. Library constants are available only when the library is loaded.
  • Types in the delay library cannot be used when importing files. If you need to use types, consider moving the interface type into another library and having both libraries import it separately.
  • Dart implies thatloadLibrary()Function import to useDeferred AS namespace.loadLibrary()Method returns aFuture.

22.Asynchrony Support

Dart has several language features to support asynchronous programming. The most common features are async methods and await expressions. The Dart library has many methods for returning Future or Stream objects. These methods are asynchronous: the functions return after setting up basic operations without waiting for execution to complete. For example, read a file and return it after the file is opened. There are two ways to use data in a Future object:

  • useasyncandawait
  • A Future API

Similarly, there are two ways to get data from a Stream:

  • useasyncAnd an asynchronous for loop (await for)
  • Using Stream API

Code that uses async and await is asynchronous, but looks a bit like synchronous code. Here is an example of waiting for an asynchronous method to return with await:

await lookUpVersion(a)
Copy the code

To use await, the method must have the async keyword:

checkVersion() async {
  var version = await lookUpVersion(a);
  if (version == expectedVersion) {
    // Body content.
  } else {
    // Body content.}}Copy the code

We can use try,catch, and finally to handle exceptions that use await:

try {
  server = await HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, 4044);
} catch (e) {
  // Respond to unable to bind to port...
}
Copy the code

22.1. Literal async functions(declare asynchronous methods)

An async method is a method whose function body is marked async. Although the execution of an asynchronous method may take some time, the asynchronous method returns immediately – before the method body has even been executed.

checkVersion() async {
  // ...
}

lookUpVersion() async => / *... * /;
Copy the code

Add the async keyword to a method that returns a Future. For example, here is a synchronization method that returns a string:

String lookUpVersionSync(a) = >'1.0.0';
Copy the code

If the async keyword is used, the method returns a Future and considers the function to be a time-consuming operation.

Future<String> lookUpVersion(a) async = >'1.0.0';
Copy the code

Note that the function body of a method is not required to use the FutureAPI. Dart automatically creates Future objects when needed.

22.2.Using await expressions with Futures

Await expressions have the form:

await expression
Copy the code

You can use multiple await expressions to express water within an asynchronous method. For example, the following example uses three await expressions to perform related functions:

var entrypoint = await findEntrypoint(a);
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
Copy the code

In await expression, the return value of expression is usually a Future; If the value returned is not a Future, Dart automatically returns that value in the Future. The Future object represents the promise to return an object. The result of the await expression execution is the returned object. Await expression blocks the host until the desired object is returned. If await cannot be used properly, make sure it is in an async method. For example, to use await in the main() method, the function body of the main() method must be marked async:

main() async {
  checkVersion();
  print('In main: version is ${await lookUpVersion()}');
}
Copy the code

22.3.Using asynchronous for loops with Stream

An asynchronous for loop has the following form:

await for (variable declaration in expression) {
  // Executes each time a stream is emitted.
}
Copy the code

The above expression must return a value of type Stream. The execution process is as follows:

  1. Wait until the stream returns a data
  2. Execute the for loop code with the arguments returned by stream
  3. Repeat 1 and 2 until the stream data is returned

Use a break or return statement to stop receiving stream data, thus picking out the for loop and unregistering the stream. If the ** asynchronous for loop does not work properly, be sure to use it in an async method. ** To use asynchronous for loops in main(), mark the body of the main() function as async:

main() async {
  ...
  await for (var request in requestServer) { handleRequest(request); }... }Copy the code

23.Callable classes(Callable classes)

Dart can be called as a method if it implements the call() function. In the following example, the wannabeFunction class defines a call() method that takes three string arguments and returns the result of three strings concatenated:


class WannabeFunction {
  call(String a, String b, String c) => '$a $b $c! ';
}

main() {
  var wf = new WannabeFunction();
  var out = wf("Hi"."there,"."gang");
  print('$out');   // Output :Hi there, gang!
}
Copy the code

24.Isolates

Modern browsers and mobile browsers run on multi-core CPU systems. To make full use of these cpus, developers typically use shared memory data to make multithreading work properly. However, sharing data with multiple threads often leads to a lot of potential problems and causes code to run incorrectly, with unintended results. All Dart code runs in Extensions, not threads. Each ISOLATE has its own heap memory and ensures that the status of each ISOLATE is not accessible to other isolates.

25.Typedefs

In the Dart language, methods are also objects. Use a typedef or function-type-alias to name method types. Then you can use named methods that retain type information when assigning a method type to a variable. The following code does not use a typedef:

class SortedCollection {
  Function compare;

  SortedCollection(int f(Object a, Object b)) { compare = f; }}// Initial, broken implementation.
 int sort(Object a, Object b) = >0;

main() {
  SortedCollection coll = new SortedCollection(sort);

  // Compare is a Function;
  // What is the Function type?
  assert(coll.compare is Function);
}
Copy the code

When f is assigned to compare, the type information is lost, and f is of type (object,object)->int which of course is a Function. If you use an explicit name and retain type information, developers and tools can use this information:

typedef int Compare(Object a, Object b);

class SortedCollection {
  Compare compare;

  SortedCollection(this.compare);
}

 // Initial, broken implementation.
 int sort(Object a, Object b) = >0;

main() {
  SortedCollection coll = new SortedCollection(sort);
  assert(coll.compare is Function);
  assert(coll.compare is Compare);
}
Copy the code

26.Metadata

Use metadata to add extra information to your code, starting with the @ character followed by a compile-time constant or calling a constant constructor. There are three annotations that all Dart code can use: @deprecated, @Override, and @proxy.

class Television {
  /// deprecated, please use [open]
  @deprecated
  void activate(a) {
    turnOn();
  }

  /// Turn on the TV.
  void turnOn(a) {
    print('on! '); }}Copy the code

You can define your own metadata annotations. The following example defines an @todo annotation with two parameters:

library todo;

class todo {
  final String who;
  final String what;

  const todo(this.who, this.what);
}

Copy the code

Examples of @todo annotations:

import 'todo.dart';

@todo('seth'.'make this do something')
void doSomething(a) {
  print('do something');
}
Copy the code

Metadata can be used before library, typedef, type parameter, constructor, factory, function, field, parameter, or variable declarations. It can also be used before import or export directives, and reflection can be used to retrieve metadata information at runtime.

Five, the summary

Dart syntax is very similar to Java syntax. It is easy to get started and understand. Why should I learn Dart syntax? The explanation was clear from the beginning, but it was all about laying the foundation. What to learn must have a purpose to learn, the following directly on the picture:

Learning Resources:

  1. Start – the Dart
  2. Dart tutorial for Java developers

Additional knowledge

Flutter has four modes of operation: Debug, Release, Profile, and Test.

  1. Debug: Debug mode can run on both the real machine and the simulator: all assertions are enabled, including debugging messages, debugger AIDS (such as observatory), and service extensions.
  2. Release: The Release mode runs only on the real machine, not on the emulator: disables all assertions and debugging information, and all debugger tools.
  3. Profile: Profile mode runs only on the real machine, not on the emulator: basically the same as Release mode.
  4. Test: The headless test mode can run only on the desktop. The headless test mode is the same as the Debug mode.

Flutter operates in four ways