preface
Because the Dart official document is not very detailed and has too many language features, I often get stuck on a certain point when reading the official document. Therefore, I complete this introduction to Dart grammar by consulting various materials.
All the information in this article comes from the OFFICIAL Dart documentation, the Dart blog, etc. The purpose of this article is to help students who want to learn about Flutter master Dart language features or solve some questions about Dart.
If you know Typescript or Java, you can quickly read the Dart features. You just need to learn the syntax differences.
If you only know JavaScript, this article is for you, and you’ll get an in-depth look at all of Dart’s features. I sometimes compare Dart to JavaScript.
If you already know Dart but sometimes forget the grammar, you can quickly find out what you want to know.
I hope this article has been useful to you who use or will soon use Dart development.
No more words, let’s go!
A simple Dart program
In DART, we need to run the program inside main, and all code needs to be written inside main to be called.
// Define a function.
void printInteger(int aNumber) {
print('The number is $aNumber. '); // Print to the output console
}
// The position where the application starts execution must be written
void main() {
var number = 42; // Declare and initialize a value
printInteger(number); // Execute a function
}
Copy the code
The above code contains the following:
/ / : a comment
Viod: the function returns no value
Var: Lets Dart automatically determine how a type is declared
Print () : Prints the content
Int: indicates the integer number type
{expression} : string interpolation, string variables containing variables or expressions
Main () : Special and required top-level function from which Dart will start execution
Important concepts
- All variables refer to objects, and each object is an instance of a class. Numbers, functions, and Nulls are all objects. With the exception of NULL (if null security is enabled), all classes inherit from the Object class.
- Although Dart is strongly typed, it is possible to declare variables without specifying a type, and Dart has the same type inference as Typescript.
- If null security is enabled, variables cannot be null if they are not declared as nullable types. You can use the type suffix (?) Declare the type as nullable. For example, int? Variables can be integer numbers or NULL.
- If Dart thinks an expression may be null, but you know it can’t be null, you can use an assertion (!) To mean not empty. For example: int x=nullableButNotNullInt!
- If we want to explicitly allow any type, we can use Object? (with null security enabled), Object, or the special type Dynamic
- Dart supports generics, such as arrays
List<int>
Represents a list or list of int objectsList<Object>
Represents a list of objects of any type - Dart supports top-level variables as well as variables (static and instance variables) that define a class or object. Instance variables can be called attributes.
- Dart does not have public, protected, or private member access qualifiers. To represent private, use
_
Precede the variable declaration as an identifier. - Variable declarations, like in other languages, can start with a letter or underscore followed by a combination of characters and numbers.
- Datr expressions have values, statements have no values. Like conditional expressions
expression condition ? expr1 : expr2
Contains the valueexpr1
expr2
.if-else
A branch statement has no value. - The Dart tool displays both warning and error types of problems. Warnings indicate a problem with the code but do not prevent it from running; Errors include compile errors and run errors. Compile errors cause code to fail to run, and run errors cause exceptions at run time.
Variable declarations
Variable declarations can be made as follows:
var str='hello dart';
var num=123;
// A string of characters
String str='hello dart';
// Number type
Int num=123;
Copy the code
Using var, you can infer the type automatically, or you can write the type String manually, as Java does.
Automatic inference is preferred, as recommended by the efficient guidelines from the OFFICIAL Dart language
If a reference to an Object can be of any type, as with ANY for TS, then Object or Dynamic can be specified
Object a = 'blob';
a = 123;
a = [];
dynamic b = 'blob';
b = 123;
b = [];
Copy the code
Difference between Object and Dynamic
Object is the base class of all classes and is equivalent to a supertype that is compatible with all types. Dynamic is a dynamic class, similar to TypeScript’s any.
In the following code, an Object declaration would fail compilation.
Object a = 'String';
a.subString(1); //❌The method 'subString' isn't defined for the type 'Object'
Copy the code
Changing to Dynamic indicates that this is a dynamic type, bypassing compilation checks.
dynamic a = 'String';
a.substring(1);
Copy the code
Final vs. const
Constants can be declared with final and const modifiers, which can either replace var or precede the type.
final name='some name';
const age=20;
const int age = 123;
final List list = [];
Copy the code
Final contains the function of const. The difference is that:
- Final can be assigned initially and only once if it is assigned. Const needs to be assigned to begin with
- Const must be given an explicit compile constant value (that is, a value determined at compile time)
- Final can obtain a value (that is, a value determined at run time) by evaluating/functions
- Final not only has the property of being a const compile-time constant, but it is lazy-initialized, that is, not initialized until it is first used at run time
For example
// The constant 'a' must be initialized is initialized.
const a;
// Error initialized Const variables must be initialized with a constant value
const a = new DateTime.now();
final b;
b = new DateTime.now(); // No error will be reported
Copy the code
Const variables are compile-time constants. If you use const to modify a variable in a class, you must add the static keyword, that is, static const.
const
A const variable can be assigned directly when declared, or it can be assigned using another const variable.
const bar = 1000000; // Unit of pressure (dynes/cm2)
const double atm = 1.01325 * bar; // Standard atmosphere
Copy the code
The const keyword is used not only to define constants, but also to create constant values that can be assigned to any variable. A constructor of this type can also be declared to be const; the object created by this type of constructor is immutable.
var foo = const [];
final bar = const [];
const baz = []; // equivalent to 'const []'
Copy the code
Assigning a constant using an initialization expression omits the const keyword. For example, assigning the constant bar omits const.
It is also possible to create a singleton-like object using const variables, such as the following syntax, which creates the same object
class Person {
final String name;
const Person(this.name);
}
void main(List<String> args) {
const p1 = const Person('myname');
const p2 = Person('myname'); // const can be omitted
print(identical(p1, p2));// These two objects are equal
}
Copy the code
The default value
The default value for uninitialized and nullable variables is NULL.
int? lineCount;
assert(lineCount == null);
var a;
assert(a == null);
Copy the code
Calls to assert() will be ignored in production code. During development, assert(condition) will throw an exception if the condition is judged to be false.
If null security is enabled, a variable must be initialized before you can use it
int count;
// The non-nullable local variable 'count' must be assigned before it can be used. Local variables that are not empty must be assigned before they are used
print(count);
Copy the code
We don’t have to initialize the variable at first declaration, but we need to assign it before we use it. For example, the following code is valid because Dart detects that count is an assigned variable before it is passed to print for use
int count;
count = 0;
print(count);
Copy the code
Top-level and class variables are lazily initialized: the initialization code runs when the variable is first used.
The data type
-
Numbers: int and double
-
Strings Type: String
-
Booleans type: bool
-
Lists Type: List
-
Maps type: Map type
-
Sets: Sets
-
Symbols type: Symbol
-
Null: Null
-
Runes: Commonly used for character substitution in the Characters API
Each variable reference in Dart points to an object, and a constructor can also be used to initialize variables. Some built-in types have their own constructors, such as using Map() to create a Map object.
There are some types of special roles in Dart:
- Object: is a superclass of all types except Null
- Future and Stream: Used asynchronously
- Iterable: Generator constructor for for-in loops and synchronization
- Never: Indicates that the expression can Never be reached. Usually used when a function throws an exception.
- Dynamic: This type can be used if you want to disable static checking. Usually you can use Object or Object? Instead.
- Void: Indicates that no value is returned
Numbers
Int is an integer and double is a floating-point number.
Numeric types are subclasses of num. Num defines some basic operators and methods. You can also look at the APIS in the DART: Math library.
Double is compatible with integers, meaning that a double can be either an integer or a floating-point number.
If you want to declare that a type is a number, either an integer or a floating-point number, you can use the num type
num x = 1;
x += 2.5;
print(x); / / 3.5
Copy the code
If you declare a double but the value is an integer, it is automatically converted to a float.
double a = 1;
print(a); / / 1.0
Copy the code
Number to string:toString()
int a = 123;
double b = 123.23;
String _a = a.toString();
assert(_a == '123');
String _b = b.toStringAsFixed(1);
assert(_a == '123.2');
Copy the code
String to number:parse
String a = '123';
String b = '123.23';
int _a = int.parse(a);
assert(_a == 123);
double _b = double.parse(b);
assert(_b == 123.23);
Copy the code
It is recommended to use double. Parse to convert to numbers
Strings
Strings can be quoted in single/double quotes, single quotes inside double quotes can be escaped, and vice versa.
var s1 = 'Creates string literals 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 a slash to escape strings that conflict with single quotes: \'. ';
var s4 = "In double quotes, you don't need to escape strings that conflict with single quotes: '";
Copy the code
String concatenation with $or ${} or +, the specific use of ${expression}, if a single variable, can be omitted {}. If the expression results in an object, Dart automatically calls the object’s toString method to get a string.
var s = 'this is string';
var map = {"name": "qiuyanxi"};
print(The value of 'is:${s.toUpperCase()}The value of the map is$map');
THIS IS STRING map {name: qiuyanxi}
Copy the code
Using three single quotes or three double quotes creates a multi-line string.
var s1 = '''
You can create
multi-line strings like this one.
''';
var s2 = """This is also a
multi-line string.""";
Copy the code
If you want nothing to be done to the contents of the string (such as escaping), you can create a RAW string by prefixing the string with r.
var s = R 'In raw strings, the escaped string \n will print "\n" directly instead of escaping to a newline. ';
Copy the code
Compile-time constants (null, number, string, Boolean) can be interpolated as string literals only if the string is a compile-time constant declared by const.
// These are available in string constants declared by const
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';
// These cannot be used in const strings, only var declarations are useful
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';
var invalidConstString = '$aNum $aBool $aString $aConstList';
Copy the code
booleans
Bool Indicates a Boolean type
Dart’s conditional judgment is different from JavaScript, which can use Falsy or Truthy values for conditional judgment
0, ' ',false,undefined,null, -0,NaN // Falsy value of javascript
/ / such as:
let a;
if(! a) {console.log(` a is${a}`); / / a is undefined
}
Copy the code
Dart requires a bool or a return bool to make a conditional judgment.
Bool type
String? a = null;
// Conditions must have a static type of 'bool'.Try changing the condition
// ❌ The condition must be bool
/* if (a) { print(a); } else { print(a); } * /
/* The following code is correct */
bool b;
bool getResult() {
return true;
}
b = getResult();
if (b) {
print(b);
} else {
print(b);
}
Copy the code
Returns a value of type bool
// The dart version determines whether it is an empty string, NaN, null, or 0
// Check for an empty string.
var fullName = ' ';
assert(fullName.isEmpty);
// Check for zero.
var hitPoints = 0;
assert(hitPoints == 0);
// Check for null.
var unicorn;
assert(unicorn == null);
// Check for NaN.
var iMeantToDoThis = 0 / 0;
assert(iMeantToDoThis.isNaN);
Copy the code
Lists
Like arrays in JavaScript, Dart arrays are wrapped Object special classes, not arrays in the traditional sense.
List declaration
var arr = <String> ['0'.'1'.'2'.'3']; // Define the array type
var arr1 = [0.1.2.3.4]; // Automatic inference
List arr5 = <String> ['0'.'1'.'2'.'3'];// Define the list as a type
Copy the code
-
Use the const keyword to create compile-time variables that cannot be modified or added
var arr2 = const [1.2.3.4]; // Create a compile-time constant that cannot be modified or added arr2.add(5); // Cannot add to an unmodifiable list Copy the code
-
Create a fixed length collection
var arr3 = List.filled(2.' ');// Create a fixed length collection var arr4 = List.filled<int> (2.0);// Create a fixed length typed collection Copy the code
-
Extend operator operations on arrays
var list = [1.2.3]; var list2 = [0. list];// Insert list into list2 assert(list2.length == 4); Copy the code
-
The null-aware operator operates on an array to avoid exceptions if null
var list; var list2 = [0. ? list];assert(list2.length == 1); Copy the code
-
Get the array length
var arr = <String> ['0'.'1'.'2'.'3']; arr.length Copy the code
-
Check whether it is empty
var arr = <String> ['0'.'1'.'2'.'3']; arr.isEmpty arr.isNotEmpty Copy the code
-
Flip the array
var arr = ['1'.'2']; var newArr = arr.reversed.toList(); print(newArr); Copy the code
-
You can use if or for in a List
var nav = ['Home'.'Furniture'.'Plants'.if (true) 'Outlet']; var listOfInts = [1.2.3]; var listOfStrings = ['# 0'.for (var i in listOfInts) '#$i']; print(listOfStrings); // [#0, #1, #2, #3] Copy the code
For other apis, please refer to the official documentation.
Sets
Dart’s SET declaration
var halogens = {'fluorine'.'chlorine'.'bromine'.'iodine'.'astatine'};
Set s = <String> {'fluorine'.'chlorine'};
Copy the code
You can create an empty Set by prefixing {} with a type argument, or assign {} to a variable of type Set
var s = <String> {};Set _s = <String> {};Set<String> names = {};
var _names = {}; // This is a map, not a set
Copy the code
- use
add
Methods oraddAll
Method to add items
var sets = <Object> {}; sets.add('1');
sets.addAll([1.2.3]);
print(sets);
Copy the code
-
Use.length to get the number of elements in a Set
final sets = {'fluorine'.'chlorine'}; print(sets.length); Copy the code
-
Add the const keyword to create a Set compile-time variable
final constantSet = const { 'fluorine'.'chlorine'.'bromine'.'iodine'.'astatine'};// constantSet.add('helium'); // This line will cause an error. Copy the code
-
Sets can use both extension operators and null-aware operators
final sets = {'fluorine'.'chlorine'}; var maybeNull; final a = <String> {'hello'. sets};final b = <String> {'hello'. ? maybeNull};print(a); print(b); Copy the code
Maps type
The Maps type in Dart is similar to the Map data structure in JavaScript, except that quotes are enforced around keys. The Maps type is used as an object in Dart.
Declare a map, using var to make the map automatically infer, or manually write the map type
const a = [1.2.3];
var map = {a: '123'}; // map is used for js map, and key is not used for [key].
var map1 = <String.String> {'a': '123'}; // map is used as the js object, and the key should be quoted
var map2 = Map(a);// Create a map of free type
var map3 = Map<int.String> ();// Define the type when creating the map
map3[1] = '1'; // Assign a value to map
print(map);
print(map1);
print(map.containsKey(a)); // js's map.has method determines whether the key exists
Copy the code
In JavaScript, you can use new Map() to turn normal functions into constructors. Dart can omit new, and the above code creates a Map object using the Map() constructor.
-
Add a single attribute and multiple attributes
var map = {}; map['age'] = 20; map.addAll({"name": 'qiuyanxi'.1: 2}); print(map); Copy the code
-
Returns null if the key is not in the map
var map = {}; assert(map['name'] = =null); Copy the code
-
Length gets the number of key-value pairs
var map = {}; assert(map.length == 0); Copy the code
-
Adding the const keyword to a Map literal creates a Map compile-time constant:
final constantMap = const { 2: 'helium'.10: 'neon'.18: 'argon'};// constantMap[2] = 'Helium'; // This line will cause an error. Copy the code
-
Map uses extension operators and null-aware operators
var map = {'name': "qiuyanxi"}; Map? maybeNull; varnewMap = {... map};varnewMap2 = {... ? maybeNull};Copy the code
Access to type
Use runtimeType to get the type of the object
var n = null;
var s = 'String';
print(n.runtimeType); // Null
print(s.runtimeType); // String
Copy the code
function
Define functions. It is recommended to define return types
String getName() {
return 'qiuyanxi';
}
Copy the code
Functions with only one expression can be simplified using the arrow function
String getName() => 'qiuyanxi';
Copy the code
Necessary parameters
String getName(String name, int age) => '$name$age';
getName('qiuyanxi'.10);
Copy the code
Optional position parameter
Use [] to indicate optional positional arguments
void printThings([String? str, String str2 = 'default value']) {
assert(str == null);
assert(str2 == 'default value');
}
printThings();
Copy the code
Named parameters
Named parameters are optional by default. If it is a required parameter, you need required
When defining a function{parameter 1, parameter 2}
To specify named parameters
String getName2({required String name, int? age = 10}) = >'$name$age';
Copy the code
Is used when calling a functionParameter Name: Parameter value
Specify named parameters
getName2(name: 'qiuyanxi');
Copy the code
The default parameters
If a parameter is optional but cannot be null, a default value needs to be provided. The argument is null if there is no default value
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold = false.bool hidden = false{...}) }// bold will be true; hidden will be false.
enableFlags(bold: true);
Copy the code
The default value
Only optional arguments have default values, which must be compile-time constants. For example, the following arguments are the default List and Map. To become compile-time constants, the const keyword is required
void getList([List<int> list = const [1.2.3]]) {}
void getMap([Map<String.String> map = const {"name": "qiuyanxi"}]) {}
Copy the code
The main function
The main function, the mandatory top-level function of every Dart program, is the entry point to the program. The main function returns void and has an optional argument of type List
.
You can pass arguments to main from the command line
hello-world.dart
void main(List<String> args) {
Dart hello-world.dart 1 test
print(args); //['1', 'test']
assert(args.length == 2);
assert(int.parse(args[0= =])1);
assert(args[1] = ='test');
}
Copy the code
Anonymous functions
Anonymous functions are used as arguments
const list = ['apples'.'bananas'.'oranges'];
list.forEach((item) {
print('${list.indexOf(item)}: $item');
});
Copy the code
Use the anonymous arrow function as an argument
const list = ['apples'.'bananas'.'oranges'];
list.forEach((item) => print('${list.indexOf(item)}: $item'));
Copy the code
Lexical scope
Dart’s scope is lexical and, like JavaScript, is determined at code writing time.
closure
Closures, like JavaScript, won’t be explained much.
The return value
All functions return a value, even if the return value is void. If no return statement is explicitly written, return NULL is executed by default
// This is a function that explicitly returns void
void returnVoid() {
print('hello');
}
var a = returnVoid();
// void variables cannot be used
// print(a);
// This is a function without a return statement
returnNull() {}
var b = returnNull();
assert(returnNull() == null); // true
Copy the code
The operator
The assignment operator
var a = 1;
int?b; b ?? =2; // Assign 2 to b if b is empty
a += 0; // a=a+0
Copy the code
Arithmetic operator
print(a + b);
print(a - b);
print(a * b);
print(a / b);
print(a % b); / / to take over
print(a ~/ b); / / integer
a ++ // calculate first and then increment
a -- // calculate and then subtract
-- a // subtract first
++ a // increment first and then calculate
Copy the code
Relational operator
print(a == b);
print(a >= b);
print(a <= b);
print(a ! = b); identical(DateTime.now(), DateTime.now()); // Determine whether two objects are equal
Copy the code
Type judgment operator
Operator | Meaning |
---|---|
as |
Type conversion (also used as a specificationClass prefix)) |
is |
Returns true if the object is of the specified type |
is! |
Returns false if the object is of the specified type |
Logical operator
The operator | describe |
---|---|
! * Expression * |
Invert the result of the expression (true to false, false to true) |
` | ` |
&& |
Logic and |
var c = false;
var d = true;
/ * * /
if(! c) {print(c);
}
/* && and */
if (c && d) {}
/ * | | or * /
if (c || d) {}
Copy the code
expression
The expression 1 ?? The expression of 2
If expression 1 is null, expression 2 is returned
/ *?? The operator * /
var i;
var j = i ?? 10; // if I is null, 10 is assigned to j, merging the operator with js null
print(j);
Copy the code
conditions ? The expression 1 : The expression of 2
/* The ternary operator */
var flag;
flag = true;
var f = flag ? 'true' : 'false';
Copy the code
Cascade operator
Cascade operators (.. ,? ..) Allows you to call variables or methods of multiple objects in succession on the same object.
The following code
varpaint = Paint() .. color = Colors.black .. strokeCap = StrokeCap.round .. strokeWidth =5.0;
/* is the same as */
var paint = Paint();
paint.color = Colors.black;
paint.strokeCap = StrokeCap.round;
paint.strokeWidth = 5.0;
querySelector('#confirm') // Get an object.? . text ='Confirm' // Use its members.
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed! '));
/* is the same as */
var button = querySelector('#confirm'); button? .text ='Confirm'; button? .classes.add('important'); button? .onClick.listen((e) =>window.alert('Confirmed! '));
Copy the code
Other operators
The operator | The name | describe |
---|---|---|
(a) |
Method of use | Represents calling a method |
[] |
Access to the List | Access an element at a specific location in the List |
? [] |
Nullify access List | When the left caller is not empty, accesses the element at a specific location in the List |
. |
Access to members | Member accessor |
? . |
Conditional access member | Similar to the above member accessors, except that the operation object on the left cannot be null, such as foo? Bar, which returns null if foo is null, bar otherwise |
Sentenced to empty
-
Checks if the string is empty
var str = ' '; if (str.isEmpty) { print('Judged to be empty string'); } Copy the code
-
Check whether the value is null
var _null = null; if (_null= =null) { print('Judged as null'); } Copy the code
-
Determine if it is NaN
var _nan = 0 / 0; if (_nan.isNaN) { print('is NaN'); } Copy the code
Air safety
Dart currently supports controlled security, which means that their values cannot be null unless we declare them nullable, which has the benefit of improving the robustness of the code and providing errors at compile time.
Air safety requires a DART of 2.12 or above
To declare nullability, place a question mark in front of the type:
int? count;
count = null;
Copy the code
If there is no question mark, then the value cannot be empty
int count;
// ❌ A value of type 'Null' can't be assigned to a variable of type 'int'.
count = null;
Copy the code
If we know that a value cannot be null, but Dart judgment may be null, then use! Represents a non-empty assertion
String? getData(String? data) {
if (data is String) {
return 'this is string data';
}
return null;
}
- String a = getData('12131');
+ String a = getData('12131')!;
Copy the code
Flow control statement
-
The for loop
for (var i = 0; i < 10; i++) { print(i); } Copy the code
JavaScript var has only one scope in the for loop. Dart var does not have this problem, so the above code will type I normally.
-
for… In circulation
Use for.. In iterates over iterable objects, such as Lists and sets
var list = [1.2.3]; var sets = <int> {1.2.3}; for (var value in list) { print(value); } for (var value in sets) { print(value); } Copy the code
Iterable objects can also loop through using the forEach method
var collection = [1.2.3]; collection.forEach(print); / / 1 2 3 Copy the code
-
The while loop
var i = 10; while (i > 0) { print(i); i--; } Copy the code
-
The do while loop
do { print(i); i--; } while (i > 0); Copy the code
The difference between do while and while is that the do while loop does once even if the condition is not met; The while loop doesn’t
var i = 0; do { print(i); // This code executes i--; } while (i > 0); while (i > 0) { print(i);// Never execute i--; } Copy the code
-
Break, continue statement
Break skips the loop, and continue skips the loop
-
The switch and the case
-
Assertions, assert
assert(1 < 2); assert(1 > 2.'1>2 is wrong'); Copy the code
Exception handling
Dart provides two types of exceptions, Exception and Error, as well as subclasses. We can define our own exception type, or we can throw any non-null object as an exception.
- An exception is thrown
throw new Exception('This is an exception.');
throw 'This is an exception.';
Copy the code
-
Catch exceptions
try { // throw Error(); throw Exception('this is exception error'); } on Exception catch (e) { print('this is Unknown exception $e'); } catch (e,s) { print('No specified type, handles all error $e'); print('Stack trace:\n $s'); } Copy the code
The above code uses ON and catch to catch exceptions, with ON specifying the type of exception and catch catching objects. When an error is thrown that is not of the exception type specified by ON, the last catch pocket is taken.
The catch method takes two arguments. The first argument is the exception object thrown and the second argument is the stack information.
-
Rethrow Throws an exception again
When we catch an exception, we can throw it again.
The following example throws an exception caught by an inner function into an outer scope for the code in main to catch.
void misbehave() { try { dynamic foo = true; print(foo++); // Runtime error } catch (e) { print('misbehave() partially handled ${e.runtimeType}. '); rethrow; // Allow callers to see the exception.}}void main() { try { misbehave(); } catch (e) { print('main() finished handling ${e.runtimeType}. '); }}Copy the code
The code above prints the following:
misbehave() partially handled NoSuchMethodError. main() finished handling NoSuchMethodError. Copy the code
-
Finally
A finally statement is executed whether or not an exception is thrown.
try { throw Error(); } catch (e) { print('i will catch this error'); } finally { print('finally print this message'); } Copy the code
class
Dart is a language that supports mixin-based inheritance (JavaScript is prototype-based), where all objects are instances of a class and all classes except null inherit from the Object class.
Define the attributes and methods of the class
// Class definitions cannot be written in main
class Person {
String? name; // Declare instance variable name, initially null.
int age = 0; // Declare y, initially 0.
void getInfo() {
print('The ${this.name} ------ The ${this.age}');
}
void setName(String name) {
this.name=name; }}void main(List<String> args) {
var p = new Person();// You can omit new
p.getInfo();
p.setName('my name');
p.getInfo();
}
Copy the code
Instance attributes default to NULL if not initialized.
All instance variables implicitly declare Getter methods. Instance variables that can be modified and late final declarations but variables that are not initialized also implicitly declare a Setter method that we can use to read or set instance objects through getters and setters.
class Person {
String? name;
int age = 0;
late final int height;
}
void main(List<String> args) {
var p = new Person();
p.name = 'my name';// setter
p.height = 180;// setter
print(p.name);// getter
print(p.height);// getter
p.height = 190; // Field 'height' has already been initialized.
}
Copy the code
Instance variables can be final, in which case they can only be set once.
The constructor
Constructors can be created using a function with the same name as the class, and there is also a named constructor.
class Point {
double x = 0;
double y = 0;
// Point(double x, double y) {
// this.x = x;
// this.y = y;
// }
// The syntax of the constructor above can be written like this
Point(this.x, this.y);
// The named constructor uses the initializer list
Point.origin(double xOrigin, double yOrigin):x=xOrigin,y=yOrigin
// The named constructor can also be written like this
// Point.origin(double this.x, double this.y);
}
// Use it in main
void main(List<String> args) {
// Use the named constructor
var p1 = new Point.origin(10.20);
var p2 = Point(10.20);
}
Copy the code
Use the this keyword to reference the current instance.
If no constructor is declared, Dart automatically generates a no-argument constructor that calls the no-argument constructor of its parent class.
Constructors are not inherited, which means that subclasses cannot inherit constructors from their parent class. The named constructor also cannot be inherited.
There can be more than one named constructor that can be called directly when instantiated as needed.
Initialization list
Before the constructor runs, there is a concept of an initializer list. Instance variables can be initialized
class Rect {
int height;
int width;
Rect()
: width = 10,
height = 10 {
print("The ${this.width}---The ${this.height}");
}
Rect.create(int width, int height)
: width = width,
height = height {
print("The ${this.width}---The ${this.height}"); }}void main(List<String> args) {
var p1 = Rect(); / / 10-10
var p2 = Rect.create(100.200); / / 100-200
}
Copy the code
When constructed using Rect, the initializer list initializes width and height to 10.
When the rect.create construct is used, the initializer list is initialized with the value passed in.
Initializer lists can solve the problem of a class variable that is both final and optional and whose optional parameter defaults need to be determined dynamically.
class Person {
static final String name = 'myname';
final int age;
Person() : age = Person.name == 'myname' ? 10 : 20;
}
Copy the code
The above code age is optional and the default value of age needs to be determined dynamically, so there is no way to use optional parameters, as in the following example:
class Person {
static final String name = 'myname';
final int age;
// ❌ The default value of an optional parameter must be constant.
Person([this.age = Person.name == 'myname' ? 10 : 20]);
}
Copy the code
Redirect constructor
When a constructor is called, giving that constructor the ability to call another constructor is called a redirection constructor.
In the following code, when you call Person.init(), you are redirected to the Person constructor, essentially initializing the list.
class Person {
String name;
int age;
Person(this.name, this.age);
Person.init(String name, int age) : this(name, age);
}
Copy the code
Constant constructor
When a class variable is final, the constructor needs to be const, otherwise it will get an error when instantiated.
The following example creates two equal objects, similar to the singleton pattern.
class Person {
final String name;
const Person(this.name);
}
void main(List<String> args) {
const p1 = const Person('myname');
const p2 = Person('myname'); // const can be omitted
print(identical(p1, p2));// These two objects are equal
}
Copy the code
Factory constructor
Whereas a normal constructor returns an object automatically, a factory constructor’s best feature is that it can return an object manually.
This is not accessible in the factory constructor
The following uses the factory constructor to actively return a singleton
class Person {
String name;
static final Map<String, Person> cache = {};
Person(this.name);
factory Person.getSingle(String name) {
if (cache.containsKey(name)) {
return cache[name] as Person;
} else {
cache[name] = new Person(name);
return cache[name] asPerson; }}}void main(List<String> args) {
var p1 = Person.getSingle('qyx');
var p2 = Person.getSingle('qyx');
print(identical(p1, p2));// true
}
Copy the code
Private properties/methods of the instance
Separating a class into a file and prefixing an attribute or method with an _ defines the private variables of the instance object.
lib/Person.dart
class Person {
String? name; // Declare instance variable name, initially null.
int _age = 0; // Declare y, initially 0.
void getInfo() {
print('The ${this.name} ------ The ${this._age}'); }}Copy the code
main.dart
import 'lib/Person.dart';
void main(List<String> args) {
var p = Person();
// print(p._age); invalid
p.getInfo();
}
Copy the code
Getter and Setter
The constructor automatically sets getters and setters for instance variables, or we can specify them manually, which has the advantage of listening for properties.
class Rect {
int height;
int width;
Rect(this.width, this.height);
// Manually specify getters
get area {
return this.height * this.width;
}
// Manually specify how to write the setter
set h(int value) {
print("When you call xx.h, it prints this message to indicate that you are being listened to.");
this.height = value;
}
set w(int value) {
this.width = value; }}void main(List<String> args) {
var p = Rect(10.20);
print(p.area);// getter
p.h = 100;// setter
p.w = 100;
print(p.area);
}
Copy the code
Static members
As with TS, static is used to declare static members.
class Rect {
static int height = 10;
static int width = 10;
static getArea() {
print(height * width); }}void main(List<String> args) {
Rect.getArea();
}
Copy the code
There are two points to note:
-
Static members do not have access to instance variables
class Rect { int height = 10; static int width = 10; static getArea() { print(this.height * width); // Failed to access the height attribute}}Copy the code
-
Instance methods can access static members
class Rect { int height; static int width = 10; Rect(this.height); getArea() { print(this.height * width);// If accessing instance attributes, this is recommended.}}void main(List<String> args) { new Rect(10).getArea(); } Copy the code
inheritance
Constructors cannot be inherited; the extends and super keywords are used to inherit properties and methods of the parent class.
Pure inherited parent class
class Animal {
String name;
void sound(voice) {
print(voice);
}
Animal(this.name);
}
class Dog extends Animal {
Dog([String name = 'dog') :super(name);
}
void main(List<String> args) {
var dog = new Dog();
print(dog.name); // dog
dog.sound('wang wang'); / / wang wang
}
Copy the code
Where Dog([String name = ‘Dog ‘]) : super(name); Some explanation is needed:
: super(name)
This syntax is set with an initializer that calls the constructor of its parent class when Dog is constructedname
Dog([String name = 'dog'])
This syntax is callednew Dog()
whenname
This parameter is optional. The default value isdog
Extend the attributes and methods of subclasses
class Animal {
String name;
void sound(voice) {
print(voice);
}
Animal.create(this.name);
}
class Dog extends Animal {
String sex;
Dog(this.sex, [String name = 'dog') :super.create(name);
void run() {
print('The ${this.name} runrun'); }}Copy the code
Overrides properties and methods of the parent class
class Animal {
String name;
void sound(voice) {
print(voice);
}
Animal.create(this.name);
}
class Dog extends Animal {
String sex;
Dog(this.sex, [String name = 'dog') :super.create(name);
void run() {
print('The ${this.name} runrun');
}
@override
void sound(voice) {
print('The ${this.name} $voice'); }}void main(List<String> args) {
var dog = new Dog('male');
print(dog.name); // dog
dog.sound('wang wang'); / / dog barked
}
Copy the code
It is recommended to override the attributes and methods of the parent class using @Override
A method that calls the parent class in a subclass
The method of the parent class is called by super
class Dog extends Animal {
String sex;
Dog(this.sex, [String name = 'dog') :super.create(name);
void run() {
super.sound('wang wang');
print('The ${this.name} runrun'); }}Copy the code
An abstract class
-
Abstract classes are primarily used to define standards
-
Abstract classes cannot be instantiated; only subclasses that inherit from them can be instantiated
-
Abstract classes that want to be instantiated can use factory constructors
Use the abstract keyword to indicate that this is an abstract class.
For example, the following defines an abstract class for Animal, which contains the criteria for all animals.
abstract class Animal {
sound(); // Abstract methods
print() {} // Ordinary methods may not be implemented by subclasses
}
// Subclasses must implement the same abstract methods
class Dog extends Animal {
@override
sound() {}
}
Copy the code
polymorphism
Polymorphism is the same operation on different objects, can produce different interpretations and different effects.
In JavaScript, polymorphism is implemented in the form of prototype chains, such as the toString method on Object and Array prototypes, Prototype essentially writes a toString on array. prototype to override the toString on Object. Prototype
Polymorphism in Dart is a way of overriding the parent class definition by subclasses so that each subclass behaves differently.
With an abstract class, you just define the methods of the parent class and don’t implement them, and let the subclasses that inherit them implement them, and each subclass is polymorphic.
abstract class Animal {
sound(); // Abstract methods
}
class Dog extends Animal {
@override
sound() {
print('wang wang');
}
run() {}
}
class Cat extends Animal {
@override
sound() {
print('meow meow');
}
run() {}
}
void main(List<String> args) {
var dog = new Dog();
var cat = new Cat();
print(dog.sound());
print(cat.run());
// The run method cannot be called
Animal _dog = new Dog();
Animal _cat = new Cat();
}
Copy the code
interface
Dart doesn’t have interfaces, so we use abstract classes to define interfaces and implements to make classes match interfaces.
Class matches a single interface
For example, the following uses abstract classes to encapsulate uniform add, delete, change, and query functions
abstract class Db {
String uri;
add();
remove();
save();
select();
}
Copy the code
Use implements to match the interface
class MySql implements Db {
@override
add() {}
@override
remove() {}
@override
save() {}
@override
select() {}
}
Copy the code
The above code can also be inherited and overridden using the extends keyword. In general we use it like this:
-
If we need a common method to reuse, we use extends
-
If you need a canonical constraint, use implements
Class matches multiple interfaces
abstract class A {
late String name;
getA();
}
abstract class B {
getB();
}
class C implements A.B {
@override
getA() {}
@override
getB() {}
@override
late String name;
}
Copy the code
Mixins with
Similar functionality to multiple inheritance can be achieved with mixins, which use the keywords with and mixin
mixin A {
void getA() {}
}
mixin B {
void getB() {}
}
class C with A.B {}
void main(List<String> args) {
var c = new C();
c.getA();
c.getB();
}
Copy the code
The above code mixes the instance methods of multiple Mixins classes.
-
Mixins can only inherit from Object classes, not from other classes.
class A { void getA() {} } class B extends A { void getB() {} } class C with A.B {} // ❌ error: B is a mixins class and cannot be inherited Copy the code
To make mixins classes more intuitive, it is recommended to define mixin classes using the mixin keyword
Mixin A {void getA() {}} mixin B extends A {// ❌Copy the code
-
Classes that are mixins cannot have constructors
mixin A { void getA() {} } mixin B { B(); // ❌ error B is a mixins class and cannot have a constructor void getB() {} } class C with A.B {} Copy the code
-
A class can mixins multiple mixins classes
-
A class can inherit from a class and mixins some mixins classes
class A { void getA() {} } class B { void getB() {} } class C extends A with B {} Copy the code
-
Mixins are neither inheritance nor interface. When mixins are used, they create a superclass that is compatible with all classes
class A { void getA() {} } mixin B { void getB() {} } class C extends A with B {} void main(List<String> args) { var c = new C(); print(c is A);// true print(c is B);// true print(c is C);// true } Copy the code
-
Use the ON keyword to specify which classes can use the Mixin class
class A { void getA() {} } mixin B on A { void getB() {} } // class C with B {} ❌ class C extends A with B {} Copy the code
The generic
Like TS, Dart supports generics, which are generic types, non-specific types that are assigned to the user.
The following functions, for example, specify the type that is passed in.
T getData<T>(T data) {
return data;
}
// The caller can specify the type
getData<String> ('123');
getData<num> (123);
getData<List> ([1.2.3]);
Copy the code
A generic class
When instantiating a class, generics specify the type of the instance object.
The following is the type of the List object property value specified after the List is instantiated.
List l1 = new List<int>.filled(2.1);
List l2 = new List<String>.filled(2.' ');
Copy the code
-
Define generic classes
class A<T> { T age; A(T this.age); T getAge() { return this.age; }}Copy the code
-
Use generic classes
void main(List<String> args) { // Use generic classes var a = new A<int> (12); var b = A<String> ('12'); } Copy the code
A generic interface
A generic interface is defined as a collection of interfaces and generic classes
// Generic interface
abstract class Cache<T> {
void setKey(String key, T value);
}
// The class matches this interface
class FileCache<T> implements Cache<T> {
@override
void setKey(String key, T value) {}
}
class MemoryCache<T> implements Cache<T> {
@override
void setKey(String key, T value) {}
}
Copy the code
Specifies the specific type of the generic when used
var f = new FileCache<String> ();/ / the specified String
f.setKey('key'.'string');
var m = new MemoryCache<int> ();/ / specified int
m.setKey('key'.123);
Copy the code
Limit the generic
Like Typescript, generic constraints use the extends keyword.
abstract class Cache<T> {
void setKey(String key, T value);
}
// MemoryCache must be an int
class MemoryCache<T extends int> implements Cache<T> {
@override
void setKey(String key, T value) {}
}
void main(List<String> args) {
// var m = new MemoryCache
(); It can't be a String here
var m = new MemoryCache<int> (); m.setKey('key'.123);
}
Copy the code
enum
The rules for enumerations are very different from Typescript and are ultimately used to define constant values
-
Define the enumeration
enum Colors { RED, GREEN, BLUE } Copy the code
-
Access the subscript of the enumeration
assert(Color.red.index == 0); Copy the code
-
Gets all enumeration values
Colors.values // [Colors.RED, Colors.GREEN, Colors.BLUE] Copy the code
-
Accessing enumerated values
Colors.RED // Colors.RED Copy the code
-
Access enumerated values with subscripts
assert(Colors.values[0] == Colors.RED); Copy the code
-
The type of an enumeration value
Colors.RED.runtimeType // Colors Copy the code
Newest modifier
Dart2.12 adds the late modifier, which has two uses:
- Used to declare a variable that is initialized after the declaration and cannot be null
- Lazy initialization of variables
Dart’s control flow analysis can detect when non-NULL variables are set to non-NULL values before being used, but sometimes the analysis fails. Two common cases are top-level variables and instance variables: Dart usually can’t determine if they’re set, so it doesn’t try.
If you are sure that the variable was set before it was used, but Dart is inconsistent, you can use late to eliminate the error
// The non-nullable variable 'a' must be initialized.
String a;
void main(List<String> args) {
a = '123';
print(a);
}
Copy the code
In the code above, a is the global variable. Dart has no way of analyzing whether the global variable is set, so the code above will report an error. At this point, the late statement can be used to eliminate errors.
- String a;
+ late String a;
void main(List<String> args) {
a = '123';
print(a);
}
Copy the code
If you mark a variable as late but initialize it when it is declared, the initializer runs when the variable is first used. This lazy initialization is handy in several cases:
-
Variables are not necessarily used, so this initialization is very memory saving
// This is the program's only call to _readThermometer(). late String temperature = _readThermometer(); // Lazily initialized. Copy the code
In the above code, the _readThermometer function will not be called if temperature is never used
-
Late is added when the instance variable is not initialized but needs to be accessed
class Cache { late String name; void setName(String name) { this.name = name; }}Copy the code
In the above code, since there is no constructor, the name attribute will not be initialized when the instance is instantiated, and accessing it will result in an error
var m = new Cache(); // ❌LateInitializationError: Field 'name' has not been initialized. print(m.name); Copy the code
library
Use import to specify the namespace so that other libraries can access it.
The only argument to import is the URI used to specify the code base.
For the built-in Dart library, use the Dart: XXXXXX form.
For other libraries, you can use a file system path or the package: XXXXXX format. Package: The library specified by XXXXXX is provided through a package manager such as the pub utility.
import 'dart:math'; // Introduce the built-in Math library
import 'package:test/test.dart'; // Import the library in the package manager
import 'lib/test.dart'; // Introduce a self-written library
Copy the code
Package bag
Yaml file is manually created in the root directory, similar to package.json of NPM, which is used to manage package versions and dependencies.
Write the following as prompted:
name: my_app
environment:
sdk: '> = 2.12.0 < 3.0.0'
Copy the code
Then you can go to the website to find out what packages are available.
Take the HTTP module as an example:
Download package
Only DART uses this command
$ dart pub add http
Copy the code
Use this command when there is a flutter
$ flutter pub add http
Copy the code
The above command adds version dependencies in pubspec.yaml and implicitly runs dart pub get or flutter pub get
dependencies:
http: ^ 0.13.4
Copy the code
Use the package
import 'package:http/http.dart' as http;
Copy the code
Library conflict
If two imported code bases have the same name, you can use the specified prefix as. For example, if library1 and library2 both have Element classes, we could do this:
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// Uses Element from lib1.
Element element1 = Element(a);// Uses Element from lib2.
lib2.Element element2 = lib2.Element(a);Copy the code
Part of the import
If you want to use only part of the code base, you can use partial imports.
// Import only foo.
import 'package:lib1/lib1.dart' show foo;
// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
Copy the code
export
Use the export keyword to export
export 'package:lib1/lib1.dart';
export 'src/middleware.dart' show Middleware, createMiddleware;
Copy the code
Lazy loading
Lazy loading (also known as lazy loading) is when it is needed.
Lazy-loading is currently supported only by Dart 2JS, Dart VM and DartDevc
Use the Deferred As keyword to identify code libraries that need to be loaded late
import 'package:greetings/hello.dart' deferred as hello;
Copy the code
Call the loadLibrary function to load the library when you actually need to use the library API:
Future<void> greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
Copy the code
Use the await keyword to suspend code execution until the library is loaded. It doesn’t matter that the loadLibrary function can be called multiple times; the code base is only loaded once.
Here are some things to keep in mind when using lazy loading:
- Constants in a lazy-loaded code base need to be imported when the code base is loaded, not when it is not loaded.
- Lazy load library types cannot be used when importing files. If you need to use types, consider moving the interface type into another library and having both libraries import it separately.
- Dart implicitly will
loadLibrary()
Import into useDeferred as * Namespace *
In the class.loadLibrary()
The function returns aFuture.
Package Management Suggestions
If we implement our own code base, to improve performance, we should put the code in the /lib/src directory, and then export the SRC API in the /lib directory to expose the API in the lib/ SRC directory.
asynchronous
Future
Futures, like JavaScript promises, use async and await to make code asynchronous. We must use await in asynchronous functions with the async keyword:
Future<void> checkVersion() async {
var version = await lookUpVersion();
// Do something with version
}
Copy the code
The code above waits until lookUpVersion processing is complete before taking the next step.
The return value of an await expression is usually a Future object; If not, it will automatically wrap it in a Future object. The Future object represents a “promise” and await expression blocks until the desired object returns.
Async keyword
Just like JavaScript rules, using async alone can only generate Future objects and does not make code asynchronous. An 🌰
Future<void> checkVersion() async {
print(123);
}
checkVersion();
print(456);
/ / 123
/ / 456
Copy the code
The above code does not make checkVersion asynchronous because 123 is printed before 456.
Block code with await to make it truly asynchronous code.
Future<int> getVersion() async= >123;
Future<void> checkVersion() async {
print(0); // Here is the synchronization code again
var res = await getVersion(); // This becomes asynchronous
print(res);
}
checkVersion();
print(456);
Copy the code
The result of the above is
0, 456, 123Copy the code
Exception handling
Use try, catch, and finally to handle exceptions caused by using await:
try {
version = await lookUpVersion();
} catch (e) {
// React to inability to look up the version
}
Copy the code
Typedefs
Typedefs is a type alias, a handy way to refer to a type, often used to encapsulate a type, using the typedef keyword.
For example, if the project has a List of type numeric, we can wrap it into a type alias and use it directly
typedef IntList = List<int>;
IntList a = [1.2.3];
Copy the code
When the pass parameter is a function and an explicit type definition is required, using a type alias simplifies code
void PrintString(String getS(String str)) {
print(getString('name'));
}
Copy the code
The PrintString function above requires passing in a function that returns both a String value and an argument. Using a typedef simplifies the code:
typedef GetString = String Function(String str);
void PrintString(GetString getS) {
print(getString('name'));
}
Copy the code
The last
Promote my long-maintained Github blog
1. From learning to summary, record important knowledge points of the front-end, including in-depth Javascript, HTTP protocol, data structure and algorithm, browser principles, ES6, front-end skills, etc.
2. There is a directory in the README to find relevant knowledge points.
If it is helpful to you, welcome star and follow.
🌹✿ ° °) Blue ✿