Introduction to Dart language simplification
1. Introduction
-
Dart should be designed with reference to both Java and JavaScript and kotlin
-
object-oriented
-
JIT & AOT: JIT (Just in Time) advantages: instant compilation, faster compilation during development and faster overloading; Disadvantages: compiling the code into machine code at runtime gives users the most direct feeling that it is slow; AOT (Ahead Of Time) is compiled in advance. It is faster and smoother during release. Typical examples are C and C + +. It can directly compile machine code, that is, binary code, so its loading and execution speed is very fast. You will find that there will be some jams when installing Flutter during development, but it will get better after release. This mode enables the APP to run faster after release.
2. Basic data type
-
Numeric types (num, int, double)
1),num:
The parent class of the Dart numeric type, which accepts both floating-point and integer types
num num1 = -1.0; //num is the parent of numeric type num num2 = 2; //num is the parent of numeric type
2),int,double
num has two subclasses, one is int and the other is double. Int type can only receive integers, and double type is double precision
int int1 = 3; //Subclass an int type can only receive integers double d1 = 2.22; //Subclass two double precision
-
String (String)
In dart, you can define strings in single quotation marks or double quotation marks. One line can define one string or multiple strings, separated by commas:
String str1 = 'character string', str2 = "Double quoted string"; //Definition of string
How to splice strings in dart? Of course, there is another method, which is also used in the explanation of number types above. We can use "$" plus variable name to refer to a variable, which can be a number type or a string type. This is a bit like kotlin language
String str3 = 'str1:$str1 str2:$str2'; //String splicing String str4 = 'str1:' + str1 + "str2:" + str2; //String splicing String s = "this is dart2"; String s2 = 'this is dart2'; // Use = = to determine whether the strings are equal print(s == s2); // true
The string method is similar to the string method in java
-
Boolean
Dart is a strong bool type check. It is considered true only if the value of bool type is true
-
List of sets
The list keyword is used to define the collection in Dart. List indicates that the collection is a generic type, and any data type can be passed in here. List set can be initialized with "[]", for example: List = [1,2, 'test']; In this form, no generic type is used here, so when adding elements in brackets, you can pass in either numbers or strings. If you need to specify the generic type, for example: List list1 = []; In this form, only int type data can be added when adding elements.
List list = [1, 2, 3, 'aggregate']; //Initialize add element List list3 = []; list3.add('list3'); //Add elements through the add method list3.addAll(list); print(list3);
Traversal of List collection
The first: for loop without language:
for (int i = 0; i < list.length; i++) { print(list[i]); }
The second is for... In... Which is also for, but we make some modifications inside the expression. We use the var keyword to define a variable, and then use the in keyword to traverse the collection:
for (var o in list) { print(o); }
The third is the forEach() loop, which applies the function to each element of the collection. The elements passed in parentheses are the elements of the collection:
list.forEach((val) { print(val); });
-
Set Map
Initialization of Map collection
Map is an object that associates key and value. Both key and value can be objects of any type, and the key is unique. If the key is repeated, the key added later will replace the previous content.
///Map initialization Map names = {'xiaoming': 'Xiao Ming', "xiaohong": "Xiao Hong"}; print(names); Map ages = {}; ages['xiaoming'] = 12; ages['xiaohong'] = 18; print(ages);
Traversal of Map
It needs a callback function, which will output our key and value
///Map traversal method ages.forEach((k, v) { print('$k,$v'); });
map() method, we can call the map() method of the map set to traverse and generate a new map set. This method receives a callback function with input parameters of key and value. The function must accept a return, that is, it will return a new map. Here, the elements of each item in the previous map are returned through MapEntry. Here, I reverse the key and value of the original map, Then you can get an opposite set
Map ages2 = ages.map((k, v) { return MapEntry(v, k); }); print(ages2);
Third: for loop
First, loop through all the keys in the map set. You can use the map Keys, which returns an array of all key values (also map.values). Then we can get each element in the set through map[key]. The code is as follows:
for (var key in ages.keys) { print('$key , ${ages[key]}'); }
3. Variable declaration
-
var
Similar to var in JavaScript, it can receive any type of variable, but the biggest difference is that once the var variable in Dart is assigned, the type will be determined, and its type cannot be changed, such as:
var t = "hi world"; // The following code will report an error in dart because the type of variable t has been determined as String, // Once the type is determined, its type cannot be changed. t = 1000;
-
dynamic and Object
Object is the root base class of all objects in Dart, that is, all types in Dart are subclasses of object (including Function and Null), so any type of data can be assigned to the object declared by object. Variables declared by dynamic and object can be assigned to any object, and the type of assignment can be changed later, which is different from var, such as:
dynamic t; Object x; t = "hi world"; x = 'Hello Object'; //There is no problem with the following code t = 1000; x = 100
The difference between dynamic and Object is that the Object compiler declared by dynamic will provide all possible combinations, while the Object declared by Object can only use the properties and methods of Object, otherwise the compiler will report an error
dynamic a; Object b = ""; main() { a = ""; printLengths(); } printLengths() { // normal print(a.length); // The getter 'length' is not defined for the class' object ' print(b.length); }
4. Constant and null security
-
final and const
If you never intend to change a variable, use final or const, not var or a type. A final variable can only be set once. The difference between the two is that const variable is a compile time constant (directly replaced with constant value at compile time), and the final variable is initialized when it is used for the first time. For variables modified by final or const, the variable type can be omitted
//You can omit the type declaration String final str = "hi world"; //final String str = "hi world"; const str1 = "hi world"; //const String str1 = "hi world";
-
Null safety
Everything in Dart is an object, which means that if we define a number, if we use it before initializing it, if there is no checking mechanism, there will be no error, such as:
test() { int i; print(i*8); }
Before Dart introduces null security, the above code will not report an error before execution, but will trigger a runtime error because the value of i is null. But now with null security, we can specify whether the variable can be null or non null when defining the variable.
int i = 8; //The default value is not null and must be initialized at definition time. int? j; // It is defined as a nullable type. For nullable variables, we must judge them as null before using them. // If we expect that the variable cannot be empty, but its initial value cannot be determined during definition, we can add the late keyword, // Indicates that it will be initialized later, but it must be initialized before it is officially used, otherwise an error will be reported late int k; k=9;
If a variable is defined as a nullable type, in some cases, even if we assign a value to it, the preprocessor may still not recognize it. At this time, we need to explicitly (by adding a "!" symbol after the variable) tell the preprocessor that it is not null, for example:
class Test{ int? i; Function? fun; say(){ if(i!=null) { print(i! * 8); //Because it has been judged to be empty, i must not be null if it can go here. If there is no explicit declaration, the IDE will report an error } if(fun!=null){ fun!(); // ditto } } }
In the above, if the function variable can be null, the syntax can be used when calling:
fun?.call() // Called when fun is not empty
This is a little similar to kotlin
5. Functions
Dart is a true object-oriented language, so even functions are objects and have a type Function. This means that functions can be assigned to variables or passed as parameters to other functions, which is a typical feature of functional programming.
-
Function declaration
bool isNoble(int atomicNumber) { return _nobleGases[atomicNumber] != null; }
The function declaration here is a little similar to java or javascript
For functions that contain only one expression, you can use the shorthand syntax:
bool isNoble (int atomicNumber)=> true ;
-
Function as variable
var say = (str){ print(str); }; say("hi world");
-
Functions are passed as arguments
void execute(var callback) { callback(); } execute(() => print("xxx"))
-
Optional location parameters
String say(String from, String msg, [String device]) { var result = '$from says $msg'; if (device != null) { result = '$result with a $device'; } return result; } say('Bob', 'Howdy'); //The result is: Bob says Howdy say('Bob', 'Howdy', 'smoke signal'); //The result is: Bob says how with a smoke signal
-
Optional named parameters
When defining a function, {param1, param2,...} is used at the end of the parameter list to specify named parameters. For example:
//Set the [bold] and [hidden] flags void enableFlags({bool bold, bool hidden}) { // ... }
When you call a function, you can use the specified named parameters. For example: paramName: value
enableFlags(bold: true, hidden: false);
Optional named parameters are widely used in fluent. Note that you cannot use both optional positional parameters and optional named parameters.
6. Classes and inheritance
Inheritance in Dart:
1. The subclass uses the extends keyword to inherit the parent class
2. The subclass will inherit the properties and methods visible in the parent class, but will not inherit the constructor
3. The subclass can duplicate the method getter and setter of the parent class
4.Dart does not support multi inheritance. Classes and interfaces are unified, and classes are interfaces.
Generally, abstract classes are used as interfaces
void main() { new Student().run(); } abstract class Person { void run(); } class Student implements Person { @override void run() { print("...."); } }
7. Asynchronous Support
The Dart class library has many functions that return Future or Stream objects. These functions are called asynchronous functions: they only return after setting up some time-consuming operations, such as IO operations. Instead of waiting for the operation to complete.
-
Future
Future is very similar to Promise in JavaScript, which represents the final completion (or failure) of an asynchronous operation and the representation of its result value. In short, it is used to handle asynchronous operations. If asynchronous processing succeeds, successful operations will be executed. If asynchronous processing fails, errors will be captured or subsequent operations will be stopped. A future will only correspond to one result, either success or failure.
Due to its many functions, here we only introduce its common APIs and features. Also, remember that the return value of all Future APIs is still a Future object, so it is convenient to make chain calls
1),Future.then
To facilitate the example, in this case, we use future Delayed creates a delayed task (the actual scenario will be a real time-consuming task, such as a network request), that is, the result string "hi world!" will be returned after 2 seconds, Then we receive asynchronous results in then and print the results. The code is as follows:
Future.delayed(Duration(seconds: 2),(){ return "hi world!"; }).then((data){ print(data); });
2),Future.catchError
Future.delayed(Duration(seconds: 2),(){ //return "hi world!"; throw AssertionError("Error"); }).then((data){ //Successful implementation will come here print("success"); }).catchError((e){ //Execution failure will come here print(e); });
3),Future.whenComplete
Sometimes, we will encounter scenarios where we need to do something regardless of the success or failure of asynchronous task execution, such as pop-up the load dialog box before the network request and close the dialog box after the request. In this scenario, there are two methods. The first is to close the dialog box in then or catch respectively. The second is to use the Future whenComplete callback. Let's change the above example:
Future.delayed(Duration(seconds: 2),(){ //return "hi world!"; throw AssertionError("Error"); }).then((data){ //Successful implementation will come here print(data); }).catchError((e){ //Execution failure will come here print(e); }).whenComplete((){ //Success or failure will come here });
4),Future.wait
Sometimes, we need to wait for the execution of multiple asynchronous tasks before performing some operations. For example, we have an interface. We need to obtain data from two network interfaces respectively. After successful acquisition, we need to process the data of the two interfaces and then display it on the UI interface. What should we do? The answer is Future Wait, which accepts a Future array parameter. The successful callback of then will be triggered only after all Future in the array are successfully executed. As long as one Future fails to execute, the error callback will be triggered. Next, we simulate Future Delayed to simulate two asynchronous tasks of data acquisition. When the two asynchronous tasks are successfully executed, the results of the two asynchronous tasks will be spliced and printed. The code is as follows:
Future.wait([ // Return results in 2 seconds Future.delayed(Duration(seconds: 2), () { return "hello"; }), // Return results in 4 seconds Future.delayed(Duration(seconds: 4), () { return " world"; }) ]).then((results){ print(results[0]+results[1]); }).catchError((e){ print(e); });
Execute the above code and you will see "hello world" in the console in 4 seconds.
5),Async/await
If there is a large amount of asynchronous logic in the code and a large number of asynchronous tasks depend on other asynchronous tasks, future is bound to appear Set callback in then callback. For example, there is a demand scenario where the user logs in first, and after successful login, the user ID will be obtained, and then the user ID will be used to request the user's personal information. After obtaining the user's personal information, we need to cache it in the local file system for convenience. The code is as follows:
//First, define each asynchronous task separately Future<String> login(String userName, String pwd){ ... //User login }; Future<String> getUserInfo(String id){ ... //Get user information }; Future saveUserInfo(String userInfo){ ... // Save user information };
Next, execute the entire task flow:
login("alice","******").then((id){ //After successful login, obtain user information through id getUserInfo(id).then((userInfo){ //Save after obtaining user information saveUserInfo(userInfo).then((){ //Save the user information, and then perform other operations ... }); }); })
Eliminate nesting
login("alice","******").then((id){ return getUserInfo(id); }).then((userInfo){ return saveUserInfo(userInfo); }).then((e){ //Perform the next operation }).catchError((e){ //error handling print(e); });
Using async/await to eliminate nesting
task() async { try{ String id = await login("alice","******"); String userInfo = await getUserInfo(id); await saveUserInfo(userInfo); //Perform the next operation } catch(e){ //error handling print(e); } }
-
async is used to indicate that the function is asynchronous. The defined function will return a Future object. You can use the then method to add a callback function.
-
await is followed by a Future, which means wait for the asynchronous task to complete, and then go down; await must appear inside the async function.
-
-
Stream
Stream is also used to receive asynchronous event data. Unlike Future, it can receive the results of multiple asynchronous operations (success or failure). That is, when executing asynchronous tasks, you can trigger success or failure events multiple times to pass result data or error exceptions. Stream is often used in asynchronous task scenarios that can read data multiple times, such as network content download, file reading and writing, etc. for instance:
Stream.fromFutures([ // Return results in 1 second Future.delayed(Duration(seconds: 1), () { return "hello 1"; }), // Throw an exception Future.delayed(Duration(seconds: 2),(){ throw AssertionError("Error"); }), // Return results in 3 seconds Future.delayed(Duration(seconds: 3), () { return "hello 3"; }) ]).listen((data){ print(data); }, onError: (e){ print(e.message); },onDone: (){ });
The above code will output in sequence:
I/flutter (17666): hello 1 I/flutter (17666): Error I/flutter (17666): hello 3