reference resources: https://dart.cn/guides/language/language-tour
Dart Development Language Overview
This article will introduce you to the main functions of Dart programming language from variables and operators to the use of classes and libraries. It is assumed that you have experience in programming in other languages.
You can view Dart library overview Learn more about Dart core library. For more details on language features, see Dart programming language specification.
Notes:
You can experience most of Dart's language functions through DartPad( Learn more), Open DartPad.
Some DartPads are embedded in this page for example display,
If you only see blank boxes (without any content), please refer to DartPad FAQ page.
A simple Dart program
The following application code uses many of Dart's basic functions:
// Define a function. void printInteger(int aNumber) { print('The number is $aNumber.'); // Print to console. } // This is where the app starts executing. void main() { var number = 42; // Declare and initialize a variable. printInteger(number); // Call a function. }
The following are the code snippets used in the above applications. These code snippets are applicable to all (or almost all) Dart applications:
// This is a comment.
//Notes.
A line beginning with a double slash is called a single line comment. Dart also supports multiline comments and document comments. consult notes For more information.
void
A special type that indicates that a value will never be used. Functions like main() and printInteger() return types declared as void and do not return values.
int
Another data type that represents an integer number. Some other in Dart Built in type Including String, List and bool.
42
Represents a numeric literal. A numeric literal is a compile time constant.
print()
A convenient way to output and display information.
'...' (or "...")
Represents a string literal.
variableName (or {expression})
Represents string interpolation: a variable or expression contained in a string literal. consult character string For more information.
main()
A special and necessary top-level function from which Dart applications always execute. consult main() function For more information.
var
Used to define variables. Defining variables in this way does not require specifying variable types.
Notes:
The code of this site follows Dart style guide Agreement in.
Important concepts
When learning Dart language, you should keep the following points in mind:
- All variables refer to objects, and each object is an instance of a class. Numbers, functions, and null s are objects. All classes inherit from Object Class.
- Although Dart is a strongly typed language, specifying the type when declaring a variable is optional because Dart can infer the type. In the above code, the type of variable number is inferred as int. If you want to explicitly declare an indeterminate type, you can Use special type dynamic.
- Dart supports generics, such as list < int > (representing a list of int objects) or list < dynamic > (representing a list of objects of any type).
- Dart supports top-level functions (such as the main method), as well as defining functions belonging to classes or objects (that is, static and instance methods). You can also define functions (nested or local functions) in functions.
- Dart supports top-level variables and defines variables belonging to classes or objects (static and instance variables). Instance variables are sometimes called domains or properties.
- Dart has no public, protected and private member access qualifiers like Java. If an identifier is underlined () The beginning indicates that the identifier is private in the library. Available for reference Library and visibility For more information.
- Identifiers can be letters or underscores () It can be followed by a combination of characters and numbers.
- There are differences between expressions and statements in Dart. Expressions have values but statements do not. such as Conditional expression expression condition ? Expr1: expr2 contains values expr1 or expr2. And If else branch statement In contrast, if else branch statements have no value. A statement usually contains one or more expressions, but an expression cannot contain only one statement.
- The Dart tool can display both warning and error types of problems. A warning indicates that there may be a problem with the code, but it will not prevent it from running. Errors are divided into compile time errors and run-time errors; Compile time error code cannot run; Runtime errors can result when code runs abnormal.
Notes:
If you wonder why Dart uses underscores instead of modifiers such as public or private, see SDK topics #33383.
keyword
The following table lists the keywords used by the Dart language.
abstract 2 | else | import 2 | super |
---|---|---|---|
as 2 | enum | in | switch |
assert | export 2 | interface 2 | sync 1 |
async 1 | extends | is | this |
await 3 | extension 2 | library 2 | throw |
break | external 2 | mixin 2 | true |
case | factory 2 | new | try |
catch | false | null | typedef 2 |
class | final | on 1 | var |
const | finally | operator 2 | void |
continue | for | part 2 | while |
covariant 2 | Function 2 | rethrow | with |
default | get 2 | return | yield 3 |
deferred 2 | hide 1 | set 2 | |
do | if | show 1 | |
dynamic 2 | implements 2 | static 2 |
You should avoid using these words as identifiers. However, words with superscripts can be used as identifiers if necessary:
- Keywords with superscript 1 are context keywords, which are meaningful only in specific scenarios. They can be used as valid identifiers anywhere.
- Keywords with superscript 2 are built-in identifiers, which are only easier when JavaScript code is converted to Dart code. These keywords can be used as valid identifiers most of the time, but they cannot be used as class names or type names or as import prefixes.
- The keyword with superscript 3 is Dart 1.0, which is used after publishing Support asynchronous Relevant contents. await or yield cannot be used as an identifier in a method body identified by the keywords async, async * or sync *.
Other keywords without superscript are reserved words and cannot be used as identifiers.
variable
The following example code creates a variable and initializes it:
var name = 'Bob';
Variables store only references to objects. Here, the variable named name stores a reference to an object of type String, and "Bob" is the value of the object.
The type of the name variable is inferred as String, but you can specify the type for it. If the reference of an object is not limited to a single type, you can Design Guide Specify it as Object or dynamic type.
dynamic name = 'Bob';
In addition, you can also specify the type:
String name = 'Bob';
Notes:
This article follows Style suggestion Guide As suggested in, declare local variables through var instead of using the specified type.
Default value
In Dart, uninitialized variables have a default initialization value: null. Even numbers are the same, because everything is an object in Dart, and numbers are no exception.
int lineCount; assert(lineCount == null);
Notes:
The call to assert() will be ignored in the production code. During development, assert(condition) will throw an exception when the condition is judged to be false. For details, please refer to Assert.
Final and Const
If you don't want to change a variable, you can use the keyword final or const to modify the variable. These two keywords can replace the var keyword or be added before a specific type. A final variable can only be assigned once; A const variable is a compile time constant (const variable is also final). The final variable of the top level or the final variable of the class is initialized when it is used for the first time.
Notes:
The instance variable can be final but not const. The final instance variable must be initialized before the constructor starts. For example, when declaring the instance variable, it can be used as a constructor parameter or placed in the constructor Initialization list Yes.
In the following example, we create and set two final variables:
final name = 'Bob'; // Without a type annotation final String nickname = 'Bobby';
You cannot modify the value of a final variable:
name = 'Alice'; // Error: a final variable can only be set once.
Modify a variable with the keyword const to indicate that the variable is a compile time constant. If you use const to modify variables in a class, you must add the static keyword, static const. When declaring const variables, you can directly assign values to them, or you can use other const variables to assign values to them:
const bar = 1000000; // Direct assignment [Unit of pressure (dynes/cm2)] const double atm = 1.01325 * bar; // Assign values using other const variables (Standard atmosphere)
Const keyword can be used not only to define constants, but also to create constant values that can be assigned to any variable. You can also declare the constructor const, and the object created by this type of constructor is immutable.
var foo = const []; final bar = const []; const baz = []; // Equivalent to 'const []' (equivalent to 'const []')
If you use an initialization expression to assign a value to a constant, you can omit the keyword const. For example, const is omitted from the assignment of the above constant baz. For details, please refer to Do not use const redundantly.
The values of variables that are not decorated with final or const can be changed, even if they have previously referenced const values.
foo = [1, 2, 3]; // The value of foo was const [] (Was const [])
The value of a constant cannot be modified:
baz = [42]; // Error: constant cannot be assigned. (Error: Constant variables can't be assigned a value.)
You can use it in constants Type checking and cast (is and as) if in collection as well as Expand operator (... And...?):
const Object i = 3; // Where i is a const Object with an int value... const list = [i as int]; // Use a typecast. const map = {if (i is int) i: "int"}; // Use is and collection if. const set = {if (list is List<int>) ...list}; // ...and a spread.
Memo: Although a final object cannot be modified, its fields can be changed In comparison, a const object and its fields cannot be changed: they’re immutable.
Available for reference Lists,Maps and Classes Get more information about using const to create constant values.
Built in type
The Dart language supports the following types:
- numbers
- strings
- booleans
- lists (also known as arrays)
- sets
- maps
- runes (used to represent Unicode characters in a string)
- symbols
You can directly use literals to initialize the above types. For example, 'This is a string' is a string literal and true is a Boolean literal.
Since each variable reference in Dart points to an object (an instance of a class), you can usually use a constructor to initialize variables. Some built-in types have their own constructors. For example, you can use Map() to create a map object.
Numbers
Dart supports two Number types:
Integer value; The length does not exceed 64 bits, and the specific value range depends on different platforms. On DartVM, the value is between - 263 and 263 - 1. Dart compiled into JavaScript JavaScript number , the allowable value range is - 253 to 253 - 1.
64 bit double precision floating-point number and complies with IEEE 754 standard.
int and double are both num Subclass of. Num defines some basic operators, such as +, -, *, / and so on. It also defines methods such as abs(), ceil() and floor() (bit operators, such as > > are defined in int). If num and its subclasses do not meet your requirements, you can view dart:math API in the library.
An integer is a number without a decimal point. Here are some examples of defining the face value of an integer:
var x = 1; var hex = 0xDEADBEEF;
If a number contains a decimal point, it is floating point. Here are some examples of defining floating-point numbers:
var y = 1.1; var exponents = 1.42e5;
Integer literals will be automatically converted to floating point literals when necessary:
double z = 1; // Equivalent to double z = 1.0.
Version prompt:
Prior to Dart 2.1, it was wrong to use integer facets in the context of floating-point numbers.
Here is how to convert between strings and numbers:
// String -> int var one = int.parse('1'); assert(one == 1); // String -> double var onePointOne = double.parse('1.1'); assert(onePointOne == 1.1); // int -> String String oneAsString = 1.toString(); assert(oneAsString == '1'); // double -> String String piAsString = 3.14159.toStringAsFixed(2); assert(piAsString == '3.14');
Integer supports traditional displacement operations, such as shift (<, > >), bitwise and (&), bitwise OR (|), such as:
assert((3 << 1) == 6); // 0011 << 1 == 0110 assert((3 >> 1) == 1); // 0011 >> 1 == 0001 assert((3 | 4) == 7); // 0011 | 0100 == 0111
Numeric literals are compile time constants. As long as the operands of many arithmetic expressions are constants, the result of the expression is also a compile time constant.
const msPerSecond = 1000; const secondsUntilRetry = 5; const msUntilRetry = secondsUntilRetry * msPerSecond;
Strings
The Dart string is a UTF-16 encoded character sequence. You can use single or double quotation marks to create strings:
var s1 = 'Single quotes work well for string literals.'; var s2 = "Double quotes work just as well."; var s3 = 'It\'s easy to escape the string delimiter.'; var s4 = "It's even easier to use the other delimiter."; // Code Chinese interpretation var s1 = 'Creates a string literal using single quotes.'; var s2 = "Double quotes can also be used to create string literals."; var s3 = 'When creating strings with single quotes, you can use slashes to escape strings that conflict with single quotes:\'. '; var s4 = "In double quotation marks, you do not need to escape strings that conflict with single quotation marks:'";
In the string, use the expression in the form of ${expression}. If the expression is an identifier, you can omit {}. If the result of the expression is an object, Dart will call the toString method of the object to get a string.
var s = 'string interpolation'; assert('Dart has $s, which is very handy.' == 'Dart has string interpolation, ' + 'which is very handy.'); assert('That deserves all caps. ' + '${s.toUpperCase()} is very handy!' == 'That deserves all caps. ' + 'STRING INTERPOLATION is very handy!'); // Code Chinese interpretation var s = 'String interpolation'; assert('Dart have $s,It is very convenient to use.' == 'Dart There is string interpolation, which is very convenient to use.'); assert('use ${s.substring(3,5)}Expressions are also very convenient' == 'It is also very convenient to use interpolation expressions.');
Notes:
==Operator is responsible for judging whether the contents of two objects are the same. If two strings contain the same character coding sequence, it means equality.
You can use the + operator or place multiple strings side by side to connect strings:
var s1 = 'String ' 'concatenation' " works even over line breaks."; assert(s1 == 'String concatenation works even over ' 'line breaks.'); var s2 = 'The + operator ' + 'works, as well.'; assert(s2 == 'The + operator works, as well.'); // Code Chinese interpretation var s1 = 'Can splice' 'character string' "Even if they are not on the same line."; assert(s1 == 'Strings can be spliced even if they are not on the same line.'); var s2 = 'Use plus sign + operator' + 'The same effect can be achieved.'; assert(s2 == 'Use plus sign + Operator can achieve the same effect.');
Multiline strings can also be created using three single quotes or three double quotes:
var s1 = ''' You can create multiline strings like this. '''; var s2 = """This is also a multiline string.""";
Prefix the string with r to create a "raw" string (that is, a string that will not be processed (such as escape):
var s = r'In a raw string, not even \n gets special treatment.'; // Code Chinese interpretation var s = r'stay raw String, escape string \n Will output directly“\n" Instead of escaping to a newline.';
You can look it up Runes and grapheme clusters Get more information about how to represent Unicode characters in strings.
String literal is a compile time constant. As long as it is a compile time constant, it can be used as an interpolation expression of string literal:
// The following three constants can be spliced into the string literal as string interpolation. (These work in a const string.) const aConstNum = 0; const aConstBool = true; const aConstString = 'a constant string'; // The following three constants cannot be spliced into string literal as string interpolation. var aNum = 0; var aBool = true; var aString = 'a string'; const aConstList = [1, 2, 3]; const validConstString = '$aConstNum $aConstBool $aConstString'; // const invalidConstString = '$aNum $aBool $aString $aConstList';
Available for reference Strings and regular expressions Get more information about how to use strings.
Booleans
Dart uses the bool keyword to represent the boolean type. The boolean type has only two objects, true and false, both of which are compile time constants.
Dart's type safety does not allow you to check Boolean values with code like if (nonpooleanvalue) or assert (nonpooleanvalue). Instead, you should always check Boolean values explicitly, such as the following code:
// Check for an empty string var fullName = ''; assert(fullName.isEmpty); // Check whether it is less than or equal to zero. var hitPoints = 0; assert(hitPoints <= 0); // Check if it is null. var unicorn; assert(unicorn == null); // Check whether it is NaN. var iMeantToDoThis = 0 / 0; assert(iMeantToDoThis.isNaN);
Lists
Array is the most common collection type in almost all programming languages. In Dart, array is composed of List Object representation. It is often called a List.
The List literal in Dart looks the same as the array literal in JavaScript. The following is an example of a Dart List:
var list = [1, 2, 3];
Notes:
Here, Dart infers that the type of list is list < int >, and an error will be reported if an object of non int type is added to the array. You can read Type inference For more information.
You can add a comma after the last entry of Dart's collection type. This trailing comma does not affect the collection, but it can effectively avoid "copy and paste" errors.
var list = [ 'Car', 'Boat', 'Plane', ];
The subscript index of List starts from 0, the subscript of the first element is 0, and the subscript of the last element is List length - 1. You can get the length and elements of List in Dart as in JavaScript:
var list = [1, 2, 3]; assert(list.length == 3); assert(list[1] == 2); list[1] = 1; assert(list[1] == 1);
Adding const keyword before List literal will create a compile time constant:
var constantList = const [1, 2, 3]; // constantList[1] = 1; // This line will cause an error.
Dart introduced the extension operator (...) in 2.3 And null aware extension operators (...?), They provide a concise way to insert multiple elements into a collection.
For example, you can use the extension operator (...) Insert all elements in one List into another List:
var list = [1, 2, 3]; var list2 = [0, ...list]; assert(list2.length == 4);
If the right side of the extension operator may be null, you can use the null aware extension operator (...?) To avoid exceptions:
var list; var list2 = [0, ...?list]; assert(list2.length == 1);
Available for reference Extension operator recommendations Get more information about how to use extension operators.
Dart also introduces the if in the set and the for operation in the set. When building the set, you can use conditional judgment (if) and loop (for).
The following example is an example of using the if in the collection to create a List, which may contain 3 or 4 elements:
var nav = [ 'Home', 'Furniture', 'Plants', if (promoActive) 'Outlet' ];
The following is an example of using for in the collection to modify and add an element in a list to another list:
var listOfInts = [1, 2, 3]; var listOfStrings = [ '#0', for (var i in listOfInts) '#$i' ]; assert(listOfStrings[1] == '#1');
You can look it up Recommendations for using control flow in Collections Get more details and examples about using if and for in collections.
There are many convenient methods for operating List in the List class, which you can refer to generic paradigm and aggregate Get more information about it.
Sets
In dart, set is an unordered set of specific elements. Dart supports set by set literals and Set Class.
Version prompt:
Although Set type has always been a core function of Dart, Set literals were added in Dart 2.2.
The following is a method to create a Set set using the Set literal:
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
Notes:
Dart infers that the halogens variable is a Set of type Set < string >, and an error will be reported if an object of incorrect type is added to the Set. You can look it up Type inference Get more about it.
You can create an empty Set by adding a type parameter before {}, or assign {} to a variable of type Set:
var names = <String>{}; // Create a Set as type + {}. // Set<String> names = {}; // Create Set (This works, too) in the form of declaring type variables // var names = {}; // This form will create a Map instead of a Set (Creates a map, not a set.)
Set or Map? The Map literal syntax is similar to the set literal syntax. Because of the previous Map literal syntax, {} defaults to the Map type. If you forget to annotate the type on {} or assign it to a variable of undeclared type, Dart will create an object of type Map < dynamic, dynamic >.
Use the add() method or addAll() method to add an item to an existing Set:
var elements = <String>{}; elements.add('fluorine'); elements.addAll(halogens);
use. length to get the number of elements in the Set:
var elements = <String>{}; elements.add('fluorine'); elements.addAll(halogens); assert(elements.length == 5);
You can add const keyword before Set literal to create a Set compile time constant:
final constantSet = const { 'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine', }; // constantSet.add('helium'); // This line will cause an error.
Starting from Dart 2.3, Set can support the use of extension operators (... And...?) like List And Collection If and Collection For operations. You can look it up List extension operator and List set operator For more information.
You can also refer to generic paradigm as well as Set For more information.
Maps
Generally speaking, a map is an object used to associate keys and values. The key and value can be any type of object. Each key can only appear once, but the value can appear multiple times. Map in Dart provides map literal and Map There are two types of maps.
The following is a pair of examples of creating a Map using Map literals:
var gifts = { // Keys: Values 'first': 'partridge', 'second': 'turtledoves', 'fifth': 'golden rings' }; var nobleGases = { 2: 'helium', 10: 'neon', 18: 'argon', };
Notes:
Dart infers the type of the gifts variable as Map < string, string >, and the type of nobleGases as Map < int, string >. If you add incorrect type values to these two Map objects, it will cause a runtime exception. You can read Type inference For more information.
You can also use the Map builder to create a Map:
var gifts = Map(); gifts['first'] = 'partridge'; gifts['second'] = 'turtledoves'; gifts['fifth'] = 'golden rings'; var nobleGases = Map(); nobleGases[2] = 'helium'; nobleGases[10] = 'neon'; nobleGases[18] = 'argon';
If you come from a language like c# or Java, you might expect to see new map() instead of just map() In Dart, the new keyword is optional. For details, see Using constructors.
If you used a language like C# or Java before, maybe you want to use new Map() to construct Map objects. However, in Dart, the new keyword is optional. You can refer to Use of constructors For more information.
Adding key value pairs to an existing Map is similar to JavaScript:
var gifts = {'first': 'partridge'}; gifts['fourth'] = 'calling birds'; // Add a key value pair
The operation of getting a value from a Map is also similar to JavaScript.
var gifts = {'first': 'partridge'}; assert(gifts['first'] == 'partridge');
If the retrieved Key does not exist in the Map, a null will be returned:
var gifts = {'first': 'partridge'}; assert(gifts['fifth'] == null);
use. length to obtain the number of key value pairs in the Map:
var gifts = {'first': 'partridge'}; gifts['fourth'] = 'calling birds'; assert(gifts.length == 2);
Adding const keyword before a Map literal can create a Map compile time constant:
final constantMap = const { 2: 'helium', 10: 'neon', 18: 'argon', }; // constantMap[2] = 'Helium'; // This line will cause an error.
Map can support the use of extension operators (... And...?) like List And the if and for operations of the collection. You can look it up List extension operator and List set operator For more information.
You can also refer to generic paradigm as well as Maps For more information.
Runes and grapheme clusters
In Dart, runes Exposes the Unicode code point of a string. use characters package To access or manipulate user aware characters, also known as Unicode (Extended) grapheme clusters.
Unicode encoding defines a unique numeric value for each letter, number, and symbol. Because the string in Dart is a UTF-16 character sequence, you need a special syntax if you want to represent 32-bit Unicode values.
A common way to represent Unicode characters is to use \ uXXXX, where XXXX is a four digit hexadecimal number. For example, heart characters( ♥) Unicode for is \ u2665. Hexadecimal numbers that are not four digits need to be enclosed in curly braces. For example, the emoji expression of laughter( 😆) Unicode for is \ u{1f600}.
If you need to read and write a single Unicode character, you can use the characters getter defined in the characters package. It will return Characters Object as a string of grapheme clusters. The following is an example of using the characters API:
import 'package:characters/characters.dart'; ... var hi = 'Hi 🇩🇰'; print(hi); print('The end of the string: ${hi.substring(hi.length - 1)}'); print('The last character: ${hi.characters.last}\n');
The output depends on your environment and is roughly similar to:
$ dart bin/main.dart Hi 🇩🇰 The end of the string: ??? The last character: 🇩🇰
For more information about manipulating strings using the characters package, see for characters package Sample and API reference.
Notes:
Be careful when using List to operate Rune. Depending on the language and character set of the operation, string problems may occur. For details, please refer to the question in Stack Overflow: [how can I reverse a string in dart?] [How do I reverse a String in Dart?].
Symbols
Symbol represents the operator or identifier declared in Dart. You hardly need symbols, but they are useful for API s that reference identifiers by name, because after code compression, although the name of identifiers will change, their symbols will remain unchanged.
Symbol s can be obtained by # prefixing identifiers:
#radix #bar
The Symbol literal is a compile time constant.
function
Dart is a truly object-oriented language, so even functions are objects and of type Function This means that functions can be assigned to variables or as parameters to other functions. You can also call an instance of the Dart class like a function. For details, please refer to Callable class.
The following is an example of defining a function:
bool isNoble(int atomicNumber) { return _nobleGases[atomicNumber] != null; }
Although efficient Dart guidelines recommend The return type is defined on the exposed API However, even if it is not defined, the function is still valid:
isNoble(atomicNumber) { return _nobleGases[atomicNumber] != null; }
If the function body contains only one expression, you can use the shorthand syntax:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
Syntax = > the expression is a {return expression;} For short, = > is sometimes called an arrow function.
Notes:
In = > and; Between can only be expressions, not statements. For example, you can't put one if statement In it, but it can be placed Conditional expression.
parameter
Functions can have two forms of arguments: required and optional. The required parameters are defined in front of the parameter list, and the optional parameters are defined after the required parameters. Optional parameters can be named or positional.
Notes:
Some API s (especially Flutter Control's constructor) uses only named parameters, even if the parameters are mandatory. You can refer to the next section for more information.
[trailing comma] [trailing comma] can be used when passing in parameters to a function or defining function parameters.
Named parameters
Named parameters default to optional parameters unless they are specifically marked as necessary.
When you call a function, you can specify named parameters in the form of parameter name: parameter value. For example:
enableFlags(bold: true, hidden: false);
When defining a function, use {parameter 1, parameter 2,...} to specify named parameters:
When defining a function, use {param1, param2,...} to specify named parameters:
///Set [bold] and [hidden] identification /// Sets the [bold] and [hidden] flags... void enableFlags({bool bold, bool hidden}) {...}
Although named parameters are a type of optional parameters, you can still use them @required Annotation to identify a named parameter is a required parameter, and the caller must provide a value for the parameter. For example:
const Scrollbar({Key key, @required Widget child})
If the caller wants to construct a Scrollbar object through the Scrollbar constructor without providing the child parameter, it will cause a compilation error.
@required Annotation defined in meta In package, you can import package: meta / meta Dart package use.
Optional location parameters
Wrap a series of parameters with [] as position parameters:
String say(String from, String msg, [String device]) { var result = '$from says $msg'; if (device != null) { result = '$result with a $device'; } return result; }
The following is an example of calling the above function without optional parameters:
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
The following is an example of calling the above function with optional parameters:
assert(say('Bob', 'Howdy', 'smoke signal') == 'Bob says Howdy with a smoke signal');
Default parameter value
You can use = to define default values for the named and positional parameters of the function. The default value must be a compile time constant. If no default value is specified, the default value is null.
The following is an example of setting default values for optional parameters:
///Set [bold] and [hidden] identification /// Sets the [bold] and [hidden] flags ... void enableFlags({bool bold = false, bool hidden = false}) {...} // The value of bold will be true; hidden will be false. enableFlags(bold: true);
In older versions of Dart code, colons (:) were used instead of = to set the default value of named parameters. The reason is that at the beginning, named parameters only support:. But now this support is outdated, so we suggest that you only Use = to specify the default value.
The next example will show you how to set default values for location parameters:
String say(String from, String msg, [String device = 'carrier pigeon']) { var result = '$from says $msg with a $device'; return result; } assert(say('Bob', 'Howdy') == 'Bob says Howdy with a carrier pigeon');
List or Map can also be used as the default value. The following example defines a function named doStuff() and specifies a value of type list and a value of type Map for its parameters named list and gifts.
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'); }
main() function
Each Dart program must have a main() top-level function as the program entry. The return value of the main() function is void and has an optional parameter of type list < string >.
The following is an example of the main() function of a Web application:
void main() { querySelector('#sample_text_id') ..text = 'Click me!' ..onClick.listen(reverseText); }
Notes:
In the above code Grammar calls it Cascade call . Using cascading access, you can perform multiple operations on an object.
The following is an example of using the command line to access the main() function with parameters:
// Use the command dart args Dart 1 test runs the application // 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'); }
You can use Parameter library To define and parse command line parameters.
Functions are first-class objects
You can pass a function as an argument to another function. For example:
void printElement(int element) { print(element); } var list = [1, 2, 3]; // Pass the printElement function as an argument. list.forEach(printElement);
You can also assign a function to a variable, such as:
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!'; assert(loudify('hello') == '!!! HELLO !!!');
Anonymous functions are used in this example. The next section will be more about it.
Anonymous function
Most methods have names, such as main() or printElement(). You can create a method without a name, called an anonymous function, a Lambda expression, or a Closure. You can assign an anonymous method to a variable and use it, such as adding or removing the variable from the collection.
Anonymous methods look like named methods. Parameters can be defined between parentheses, separated by commas.
The contents in the following braces are the function body:
([[type] parameter [,...]){ Function body; };
The following code defines an anonymous method with only one parameter item and no parameter type. Each element in the List will call this function to print the string of element position and value:
var list = ['apples', 'bananas', 'oranges']; list.forEach((item) { print('${list.indexOf(item)}: $item'); });
Click the run button to execute the code.
If there is only one line of return statement in the function body, you can use the fat arrow abbreviation. Paste the following code into DartPad and click the run button to verify whether the two functions are consistent.
list.forEach( (item) => print('${list.indexOf(item)}: $item'));
Lexical scope
Dart is a lexical language with scope. The scope of variables is determined when writing code. Variables defined in braces can only be accessed in braces, which is similar to Java.
The following is an example of variables in a nested function in multiple scopes:
bool topLevel = true; void main() { var insideMain = true; void myFunction() { var insideFunction = true; void nestedFunction() { var insideNestedFunction = true; assert(topLevel); assert(insideMain); assert(insideFunction); assert(insideNestedFunction); } } }
Note that the nestedFunction() function can access all variables, including the top-level variables.
Lexical Closure
A closure is a function object. Even if the call of a function object is outside its original scope, it can still access variables within its lexical scope.
A function can enclose variables defined within its scope. In the following example, the function makeAdder() captures the variable addBy. Whenever a function returns, it can use the captured addBy variable.
///Returns a function that adds [addBy] to the function parameter. /// Returns a function that adds [addBy] to the /// function's argument. Function makeAdder(int addBy) { return (int i) => addBy + i; } void main() { // Generate a function that adds 2. var add2 = makeAdder(2); // Generate a function that adds 4. var add4 = makeAdder(4); assert(add2(3) == 5); assert(add4(3) == 7); }
Test whether the functions are equal
The following is a test example of the equivalence of top-level functions, static methods and sample methods:
void foo() {} // Define a top-level function class A { static void bar() {} // Define static methods void baz() {} // Define instance method } void main() { var x; // Compare top-level functions for equality. x = foo; assert(foo == x); // Compare static methods for equality. x = A.bar; assert(A.bar == x); // Compare instance methods for equality. var v = A(); // Example of A #1 var w = A(); // Example of A #2 var y = w; x = w.baz; // The two closures refer to the same instance object, so they are equal. assert(y.baz == x); // The two closures refer to different instance objects, so they are not equal. assert(v.baz != w.baz); }
Return value
All functions have return values. The last line of the function that does not display the return statement is return null; by default;.
foo() {} assert(foo() == null);
operator
Dart supports the operators in the following table. You can implement these operators as Members of a class.
describe | operator |
---|---|
Unary suffix | Expression + + expression -- () [] |
Unary prefix | -Expression! Expression ~ expression + + expression -- expression |
Multiplication and division | * / % ~/ |
Addition and subtraction method | + - |
Bit operation | << >> >>> |
Binary and | & |
Binary XOR | ^ |
Binary or | | |
Relationship and type testing | >= > <= < as is is! |
Equality judgment | == != |
Logic and | && |
Logical or | || |
Null judgment | ?? |
Conditional expression | Expression 1? Expression 2: expression 3 |
cascade | .. |
assignment | =* = / = + = - = & = ^ = etc |
Please note that:
The above operator priority is an imitation of the behavior of the Dart parser. For a more accurate description, see Dart language specification Syntax in.
Once you use an operator, you create an expression. Here are some examples of operator expressions:
a++ a + b a = b a == b c ? a : b a is T
stay Operator table In, the priority of operators is arranged in order, that is, the first row has the highest priority and the last row has the lowest priority. In the same row, the leftmost row has the highest priority and the rightmost row has the lowest priority. For example, the% operator takes precedence over = =, and = = takes precedence over & &. According to the priority rule, it means that the following two lines of code perform the same effect:
// Parentheses improve readability. // Parentheses improve readability. if ((n % i == 0) && (d % i == 0)) ... // It's hard to understand, but the effect is the same as the code above. if (n % i == 0 && d % i == 0) ...
Please note that:
For an operator with two operands, the operand on the left determines the function of the operator. For example, for a Vector object and a Point object, the expression a Vector + a Point uses the addition operator (+) defined in the Vector object.
Arithmetic operator
Dart supports common arithmetic operators:
operator | describe |
---|---|
+ | plus |
– | reduce |
-Expression | A unary negative can also be used as an inversion (reversing the sign of an expression) |
* | ride |
/ | except |
~/ | Divide and round |
% | Take mold |
Example:
assert(2 + 3 == 5); assert(2 - 3 == -1); assert(2 * 3 == 6); assert(5 / 2 == 2.5); // The result is a floating point number assert(5 ~/ 2 == 2); // The result is an integer assert(5 % 2 == 1); // Surplus assert('5/2 = ${5 ~/ 2} r ${5 % 2}' == '5/2 = 2 r 1');
Dart also supports self increasing and self decreasing operations.
Operator ++var | var = var + 1 (the value of the expression is var + 1) |
---|---|
var++ | var = var + 1 (the value of the expression is var) |
--var | var = var – 1 (the value of the expression is var – 1) |
var-- | var = var – 1 (the value of the expression is var) |
Example:
var a, b; a = 0; b = ++a; // Increase a by 1 before the assignment of b. assert(a == b); // 1 == 1 a = 0; b = a++; // Increase a by 1 after the assignment of b. assert(a != b); // 1 != 0 a = 0; b = --a; // Decrease a by 1 before the assignment of b. assert(a == b); // -1 == -1 a = 0; b = a--; // Reduce a by 1 after the assignment of b. assert(a != b); // -1 != 0
Relational operator
The following table lists the relational operators and their meanings:
Operator == | equal |
---|---|
!= | Unequal |
> | greater than |
< | less than |
>= | Greater than or equal to |
<= | Less than or equal to |
To determine whether two objects x and y represent the same thing, use = =. (in rare cases, it may be necessary to use identical() Function to determine whether two objects are identical.). Here are some rules for the = = operator:
- Assuming that there are variables X and y, and at least one of X and Y is null, x == y will return true if and only if both X and y are null, otherwise only one is null, it will return false.
- x.==(y) will return the value, whether there is y or not, that is, y is optional. That is, = = is actually a method in X and can be overridden. For details, please refer to Rewrite operator.
The following code gives examples of each relational operator:
assert(2 == 2); assert(2 != 3); assert(3 > 2); assert(2 < 3); assert(3 >= 3); assert(2 <= 3);
Type judgment operator
as,is,is! Operator is an operator that determines the type of object at run time.
Operator | Meaning |
---|---|
as | Type conversion (also used as a prefix for specified classes) |
is | Returns true if the object is of the specified type |
is! | Returns false if the object is of the specified type |
obj is T is true if and only if obj implements the interface of T. For example, obj is Object is always true because all classes are subclasses of Object.
Only when you are sure that the object is of this type can you use the as operator to convert the object to a specific type. For example:
(emp as Person).firstName = 'Bob';
If you are not sure whether this object type is T, please use is T to check the type before transformation.
if (emp is Person) { // Type check emp.firstName = 'Bob'; }
You can use the as operator for abbreviations:
(emp as Person).firstName = 'Bob';
Notes:
The above two methods are different: if emp is null or not of Person type, the first method will throw an exception, while the second method will not.
Assignment Operators
You can use =, or you can use= To assign a value to a variable with a null value.
// Assign value to a (Assign value to a) a = value; // The value is assigned if and only if b is null b ??= value;
Assignment operators like + = combine arithmetic and assignment operators.
= | –= | /= | %= | >>= | ^= |
---|---|---|---|---|---|
+= | *= | ~/= | <<= | &= | |= |
The following table explains the principle of the coincidence operator:
scene | Compound operation | Equivalent expression |
---|---|---|
Suppose there is an operator op: | a op= b | a = a op b |
Example: | a += b | a = a + b |
The following example shows how to use assignment and compound assignment operators:
var a = 2; // Assign using = a *= 3; // Assign and multiply: a = a * 3 assert(a == 6);
Logical operator
Using logical operators, you can reverse or combine Boolean expressions.
operator | describe |
---|---|
! expression | Negate the expression result (that is, true becomes false and false becomes true) |
|| | Logical or |
&& | Logic and |
The following is an example of using a logical expression:
if (!done && (col == 0 || col == 3)) { // ...Do something... }
Bitwise and shift operators
In Dart, the binary bit operator can operate on a bit of binary, but only applies to integers.
operator | describe |
---|---|
& | Bitwise AND |
| | Bitwise OR |
^ | Bitwise XOR |
~Expression | Bitwise inversion (i.e. "0" becomes "1" and "1" becomes "0") |
<< | Bit shift left |
>> | sftr |
The following is an example of using bitwise and shift operators:
final value = 0x22; final bitmask = 0x0f; assert((value & bitmask) == 0x02); // Bitwise AND assert((value & ~bitmask) == 0x20); // And not after inversion assert((value | bitmask) == 0x2f); // Bitwise OR assert((value ^ bitmask) == 0x2d); // Bitwise XOR (XOR) assert((value << 4) == 0x220); // Shift left assert((value >> 4) == 0x02); // Shift right
Conditional expression
Dart has two special operators to replace if-else sentence:
condition ? expr1 : expr2
If condition is true, evaluates expr1 (and returns its value); otherwise, evaluates and returns the value of expr2.
Conditions? Expression 1: expression 2: if the condition is true, execute expression 1 and return the execution result; otherwise, execute expression 2 and return the execution result.
expr1 ?? expr2
If expr1 is non-null, returns its value; otherwise, evaluates and returns the value of expr2.
Expression 1?? Expression 2: if expression 1 is non null, return its value; otherwise, execute expression 2 and return its value.
When determining an assignment from a Boolean expression, consider using?:.
var visibility = isPublic ? 'public' : 'private';
If the assignment is based on whether it is null, consider using??.
String playerName(String name) => name ?? 'Guest';
The above example can also be written in at least the following two different forms, but it is not concise enough:
// Relative usage?: It's a little longer for the operator. (Slightly longer version uses ?: operator). String playerName(String name) => name != null ? name : 'Guest'; // If you use if else, it is longer. String playerName(String name) { if (name != null) { return name; } else { return 'Guest'; } }
Level combined transport operator (..)
Level combined transport operator (..) It allows you to continuously call variables or methods of multiple objects on the same object.
For example, the following code:
querySelector('#confirm ') / / get an object ..text = 'Confirm' // Use its members ..classes.add('important') ..onClick.listen((e) => window.alert('Confirmed!'));
The first method querySelector returns a Selector object. The subsequent cascading operators are members of the Selector object and ignore the return value of each operation.
The above code is equivalent to:
var button = querySelector('#confirm'); button.text = 'Confirm'; button.classes.add('important'); button.onClick.listen((e) => window.alert('Confirmed!'));
Level transport operators can be nested, for example:
final addressBook = (AddressBookBuilder() ..name = 'jenny' ..email = 'jenny@example.com' ..phone = (PhoneNumberBuilder() ..number = '415-555-0100' ..label = 'home') .build()) .build();
Use cascading operators with caution in functions that return objects. For example, the following code is wrong:
var sb = StringBuffer(); sb.write('foo') ..write('bar'); // Error: there is no method write (Error: method 'write' isn't defined for 'void') in the void object
Sb. In the above code The write () method returns void, and the method with the return value of void cannot use the level transport operator.
Notes:
Strictly speaking Cascading is not an operator, but Dart's special syntax.
Other Operators
Most other operators have been used in other examples:
operator | name | describe |
---|---|---|
() | usage method | Represents calling a method |
[] | Access List | Access elements at a specific location in the List |
. | Access member | Member accessor |
?. | Conditional access member | Similar to the above member accessors, but the operation object on the left cannot be null, such as foo Bar. If foo is null, null is returned. Otherwise, bar is returned |
More about And Operators, please refer to class.
Process control statement
You can use the following statement to control the execution process of Dart Code:
- if and else
- for loop
- While and do while loops
- break and continue
- switch and case
- assert
The use of try catch and throw can also affect the control flow. Refer to abnormal part.
If and Else
Dart supports if - else statements, where else is optional, such as the following example. You can also refer to Conditional expression.
if (isRaining()) { you.bringRainCoat(); } else if (isSnowing()) { you.wearJacket(); } else { car.putTopDown(); }
Unlike JavaScript, the condition in Dart's if statement must be Boolean and cannot be of other types. For details, please refer to Boolean value.
For loop
You can iterate using the standard for loop. For example:
var message = StringBuffer('Dart is fun'); for (var i = 0; i < 5; i++) { message.write('!'); }
In the Dart language, the closure in the for loop automatically captures the index value of the loop to avoid some common pitfalls in JavaScript. Assume the following codes:
var callbacks = []; for (var i = 0; i < 2; i++) { callbacks.add(() => print(i)); } callbacks.forEach((c) => c());
After the above code is executed, 0 and 1 will be output, but if the same code is executed in JavaScript, two 2 will be output.
If the object to traverse implements the iteratable interface, you can use forEach() Method. If you do not need to use an index, the forEach method is a very good choice:
candidates.forEach((candidate) => candidate.interview());
Classes that implement Iterable interfaces such as List and Set also support for in forms iteration:
var collection = [1, 2, 3]; for (var x in collection) { print(x); // 1 2 3 }
While and do while
The while loop will judge the conditions before executing the loop body:
while (!isDone()) { doSomething(); }
The do while loop will execute the loop body first, and then judge the conditions:
do { printLine(); } while (!atEndOfPage());
Break and Continue
Use break to break a loop:
while (true) { if (shutDownRequested()) break; processIncomingRequests(); }
Use continue to skip this cycle and directly enter the next cycle:
for (int i = 0; i < candidates.length; i++) { var candidate = candidates[i]; if (candidate.yearsExperience < 5) { continue; } candidate.interview(); }
If you're using something like List or Set Iterable Object, you can override the above example in the following ways:
candidates .where((c) => c.yearsExperience >= 5) .forEach((c) => c.interview());
Switch and Case
Switch statement uses = = in Dart to compare integers, strings or compile time constants. The two objects compared must be of the same type and cannot be subclasses, and the = = operator is not overridden. Enumeration type It is very suitable for use in Switch statements.
Notes:
The Switch statement in Dart is only applicable to limited situations, such as scenes using interpreters and scanners.
Each non empty case clause must have a break statement. You can also end the non empty case statement through continue, throw or return.
The code in the default clause is executed without matching any case statements:
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(); }
The following example ignores the break statement of the case clause, so an error will be generated:
var command = 'OPEN'; switch (command) { case 'OPEN': executeOpen(); // Error: no break case 'CLOSED': executeClosed(); break; }
However, Dart supports empty case statements, allowing them to be executed in the form of fall through.
var command = 'CLOSED'; switch (command) { case 'CLOSED': // The form of fall through when the case statement is empty. case 'NOW_CLOSED': // The case condition values are CLOSED and now_ The statement is executed when CLOSED. executeNowClosed(); break; }
In the non empty case statement, if you want to implement the form of fall through, you can use the continue statement with label:
var command = 'CLOSED'; switch (command) { case 'CLOSED': executeClosed(); continue nowClosed; // Continue to execute the case clause labeled nowClosed. nowClosed: case 'NOW_CLOSED': // The case condition values are CLOSED and now_ The statement is executed when CLOSED. executeNowClosed(); break; }
Each case clause can have local variables and is visible only within the case statement.
Assert
During development, you can use - assert (condition, optional information) when the condition expression is false Statement to interrupt code execution. You can find a lot of examples of using assert in this article. Here are some examples:
// Make sure the variable has a non null value assert(text != null); // Ensure that the variable value is less than 100. assert(number < 100); // Make sure this is an https address. assert(urlString.startsWith('https'));
The second parameter of assert adds a string message to it.
assert(urlString.startsWith('https'), 'URL ($urlString) should start with "https".');
The first argument to assert can be any expression with a Boolean value. If the value of the expression is true, the assertion succeeds and execution continues. If the value of the expression is false, the assertion fails and an error is thrown AssertionError Abnormal.
How to determine whether the assert is effective? The effectiveness of assert depends on the development tools and frameworks used:
- Where's Flutter Debug mode Effective when.
- Some development tools, such as dartdevc Usually, it takes effect by default.
- Other tools, such as dart as well as dart2js Make the assert take effect by adding the command line parameter -- enable asserts when running the Dart program.
In production environment code, assertions are ignored, and the parameters passed in to assert are not judged.
abnormal
Dart code can throw and catch exceptions. An exception indicates some unknown error conditions. If the exception is not caught, it will be thrown, resulting in the termination of the execution of the code throwing the exception.
Different from Java, all Dart exceptions are non checking exceptions. The method does not have to declare which exceptions will be thrown, and you do not have to catch any exceptions.
Dart provides Exception and Error Two types of exceptions and their series of subclasses, you can also define your own Exception types. However, any non null object can be thrown as an Exception in Dart, not limited to Exception or Error types.
Throw exception
The following is an example of throwing or throwing an exception:
throw FormatException('Expected at least 1 section');
You can also throw any object:
throw 'Out of llamas!';
Notes:
Good code usually throws Error or Exception Exception of type.
Because the exception thrown is an expression, it can be used in = > statements or in other places where expressions are used:
void distanceTo(Point other) => throw UnimplementedError();
Catch exception
Catching an exception prevents the exception from continuing to pass (except re throwing an exception). Catching an exception gives you a chance to handle it:
try { breedMoreLlamas(); } on OutOfLlamasException { buyMoreLlamas(); }
For codes that can throw multiple exception types, you can also specify multiple catch statements. Each statement corresponds to an exception type. If the catch statement does not specify an exception type, it means that any exception type can be caught:
try { breedMoreLlamas(); } on OutOfLlamasException { // Specify exception buyMoreLlamas(); } on Exception catch (e) { // Other types of exceptions print('Unknown exception: $e'); } catch (e) { // //Do not specify type, process all other print('Something really unknown: $e'); }
As shown in the above code, you can use on or catch to catch exceptions, use on to specify exception types, and use catch to catch exception objects. Both can be used at the same time.
You can specify two parameters for the catch method. The first parameter is the exception object thrown, and the second parameter is the stack information StackTrace Object:
try { // ··· } on Exception catch (e) { print('Exception details:\n $e'); } catch (e, s) { print('Exception details:\n $e'); print('Stack trace:\n $s'); }
The keyword rethrow can throw the caught exception again:
void misbehave() { try { dynamic foo = true; print(foo++); // Runtime error } catch (e) { print('misbehave() partially handled ${e.runtimeType}.'); rethrow; // Allows the caller to view exceptions. } } void main() { try { misbehave(); } catch (e) { print('main() finished handling ${e.runtimeType}.'); } }
Finally
Whether or not an exception is thrown, the finally statement is always executed. If no catch statement is specified to catch the exception, the exception will be thrown after the finally statement is executed:
try { breedMoreLlamas(); } finally { // Always clean up, even if an exception is thrown. cleanLlamaStalls(); }
finally statement will be executed after any matching catch statement:
try { breedMoreLlamas(); } catch (e) { print('Error: $e'); // Handle the exception first. } finally { cleanLlamaStalls(); // Then clean up. }
You can read the overview of Dart core library abnormal Section for more information.
class
Dart is an object-oriented language that supports mixin inheritance mechanism. All objects are instances of a class, and all classes inherit from Object Class. mixin based inheritance means that although each class (except Object) has only one superclass, the code of one class can be reused in the inheritance of multiple other classes. Extension method Is a way to add functionality to a class without changing the class or creating subclasses.
Using members of a class
The members of an object consist of functions and data, that is, methods and instance variables. Method is called through an object. In this way, the function and data of the object can be accessed.
Use (.) To access an instance variable or method of an object:
var p = Point(2, 2); // Get y value assert(p.y == 2); // Call the distanceTo() method of variable p. double distance = p.distanceTo(Point(4, 4));
use?. Replace You can avoid problems caused by null expression on the left:
// If p is non-null, set a variable equal to its y value. var a = p?.y;
Use constructor
You can use the constructor to create an object. Constructors can be named by class name or class name The form of the identifier. For example, the following code uses Point() and Point Fromjason() creates a Point object using two constructors:
var p1 = Point(2, 2); var p2 = Point.fromJson({'x': 1, 'y': 2});
The following code has the same effect, but the new keyword before the constructor name is optional:
var p1 = new Point(2, 2); var p2 = new Point.fromJson({'x': 1, 'y': 2});
Version prompt:
Starting from Dart 2, the new keyword is optional.
Some classes provide Constant constructor . When using a constant constructor and adding const keyword before the constructor name to create a compile time constant:
var p = const ImmutablePoint(2, 2);
Two compile time constants constructed with the same constructor and the same parameter value are the same object:
var a = const ImmutablePoint(1, 1); var b = const ImmutablePoint(1, 1); assert(identical(a, b)); // They are the same instance
In the constant context scenario, you can omit the const keyword before the constructor or literal. For example, in the following example, we created a constant Map:
// Lots of const keywords here. // There are many const keywords here const pointAndLine = const { 'point': const [const ImmutablePoint(0, 0)], 'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)], };
According to the context, you can only keep the first const keyword and omit all the rest:
// Only one const, which establishes the constant context. // Only one const keyword is required, and others are implicitly associated according to the context. const pointAndLine = { 'point': [ImmutablePoint(0, 0)], 'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)], };
However, if it is impossible to judge whether const can be omitted according to the context, the const keyword cannot be omitted, otherwise a non constant object will be created, for example:
var a = const ImmutablePoint(1, 1); // Create a constant var b = ImmutablePoint(1, 1); // Does not create a constant assert(!identical(a, b)); // These two variables are not the same (NOT the same instance!)
Version prompt:
The const keyword can be omitted only from Dart 2 according to the context.
Gets the type of the object
You can use the runtimeType property of an Object object Object to get the type of an Object at run time. The Object type is Type Examples of.
print('The type of a is ${a.runtimeType}');
So far, we have solved how to use classes. The rest of this section will show you how to implement a class.
Instance variable
The following is an example of declaring an instance variable:
class Point { double x; // Declare the double variable x and initialize to null. double y; // Declare the double variable y and initialize to null double z = 0; // Declare the double variable z and initialize it to 0. }
All uninitialized instance variables have null values.
All instance variables will implicitly declare a Getter method, and instance variables of non final type will implicitly declare a Setter method. You can look it up Getter and Setter For more information.
class Point { double x; double y; } void main() { var point = Point(); point.x = 4; // Use the Setter method of x. assert(point.x == 4); // Use the Getter method of x. assert(point.y == null); // The default value is null. }
If you initialize an instance variable when you declare it (not in a constructor or other method), the value of the instance variable will be set when the object instance is created, which is earlier than the constructor and its initializer list.
Constructor
Declare a constructor by declaring a function with the same class name (for Named constructor Additional identifiers can also be added). Most constructors are in the form of generative constructors, which are used to create an instance of a class:
class Point { double x, y; Point(double x, double y) { // There will be better ways to implement this logic. Please look forward to it. this.x = x; this.y = y; } }
Use the this keyword to reference the current instance.
Notes:
It makes sense to use this keyword when and only when naming conflicts, otherwise Dart ignores this keyword.
For most programming languages, the process of assigning values to instance variables in constructors is similar, while Dart provides a special syntax sugar to simplify this step:
class Point { double x, y; // The syntax sugar used to set x and y before the constructor body is executed. Point(this.x, this.y); }
Default constructor
If you do not declare a constructor, Dart will automatically generate a parameterless constructor and the constructor will call the parameterless constructor of its parent class.
Constructor is not inherited
The subclass will not inherit the constructor of the parent class. If the subclass does not declare a constructor, there will only be a default parameterless constructor.
Named constructor
You can declare multiple named constructors for a class to express more explicit intent:
class Point { double x, y; Point(this.x, this.y); // Named constructor Point.origin() : x = 0, y = 0; }
Remember that constructors cannot be inherited, which means that subclasses cannot inherit the named constructor of the parent class. If you want to provide a named constructor with the same name as the named constructor of the parent class in the subclass, you need to declare it explicitly in the subclass.
Call the non default constructor of the parent class
By default, the constructor of the subclass will call the anonymous parameterless constructor of the parent class, and the call will be made before the function body code of the subclass constructor is executed, if the subclass constructor still has one Initialization list , the initialization list will be executed before calling the constructor of the parent class. In general, the calling order of the three is as follows:
- Initialization list
- Parameterless constructor for parent class
- Constructor of the current class
If the parent class does not have an anonymous parameterless constructor, the child class must call one of the parent class constructors and specify a parent class constructor for the child class constructor. You only need to specify it in front of the constructor body with (:).
In the following example, the constructor of the Employee class calls the named constructor of the parent class Person. Click the run button to execute the sample code.
Because the parameter will be passed to the constructor of the parent class before the constructor of the child class is executed, the parameter can also be an expression, such as a function:
class Employee extends Person { Employee() : super.fromJson(defaultData); // ··· }
Please note that:
The parameter passed to the parent class constructor cannot use the this keyword, because in this step of parameter passing, the subclass constructor has not been executed and the subclass instance object has not been initialized. Therefore, all instance members cannot be accessed, but class members can.
Initialization list
In addition to calling the parent constructor, you can initialize the instance variable before the constructor body executes. Each instance variable is separated by commas.
// Initializer list sets instance variables before // the constructor body runs. // Use the initialization list to set the instance variable before the constructor body executes. Point.fromJson(Map<String, double> json) : x = json['x'], y = json['y'] { print('In Point.fromJson(): ($x, $y)'); }
Please note that:
The statement on the right of initialization list expression = cannot use this keyword.
In development mode, you can use assert in the initialization list to verify the input data:
Point.withAssert(this.x, this.y) : assert(x >= 0) { print('In Point.withAssert(): ($x, $y)'); }
It is very convenient to use the initialization list to set the final field. In the following example, the initialization list is used to set the values of three final variables. Click the run button to execute the sample code.
Redirect constructor
Sometimes the constructor in the class is only used to call other constructors in the class. At this time, the constructor has no function body. Just use (:) after the function signature to specify other constructors to redirect to:
class Point { double x, y; // The primary constructor of this class. Point(this.x, this.y); // Delegate implementation to the main constructor. Point.alongXAxis(double x) : this(x, 0); }
Constant constructor
If the objects generated by the class are invariant, they can be changed into compile time constants when they are generated. You can implement this function by adding the const keyword to the constructor of the class and ensuring that all instance variables are final.
class ImmutablePoint { static const ImmutablePoint origin = ImmutablePoint(0, 0); final double x, y; const ImmutablePoint(this.x, this.y); }
The instance created by constant constructor is not always constant. For details, please refer to Use constructor Chapter.
Factory constructor
Using the factory keyword to identify the constructor of a class will make the constructor a factory constructor, which means that when constructing an instance of a class using this constructor, a new instance object will not always be returned. For example, a factory constructor might return an instance from the cache or an instance of a subtype.
In the following example, the Logger's factory constructor returns the object from the cache, and the Logger The fromjson factory constructor initializes a final variable from the JSON object.
class Logger { final String name; bool mute = false; // _ The cache variable is private to the library because its name is preceded by an underscore. static final Map<String, Logger> _cache = <String, Logger>{}; factory Logger(String name) { return _cache.putIfAbsent( name, () => Logger._internal(name)); } factory Logger.fromJson(Map<String, Object> json) { return Logger(json['name'].toString()); } Logger._internal(this.name); void log(String msg) { if (!mute) print(msg); } }
Notes:
this cannot be accessed in the factory constructor.
Factory constructors are called in the same way as other constructors:
var logger = Logger('UI'); logger.log('Button clicked'); var logMap = {'name': 'UI'}; var loggerJson = Logger.fromJson(logMap);
method
Methods are functions that provide behavior to objects.
Example method
Object can access instance variables and this. The following distanceTo() method is an example of an instance method:
import 'dart:math'; class Point { double x, y; Point(this.x, this.y); double distanceTo(Point other) { var dx = x - other.x; var dy = y - other.y; return sqrt(dx * dx + dy * dy); } }
Operator
Operators are instance methods with special names. Dart allows you to define operators with the following names:
< | + | | | [] |
---|---|---|---|
> | / | ^ | []= |
<= | ~/ | & | ~ |
>= | * | << | == |
– | % | >> |
Notes:
You may notice some Operator Does not appear in the list, for example! =. Because they are just grammar sugar. Expression E1= E2 just! A syntax sugar of (e1 == e2).
To represent the rewrite operator, we use the operator ID to mark it. Here is an example of overriding the + and - operators:
class Vector { final int x, y; Vector(this.x, this.y); Vector operator +(Vector v) => Vector(x + v.x, y + v.y); Vector operator -(Vector v) => Vector(x - v.x, y - v.y); // Operator == and hashCode not shown. // ··· } void main() { final v = Vector(2, 3); final w = Vector(2, 2); assert(v + w == Vector(4, 5)); assert(v - w == Vector(0, 1)); }
Getter and Setter
Getter and Setter are a pair of special methods used to read and write object properties. As mentioned above, each property of the instance object has an implicit getter method. If it is a non final property, there will also be a Setter method. You can use the get and set keywords to add getter and Setter methods for additional properties:
class Rectangle { double left, top, width, height; Rectangle(this.left, this.top, this.width, this.height); // Define two calculated attributes: right and bottom. double get right => left + width; set right(double value) => left = value - width; double get bottom => top + height; set bottom(double value) => top = value - height; } void main() { var rect = Rectangle(3, 4, 20, 15); assert(rect.left == 3); rect.right = 12; assert(rect.left == -8); }
The advantage of using Getter and Setter is that you can use your instance variables first and wrap them into methods after a period of time without changing any code, that is, define them first and then change them without affecting the original logic.
Notes:
Operators such as auto increment (+) will execute correctly regardless of whether the Getter method is defined or not. In order to avoid some unnecessary exceptions, the operator will only call Getter once, and then store its value in a temporary variable.
Abstract method
Instance method, Getter method and Setter method can be abstract. Define an interface method without specific implementation, and let the class implementing it implement the method. Abstract methods can only exist in abstract class Yes.
Use semicolons directly (;) An alternative method body can declare an abstract method:
abstract class Doer { // Define instance variables and methods, etc void doSomething(); // Define an abstract method. } class EffectiveDoer extends Doer { void doSomething() { // Provide an implementation, so here the method is no longer abstract } }
abstract class
Using the keyword abstract to identify a class can make the class an abstract class, which cannot be instantiated. Abstract classes are often used to declare interface methods and sometimes have specific method implementations. If you want an abstract class to be instantiated at the same time, you can define it Factory constructor.
Abstract classes often contain Abstract method . The following is an example of an abstract class that declares an abstract method:
// This class is declared abstract and thus // can't be instantiated. // The class is declared abstract, so it cannot be instantiated. abstract class AbstractContainer { // Define constructors, fields, methods, etc void updateChildren(); // Abstract methods. }
Implicit interface
Each class implicitly defines and implements an interface that contains all the instance members of the class and other interfaces implemented by the class. If you want to create A class A API that supports calling class B and do not want to inherit class B, you can implement the interface of class B.
A class can implement one or more interfaces through the keyword implements and implement the API defined by each interface:
// A person. The implicit interface contains greet(). // The implicit interface of the Person class contains the greet() method. class Person { // _ The name variable is also included in the interface, but it is only visible in the library. final _name; // Constructor is not in the interface. Person(this._name); // The greet() method is in the interface. String greet(String who) => 'Hello, $who. I am $_name. '; } // An implementation of the Person interface. class Impostor implements Person { get _name => ''; String greet(String who) => 'Hello $who. Do you know who I am?'; } String greetBob(Person person) => person.greet('Xiao Fang'); void main() { print(greetBob(Person('Rue '))); print(greetBob(Impostor())); }
If you need to implement multiple class interfaces, you can use commas to separate each interface class:
class Point implements Comparable, Location {...}
Extend a class
Use the extends keyword to create a subclass, and use the super keyword to reference a parent class:
class Television { void turnOn() { _illuminateDisplay(); _activateIrSensor(); } // ··· } class SmartTelevision extends Television { void turnOn() { super.turnOn(); _bootNetworkInterface(); _initializeMemory(); _upgradeApps(); } // ··· }
Override class members
Subclasses can override the instance methods of the parent class (including Operator ), Getter, and Setter methods. You can use the @ override annotation to indicate that you have rewritten a member:
class SmartTelevision extends Television { @override void turnOn() {...} // ··· }
You can use covariant keyword To narrow down those in the code that match Type safety Type of method parameter or instance variable.
Please note that:
If you override the = = operator, you must also override the Getter method of the hashCode object. You can look it up Implement mapping keys Get more examples of rewriting = = and hashCode.
noSuchMethod method
If a method or instance variable that does not exist on the object is called, the noSuchMethod method will be triggered. You can override the noSuchMethod method to track and record this behavior:
class A { // Unless you override noSuchMethod, calling a nonexistent member will cause NoSuchMethodError. @override void noSuchMethod(Invocation invocation) { print('You tried to use a member that does not exist:' + '${invocation.memberName}'); } }
You can call an unimplemented method only if one of the following conditions is true:
- The receiver is a static dynamic type.
- The receiver has a static type, defines an unimplemented method (abstract or otherwise), and the receiver's dynamic type implements the noSuchMethod method, and the specific implementation is different from that in Object.
You can look it up noSuchMethod forwarding specification For more information.
Extension method
An extension method is a way to add functionality to an existing library. You may have used it without knowing that it is an extension method. For example, when you use code to complete functions in the IDE, it recommends using extension methods with regular methods.
Here is an example of using the extension method in String. We call it parseInt(), which is in String_ apis. Defined in dart:
import 'string_apis.dart'; ... print('42'.padLeft(5)); // Use a String method. print('42'.parseInt()); // Use an extension method.
For more information about using and implementing extension methods, see Extension method page.
Enumeration type
Enumeration type is a special type, also known as enumerations or enums, used to define a fixed number of constant values.
Using enums
Use enumeration
Use the keyword enum to define the enumeration type:
enum Color { red, green, blue }
You can use it when declaring enumeration types Trailing comma.
Each enumerated value has a Getter method named index member variable, which will return the location value of the index based on 0. For example, the index of the first enumeration value is 0 and the index of the second enumeration value is 1. and so on.
assert(Color.red.index == 0); assert(Color.green.index == 1); assert(Color.blue.index == 2);
To obtain all enumeration values, use the values method of the enumeration class to obtain the list containing them:
List<Color> colors = Color.values; assert(colors[2] == Color.blue);
You can Switch statement Enumeration is used in, but it should be noted that each case of enumeration value must be handled, that is, each enumeration value must become a case clause, or a warning will appear:
var aColor = Color.blue; switch (aColor) { case Color.red: print('Red as a rose!'); break; case Color.green: print('Green as grassland!'); break; default: // Without this statement, a warning appears. print(aColor); // 'Color.blue' }
Enumeration types have the following two limitations:
- Enumeration cannot be a subclass, nor can it mix in, nor can you implement an enumeration.
- You cannot explicitly instantiate an enumeration class.
You can look it up Dart programming language specification For more information.
Adding functionality to a class using Mixin
Mixin is a method pattern that reuses code in a class in multiple inheritance.
Use the with keyword followed by the name of the Mixin class to use the Mixin pattern:
class Musician extends Performer with Musical { // ··· } class Maestro extends Person with Musical, Aggressive, Demented { Maestro(String maestroName) { name = maestroName; canConduct = true; } }
To implement a mixin, create a class that inherits from Object and does not declare a constructor. Unless you want this class to be used as normal as ordinary classes, please use the keyword mixin instead of class. For example:
mixin Musical { bool canPlayPiano = false; bool canCompose = false; bool canConduct = false; void entertainMe() { if (canPlayPiano) { print('Playing piano'); } else if (canConduct) { print('Waving hands'); } else { print('Humming to self'); } } }
You can use the keyword on to specify which classes can use the Mixin class. For example, there is Mixin class A, but a can only be used by class B. you can define a as follows:
class Musician { // ... } mixin MusicalPerformer on Musician { // ... } class SingerDancer extends Musician with MusicalPerformer { // ... }
In the preceding code, only classes that extend or implement the Musician class can use the mixin MusicalPerformer. Because SingerDancer extends Musician, SingerDancer can mix in MusicalPerformer.
Version prompt:
The mixin keyword is only referenced and supported in Dart 2.1. The code in earlier versions usually used abstract class instead. You can look it up Dart SDK change log and 2.1 mixin specification Get more information about Mixin's changes in 2.1.
Class variables and methods
Use the keyword static to declare class variables or class methods.
Static variable
Static variables (i.e. class variables) are often used to declare state variables and constants within the scope of a class:
class Queue { static const initialCapacity = 16; // ··· } void main() { assert(Queue.initialCapacity == 16); }
Static variables are initialized when they are first used.
Notes:
Code compliance in this article Style recommendation Guide Use hump case to name constants according to the naming rules in.
Static method
Static methods (that is, class methods) cannot operate on instances, so this cannot be used. But they can access static variables. As shown in the following example, you can call static methods directly on a class:
import 'dart:math'; class Point { double x, y; Point(this.x, this.y); static double distanceBetween(Point a, Point b) { var dx = a.x - b.x; var dy = a.y - b.y; return sqrt(dx * dx + dy * dy); } } void main() { var a = Point(2, 2); var b = Point(4, 4); var distance = Point.distanceBetween(a, b); assert(2.8 < distance && distance < 2.9); print(distance); }
Notes:
For some general or common static methods, they should be defined as top-level functions rather than static methods.
You can use static methods as compile time constants. For example, you can pass a static method as a parameter to a constant constructor.
generic paradigm
If you look at the API documentation for arrays, you will find arrays List The actual type of is list < E > The symbol indicates that the array is a generic (or parameterized) type usually Use a letter to represent type parameters, such as E, T, S, K, V, and so on.
Why use generics?
Generics are often used when type safety is required, but it can also be good for code operation:
- Specifying generics appropriately can help code generation better.
- Using generics can reduce code duplication.
For example, if you want to declare an array that can only contain String types, you can declare the array as list < String > (read as "list of String types"), which can easily avoid many problems caused by putting non String variables into the array. At the same time, the compiler and other code readers can easily find and locate the problems:
var names = List<String>(); names.addAll(['Seth', 'Kathy', 'Lars']); names.add(42); // Error
Another reason to use generics is to reduce duplicate code. Generics allows you to share the same interface declaration among multiple implementations of different types. For example, the following example declares an interface of a class for caching objects:
abstract class ObjectCache { Object getByKey(String key); void setByKey(String key, Object value); }
In the near future, you may want to make a cache for String class objects, so you have another class for String caching:
abstract class StringCache { String getByKey(String key); void setByKey(String key, String value); }
If you want to create a class for numeric types after a while, there will be a lot of such code
At this time, you can consider using generics to declare a class, so that different types of cache implementations can make different specific implementations of the class:
abstract class Cache<T> { T getByKey(String key); void setByKey(String key, T value); }
In the above code, T is an alternative type. It is equivalent to a type placeholder, and the specific type will be specified when the developer calls the interface.
Use set literal
List, Set, and Map literals can also be parameterized. To define a parameterized list, simply add < type > before the brackets; To define a parameterized Map, you only need to add < keytype, ValueType > before the braces:
var names = <String>['Rue ', 'Xiao Fang', 'Xiaomin']; var uniqueNames = <String>{'Rue ', 'Xiao Fang', 'Xiaomin'}; var pages = <String, String>{ 'index.html': 'homepage', 'robots.txt': 'Web robot tips', 'humans.txt': 'We are human beings, not machines' };
Constructor using type parameterization
You can also use generics when calling construction methods. Just wrap one or more types with angle brackets (<... >) after the class name:
var nameSet = Set<String>.from(names);
The following code creates a Map object with Int key and View value:
var views = Map<int, View>();
Generic collections and the types they contain
Dart's generic type is fixed, which means that the type information is maintained even at run time:
var names = List<String>(); names.addAll(['Rue ', 'Xiao Fang', 'Xiaomin']); print(names is List<String>); // true
Notes:
Unlike Java, generics in Java are type erased, which means that generic types are removed at run time. In Java, you can judge whether the object is a List, but you can't judge whether the object is a List < string >.
Limit parameterization type
Sometimes when using generics, you may want to limit the type range of generics. At this time, you can use the extends keyword:
class Foo<T extends SomeBaseClass> { // Specific implementation String toString() => "'Foo<$T>' Examples of"; } class Extender extends SomeBaseClass {...}
At this time, SomeBaseClass or its subclasses can be used as generic parameters:
var someBaseClassFoo = Foo<SomeBaseClass>(); var extenderFoo = Foo<Extender>();
You can also specify parameterless generics. In this case, the type of parameterless generics is foo < somebaseclass >:
var foo = Foo(); print(foo); // Instance of 'foo < somebaseclass >'
Using a type other than SomeBaseClass as a generic parameter will result in a compilation error:
var foo = Foo<Object>();
Using generic methods
At first Dart only supported specifying generics when declaring classes. Now you can also use generics on methods, which are called generic methods:
T first<T>(List<T> ts) { // Handle some initialization or error detection T tmp = ts[0]; // Handle some additional checks return tmp; }
The generic t of the method first < T > can be used in the following places:
- Return value type of function.
- Type of parameter (list < T >).
- Type of local variable (T tmp).
You can look it up Using generic functions Get more information about generics.
Library and visibility
The import and library keywords can help you create a modular and shareable code base. The code base not only provides API s, but also serves as a wrapper: underlined () The first member is visible only in the code base. Every Dart program is a library, even if it is not specified with the keyword library.
Dart's library can be used Package tool To publish and deploy.
Use library
Use import to specify the namespace so that other libraries can access it.
For example, you can import a code base dart:html To use the relevant API s in Dart Web:
import 'dart:html';
The only parameter of import is the URI used to specify the code base. For the built-in Library of Dart, the form of dart:xxxxxx is used. For other libraries, you can use a file system path or in the form of package:xxxxxx. Package: the library specified by XXXXXX is provided through the package manager (such as the pub tool):
import 'package:test/test.dart';
Notes:
The URI represents a uniform resource identifier.
URL (uniform resource locator) is a common URI.
Specify library prefix
If the two code bases you import have conflicting identifiers, you can specify a prefix for one of them. For example, if both library1 and library2 have Element classes, you can do this:
import 'package:lib1/lib1.dart'; import 'package:lib2/lib2.dart' as lib2; // Use the Element class of lib1. Element element1 = Element(); // Use the Element class of lib2. lib2.Element element2 = lib2.Element();
Import part of Library
If you only want to use a part of the code base, you can selectively import the code base. For example:
// Import only foo in lib1. (Import only foo). import 'package:lib1/lib1.dart' show foo; // Import all of lib2 except foo. import 'package:lib2/lib2.dart' hide foo;
Delay loading Library
Deferred loading (also commonly referred to as lazy loading) allows applications to load code bases when needed. The following scenarios may use deferred loading:
- In order to reduce the initialization time of the application.
- Handle A/B testing, such as testing different implementations of various algorithms.
- Load features that are rarely used, such as optional screens and dialog boxes.
At present, only dart2js supports delayed loading. At present, Flutter, Dart VM and DartDevc do not support delayed loading. You can look it up issue #33118 and issue #27776 For more information.
Use the deferred as keyword to identify the code base that needs delayed loading:
import 'package:greetings/hello.dart' deferred as hello;
When you actually need to use the API in the library, first call the loadLibrary function to load the Library:
Future greet() async { await hello.loadLibrary(); hello.printGreeting(); }
In the previous code, use the await keyword to pause code execution until the library is loaded. For more information about async and await, refer to Asynchronous Support.
It doesn't matter that the loadLibrary function can be called multiple times. The code base will only be loaded once.
When you use deferred loading, you need to keep the following in mind:
- The constants in the code base of delayed loading can only be imported when the code base is loaded, and will not be imported when it is not loaded.
- You cannot use types in the deferred load library when importing files. If you need to use type, consider transferring the interface type to another library, and then let both libraries import the interface library respectively.
- Dart implicitly imports the loadLibrary method into a class that uses the deferred as namespace. The loadLibrary function returns a Future.
Implementation library
consult Create dependent library package You can get advice on how to implement library packages, including:
- How to organize the source files of the library.
- How to use the export command.
- When to use the part command.
- When to use the library command.
- How to use pour and export commands to achieve multi platform library support.
Asynchronous Support
There are a large number of returns in the Dart code base Future or Stream Object. These functions are asynchronous. They will return directly before the execution of time-consuming operations (such as I/O) without waiting for the execution of time-consuming operations.
async and await keywords are used to implement asynchronous programming and make your code look synchronous.
Dealing with Future
The results of Future execution can be obtained in the following two ways:
- Use async and await;
- Use the Future API. For details, refer to Library overview.
Code using async and await is asynchronous, but looks a bit like synchronous code. For example, the following code uses await to wait for the execution result of an asynchronous function.
await lookUpVersion();
await must be used in asynchronous functions with async keyword:
Future checkVersion() async { var version = await lookUpVersion(); // Continue processing logic using version }
Notes:
Although an asynchronous function can handle time-consuming operations, it does not wait for these time-consuming operations to complete. When an asynchronous function executes, it will encounter the first await expression( Code line )Return a Future object, and then wait for the await expression to continue after execution.
Use try, catch, and finally to handle exceptions caused by using await:
try { version = await lookUpVersion(); } catch (e) { // Reaction when version cannot be found }
You can use the await keyword multiple times in asynchronous functions. For example, the following code waits for the result of the function three times:
var entrypoint = await findEntrypoint(); var exitCode = await runExecutable(entrypoint, args); await flushThenExit(exitCode);
The return value of await expression is usually a Future object; If not, it will be automatically wrapped in a Future object. The Future object represents a "commitment", and the await expression will block until the required object returns.
If you cause compilation errors when using await, make sure that await is used in an asynchronous function. For example, if you want to use await in the main() function, the main() function must be identified with the async keyword.
Future main() async { checkVersion(); print('stay Main Function: version is ${await lookUpVersion()}'); }
Declare asynchronous function
An asynchronous function is a function whose body is marked by the async keyword.
Add the keyword async to the function and let it return a Future object. Suppose there are the following methods to return String objects:
String lookUpVersion() => '1.0.0';
Change it to an asynchronous function, and the return value is Future:
Future<String> lookUpVersion() async => '1.0.0';
Note that the function body does not need to use the Future API. Dart creates Future objects if necessary.
If the function does not return a valid value, you need to set its return type to future < void >.
For an introduction to the use of Future, async and await, see the codelab: asynchronous programming codelab.
Process Stream
If you want to get values from a Stream, you have two options:
- Use the async keyword and an asynchronous loop (identified by the await for keyword).
- Use the Stream API. Refer to for details Library overview.
Notes:
Before using the await for keyword, make sure it can make the code logic clearer and is true. You need to wait for all the results to be executed. For example, you should not normally use the await for keyword on a UI event listener because the flow of events emitted by the UI framework is endless.
Using await for to define an asynchronous loop looks like this:
await for (varOrType identifier in expression) { // Executes whenever a Stream issues a value }
Expression must be of type Stream. The execution process is as follows:
- Wait until the Stream returns a data.
- Execute the loop body using the data returned by Stream in 1.
- Repeat steps 1 and 2 until the Stream data is returned.
Use the break and return statements to stop receiving Stream data, which will jump out of the loop and cancel the registration to listen to the Stream.
If you encounter compile time errors when implementing an asynchronous for loop, check to ensure that await for is in an asynchronous function. For example, to use an asynchronous for loop in an application's main() function, the body of the main() function must be marked async:
Future main() async { // ... await for (var request in requestServer) { handleRequest(request); } // ... }
You can refer to the library overview dart:async For more information on asynchronous programming.
generator
When you need to generate a series of values late, consider using the generator function. Dart built-in supports two forms of generator methods:
- Synchronization generator: returns a Iterable Object.
- Asynchronous generator: returns a Stream Object.
A synchronization generator function is implemented by adding the sync * keyword to the function and setting the return value type to iteratable. The yield statement is used to pass the value in the function:
Iterable<int> naturalsTo(int n) sync* { int k = 0; while (k < n) yield k++; }
Implementing the asynchronous generator function is similar to synchronization, except that the keyword is async * and the return value is Stream:
Stream<int> asynchronousNaturalsTo(int n) async* { int k = 0; while (k < n) yield k++; }
If the generator is called recursively, use the yield * statement to improve execution performance:
Iterable<int> naturalsDownFrom(int n) sync* { if (n > 0) { yield n; yield* naturalsDownFrom(n - 1); } }
Callable class
By implementing the call() method of the class, it is allowed to use instances of the class in a way similar to function calls.
In the following example, WannabeFunction class defines a call() function. The function accepts three string parameters. The function body splices the three strings, separates the strings with spaces, and appends an exclamation point at the end. Click the run button to execute the code.
Isolation zone
Most computers, even on mobile platforms, use multi-core CPU s. In order to make effective use of multi-core performance, developers generally use shared memory to run threads concurrently. However, multi-threaded sharing of data usually leads to many potential problems and errors in code operation.
In order to solve the concurrency problem caused by multithreading, Dart uses isolate instead of thread, and all Dart code runs in one isolate. Each isolate has its own heap memory to ensure that its state is not accessed by other isolates.
You can refer to the following documents for more information:
- Dart asynchronous programming: isolation area and event loop
- dart:isolate API reference Introduced Isolate.spawn() and TransferableTypedData Usage of
- Background parsing cookbook on the Flutter site
- About on the Flutter website Background parsing My Cookbook
type definition
In Dart language, functions are objects like String and Number. You can use type definitions (or method type aliases) to name the types of functions. When a function of this function type is assigned to a variable with a function name, the type definition will retain the relevant type information.
For example, the following code does not use type definitions:
class SortedCollection { Function compare; SortedCollection(int f(Object a, Object b)) { compare = f; } } // Simple incomplete implementation. int sort(Object a, Object b) => 0; void main() { SortedCollection coll = SortedCollection(sort); // We know that compare is a variable of function type, but we don't know what kind of function it is. assert(coll.compare is Function); }
In the above code, when the parameter f is assigned to compare, the type information of the Function is lost. Here, the type of F is (Object, Object) → int (→ represents return). Of course, this type is also a subclass of Function, but after assigning f to compare, the class type of F (Object, Object) → int will be lost. We can use typedef to explicitly preserve type information:
typedef Compare = int Function(Object a, Object b); class SortedCollection { Compare compare; SortedCollection(this.compare); } // Simple incomplete implementation. int sort(Object a, Object b) => 0; void main() { SortedCollection coll = SortedCollection(sort); assert(coll.compare is Function); assert(coll.compare is Compare); }
Notes:
At present, type definitions can only be used for function types, but they may change in the future.
Because the type definition is only an alias, we can use it to judge the type of any function:
typedef Compare<T> = int Function(T a, T b); int sort(int a, int b) => a - b; void main() { assert(sort is Compare<int>); // True! }
metadata
Using metadata can add some additional information to the code. Metadata annotations start with @ followed by a compile time constant (such as deprecated) or call a constant constructor.
There are two annotations in Dart that all code can use: @ deprecated and @ override. You can look it up Extend a class Get usage examples about @ override. The following is an example of using @ deprecated:
class Television { /// _ Deprecated: use [turnOn] instead_ @deprecated void activate() { turnOn(); } ///Turn on the TV. void turnOn() {...} }
Metadata annotations can be customized. The following example defines a @ todo annotation with two parameters:
library todo; class Todo { final String who; final String what; const Todo(this.who, this.what); }
Example of using @ todo annotation:
import 'todo.dart'; @Todo('seth', 'make this do something') void doSomething() { print('do something'); }
Metadata can be used before library, class, typedef, type parameter, constructor, factory, function, field, parameter or variable declaration, or before import or export. You can use reflection to get metadata information at run time.
notes
Dart supports single line comments, multi line comments and document comments.
Single-Line Comments
Single line comments start with / /. Everything between / / and the end of the line is ignored by the compiler.
void main() { // TODO: refactor into an AbstractLlamaGreetingFactory? print('Welcome to my Llama farm!'); }
multiline comment
Multiline comments start with / * and end with * /. All contents between / * and * / are ignored by the compiler (document comments are not ignored), and multiline comments can be nested.
void main() { /* * This is a lot of work. Consider raising chickens. Llama larry = Llama(); larry.feed(); larry.exercise(); larry.clean(); */ }
Documentation Comments
Document comments can be multi line comments or single line comments. Document comments start with / / or / * *. Using / / / on consecutive lines has the same effect as multi line document comments.
In document comments, the Dart compiler ignores all text unless enclosed in square brackets. Use brackets to reference classes, methods, fields, top-level variables, functions, and parameters. The symbols in parentheses are parsed in the lexical domain of the recorded program element.
Here is a document note that references other classes and members:
/// A domesticated South American camelid (Lama glama). /// /// Andean cultures have used llamas as meat and pack /// animals since pre-Hispanic times. class Llama { String name; /// Feeds your llama [Food]. /// /// The typical llama eats one bale of hay per week. void feed(Food food) { // ... } /// Exercises your llama with an [activity] for /// [timeLimit] minutes. void exercise(Activity activity, int timeLimit) { // ... } }
In the generated document, [Food] will become a link to the API document of Food class.
Parse Dart code and generate HTML documents. You can use the Document generation tool For examples of generating documents, refer to Dart API documentation For suggestions on document structure, please refer to the document: Guidelines for Dart Doc Comments..
summary
This page provides an overview of the features commonly used in the Dart language. There are more features to implement, but we hope they won't break existing code. For more information, refer to Dart language specification and Efficient Dart Language Guide.
To learn more about Dart core library, please refer to Dart core library overview