Preface 1: I will update some Flutter text tutorials in the next few days.
Update progress: at least two articles per week;
Renewal Place: First launched in the public number, the next day updated in the digging, think about whether or not and other places;
More Exchanges: You can add my Weibo 372623326, pay attention to my Weibo: coderwhy
I hope you can help me forward, click and see, give me more creative power.
Operators
Here, I only list operators that are special to other languages, because some operators are too simple to waste time, such as +, -, +=, ===.
You may wonder why Dart has come up with so many special operators.
You have to be convinced that all these special operators are designed to make our development easier, not to make our coding more complex.
1.1. Dividing, dividing and modular operations
Let's look at divisions, divisions, modular operations.
var num = 7; print(num / 3); // Dividing operation, result 2.3333.. print(num ~/ 3); // Dividing operation, result 2; print(num % 3); // Modeling operation, result 1;
1.2.?= assignment operation
dart has an assignment operator that many languages do not have:
- When the variable is null, assign it using the following content.
- When a variable has a value, use its original value.
main(List<String> args) { var name1 = 'coderwhy'; print(name1); // var name2 = 'kobe'; var name2 = null; name2 ??= 'james'; print(name2); // When name2 is initialized to kobe, the result is kobe. When name2 is initialized to null, james is assigned. }
1.3. Conditional operators:
Dart contains a special conditional operator: expr1?? expr2
- If expr1 is null, the result of expr2 is returned.
- If expr1 is not null, use expr1 directly.
var temp = 'why'; var temp = null; var name = temp ?? 'kobe'; print(name);
1.4. Cascading grammar:..
- Sometimes, we want to operate continuously on an object. Cascading grammar can be used at this time.
class Person { String name; void run() { print("${name} is running"); } void eat() { print("${name} is eating"); } void swim() { print("${name} is swimming"); } } main(List<String> args) { final p1 = Person(); p1.name = 'why'; p1.run(); p1.eat(); p1.swim(); final p2 = Person() ..name = "why" ..run() ..eat() ..swim(); }
II. Process Control
Similar to the characteristics of most languages, I will not go into details here, just take a look.
2.1. if and else
Like other language uses
Here's one thing to note: you don't support non-empty real or non-zero real, you have to have a clear bool type
- Let's look at the judgment that the name is null
2.2. Cyclic operation
Basic for loop
for (var i = 0; i < 5; i++) { print(i); }
for in traversal List and Set types
var names = ['why', 'kobe', 'curry']; for (var name in names) { print(name); }
while and do-while are consistent with other languages
break and continue are also used in the same way
2.3. switch-case
Common switch usage
- Note: Each case statement must end with a break by default
main(List<String> args) { var direction = 'east'; switch (direction) { case 'east': print('East'); break; case 'south': print('South'); break; case 'west': print('West'); break; case 'north': print('North'); break; default: print('Other directions'); } }
Classes and Objects
Dart is an object-oriented language. A very important concept in object-oriented is class, which produces objects.
In this section, we will specifically study classes and objects, but Dart has many features that other languages do not have, so I will spend a long time here to explain.
3.1. Class Definition
In Dart, class keywords are used to define classes.
Classes usually consist of two parts: member s and method s.
The pseudocode for defining classes is as follows:
Class class name{ Type member name; Return value type method name (parameter list){ Method body } }
Write a simple Person class:
- Here's a note: when we use attributes (member / instance variables) in our method, we don't add this;
- In Dart's development style, this is omitted when attributes are commonly used in methods, but this cannot be omitted when naming conflicts occur.
class Person { String name; eat() { print('$name Eating'); } }
Let's use this class to create the corresponding object:
- Note: Starting with Dart2, the new keyword can be omitted.
main(List<String> args) { // 1. Objects that create classes var p = new Person(); // Person() can also be created directly // 2. Assigning Attributes to Objects p.name = 'why'; // 3. Method of calling object p.eat(); }
3.2. Construction Method
3.2.1. General Construction Method
We know that when an object is created through a class, the constructor of that class is called.
- When a constructor is not explicitly specified in a class, it defaults to having an argument-free constructor.
- In the previous Person, we were calling this constructor.
We can also define our own construction methods according to our own needs:
-
Note 1: When you have your own method of construction, the default method of construction will fail and cannot be used.
- Of course, you may want to explicitly write a default constructor, but it will conflict with our custom constructor.
- This is because Dart itself does not support overloading of functions (the same name, different parameters).
- Note 2: I also implemented the toString method here.
class Person { String name; int age; Person(String name, int age) { this.name = name; this.age = age; } @override String toString() { return 'name=$name age=$age'; } }
In addition, in the implementation of construction methods, the usual thing to do is to assign attributes by parameters.
To simplify this process, Dart provides a more concise form of grammatical sugar.
The above construction method can be optimized to the following:
Person(String name, int age) { this.name = name; this.age = age; } // Equate to Person(this.name, this.age);
3.2.2. Naming construction method
But in development, we really want to implement more construction methods. What should we do?
- Because overloading of methods (functions) is not supported, we cannot create constructors with the same name.
We need to use the naming constructor:
class Person { String name; int age; Person() { name = ''; age = 0; } // Naming construction method Person.withArgments(String name, int age) { this.name = name; this.age = age; } @override String toString() { return 'name=$name age=$age'; } } // create object var p1 = new Person(); print(p1); var p2 = new Person.withArgments('why', 18); print(p2);
In the later development, we can also use the naming construction method to provide a more convenient way to create objects:
- For example, in development, we often need to convert a Map into an object, which can provide the following construction methods
// New Construction Method Person.fromMap(Map<String, Object> map) { this.name = map['name']; this.age = map['age']; } // Create objects from the above constructor var p3 = new Person.fromMap({'name': 'kobe', 'age': 30}); print(p3);
3.2.3. Initialization list
Let's redefine a class Point, pass in x/y, and get their distance:
class Point { final num x; final num y; final num distance; // Wrong writing // Point(this.x, this.y) { // distance = sqrt(x * x + y * y); // } // Correct Writing Point(this.x, this.y) : distance = sqrt(x * x + y * y); }
The above method of initializing variables is called Initializer list.
3.2.4. Redirectional Construction Method
In some cases, we want to call another constructor in one constructor, at which point we can use a redirected constructor:
- In one constructor, call another constructor (note: this is called after the colon)
class Person { String name; int age; Person(this.name, this.age); Person.fromName(String name) : this(name, 0); }
3.2.5. Constant Construction Method
In some cases, when we pass in the same value, we want to return the same object, at which point we can use a constant constructor.
By default, when an object is created, even if the same parameters are passed in, it is not created the same object. Look at the following code:
- Here we use the identical (object 1, object 2) function to determine whether two objects are the same object:
main(List<String> args) { var p1 = Person('why'); var p2 = Person('why'); print(identical(p1, p2)); // false } class Person { String name; Person(this.name); }
However, if the construction method is modified with const, the same parameter can be guaranteed and the created objects are the same.
- Such a construction method is called a constant construction method.
main(List<String> args) { var p1 = const Person('why'); var p2 = const Person('why'); print(identical(p1, p2)); // true } class Person { final String name; const Person(this.name); }
Constant construction method has some points of attention:
- Note 1: In classes with constant constructors, all member variables must be final modified.
-
Note 2: In order to create the same object through constant construction, const keyword is used instead of new keyword.
- Const can be omitted if the result is assigned to the const-modified identifier.
3.2.6. Factory Construction Method
Dart provides factory keywords to retrieve objects through factories
main(List<String> args) { var p1 = Person('why'); var p2 = Person('why'); print(identical(p1, p2)); // true } class Person { String name; static final Map<String, Person> _cache = <String, Person>{}; factory Person(String name) { if (_cache.containsKey(name)) { return _cache[name]; } else { final p = Person._internal(name); _cache[name] = p; return p; } } Person._internal(this.name); }
3.3. setter and getter
By default, attributes defined by classes in Dart can be accessed directly by the outside world.
But in some cases, we want to monitor the process of accessing attributes of this class, at which point we can use setter and getter.
main(List<String> args) { final d = Dog("yellow"); d.setColor = "black"; print(d.getColor); } class Dog { String color; String get getColor { return color; } set setColor(String color) { this.color = color; } Dog(this.color); }
3.4. Class Inheritance
One of the major features of object-oriented is inheritance, which not only reduces the amount of code, but also is the premise of polymorphism.
Inheritance in Dart uses the extends keyword, and subclasses use super to access the parent class.
All member variables and methods in the parent class are inherited, except for constructors.
main(List<String> args) { var p = new Person(); p.age = 18; p.run(); print(p.age); } class Animal { int age; run() { print('Running ing'); } } class Person extends Animal { }
Subclasses can have their own member variables and can override the methods of the parent class:
class Person extends Animal { String name; @override run() { print('$name Running ing'); } }
The construction method of the parent class can be invoked in the subclass to initialize some attributes:
- Before the construction method of the subclass is executed, it implicitly calls the default construction method of the parent class, which has no parameters and has the same name as the construction method of the class.
- If the parent class does not have a default constructor without parameters, the constructor of the subclass must explicitly invoke a constructor of the parent class through super in the initialization list.
class Animal { int age; Animal(this.age); run() { print('Running ing'); } } class Person extends Animal { String name; Person(String name, int age) : name=name, super(age); @override run() { print('$name Running ing'); } @override String toString() { return 'name=$name, age=$age'; } }
3.5. abstract classes
We know that inheritance is the premise of polymorphism.
So when defining many common invocation interfaces, we usually let the caller pass in the parent class to achieve more flexible invocation through polymorphism.
However, the parent class itself may not need to implement specific methods, so the methods defined in the parent class, we can define as abstract methods.
What is abstract method? There is no specific method (no method body) in Dart, which is abstract method.
- Abstract methods must exist in abstract classes.
- Abstract classes are classes that use abstract declarations.
In the following code, the Shape class is an abstract class that contains an abstract method.
abstract class Shape { getArea(); } class Circle extends Shape { double r; Circle(this.r); @override getArea() { return r * r * 3.14; } } class Reactangle extends Shape { double w; double h; Reactangle(this.w, this.h); @override getArea() { return w * h; } }
Matters needing attention:
- Note 1: abstract classes cannot be instantiated.
- Note 2: The abstract methods in abstract classes must be implemented by subclasses. The implemented methods in abstract classes can not be overridden by subclasses.
3.6. Implicit Interface
The interface in Dart is special, and there is no special keyword to declare the interface.
By default, each class defined corresponds to the default declaration of an interface that can be implemented by other classes (because Dart does not support multiple inheritance)
In development, we usually declare classes used to implement for others as abstract classes:
abstract class Runner { run(); } abstract class Flyer { fly(); } class SuperMan implements Runner, Flyer { @override run() { print('Superman Running'); } @override fly() { print('Superman flying'); } }
3.7. Mixin Mixing
When implementing a class through implements, all methods in the class must be reimplemented (regardless of whether the class has already implemented the method).
But in some cases, a class may want to reuse the original implementation of the previous class directly. What should be done?
- Do you use inheritance? But Dart only supports single inheritance, which means that you can only reuse the implementation of one class.
Dart offers another solution: Mixin mixing
- In addition to defining classes by class, you can also define a class by mixin keywords.
- Only classes defined by mixin are used for mixing with other classes, and with keywords for mixing.
main(List<String> args) { var superMan = SuperMain(); superMan.run(); superMan.fly(); } mixin Runner { run() { print('Running'); } } mixin Flyer { fly() { print('Flying'); } } // The way implements are implemented requires that one of them be reimplemented // class SuperMan implements Runner, Flyer {} class SuperMain with Runner, Flyer { }
3.8. Class members and methods
The members and methods we defined in the class above belong to the object level. In development, we sometimes need to define the members and methods at the class level.
In Dart, we use the static keyword to define:
main(List<String> args) { var stu = Student(); stu.name = 'why'; stu.sno = 110; stu.study(); Student.time = '8 in the morning.'; // stu.time = 9 a.m.; incorrect practice, instance objects cannot access class members Student.attendClass(); // stu.attendClass(); Implementing Object Filling Access Class Method } class Student { String name; int sno; static String time; study() { print('$name I'm learning'); } static attendClass() { print('Go to the class.'); } }
3.9. Enumeration Types
Enumeration is also very common in development, and enumeration is a special class, usually used to represent a fixed number of constants.
3.9.1. Definition of enumeration
Enumeration uses the enum keyword to define:
main(List<String> args) { print(Colors.red); } enum Colors { red, green, blue }
3.9.2. Enumeration attributes
There are two more common attributes in enumeration types:
- Index: The index used to represent each enumeration constant, starting at 0.
- values: List containing each enumerated value.
main(List<String> args) { print(Colors.red.index); print(Colors.green.index); print(Colors.blue.index); print(Colors.values); } enum Colors { red, green, blue }
Notes for enumeration types:
- Note 1: You cannot subclassify, mix, or implement enumerations.
- Note 2: An enumeration cannot be explicitly instantiated
Four. Generic
4.1. Why use generics?
For those who have a foundation, this part will not be explained.
4.2. Generics of List and Map
Generic Writing for List Use:
// How to create List var names1 = ['why', 'kobe', 'james', 111]; print(names1.runtimeType); // List<Object> // Restriction type var names2 = <String>['why', 'kobe', 'james', 111]; // Last error report List<String> names3 = ['why', 'kobe', 'james', 111]; // Last error report
Generic Writing for Map Use:
// How to Create Map s var infos1 = {1: 'one', 'name': 'why', 'age': 18}; print(infos1.runtimeType); // _InternalLinkedHashMap<Object, Object> // Display types Map<String, String> infos2 = {'name': 'why', 'age': 18}; // 18 cannot be placed in value var infos3 = <String, String>{'name': 'why', 'age': 18}; // 18 cannot be placed in value
4.3. Generics of Class Definitions
If we need to define a class to store location information Location, but we are not sure if the user wants to use an int type, a double type, or even a string, how do we define it?
- One solution is to use the Object type, but it is very inconvenient to use later.
- Another option is to use generics.
Definition of Location Class: Object Approach
main(List<String> args) { Location l1 = Location(10, 20); print(l1.x.runtimeType); // Object } class Location { Object x; Object y; Location(this.x, this.y); }
Definition of Location Class: Generic Approach
main(List<String> args) { Location l2 = Location<int>(10, 20); print(l2.x.runtimeType); // int Location l3 = Location<String>('aaa', 'bbb'); print(l3.x.runtimeType); // String } } class Location<T> { T x; T y; Location(this.x, this.y); }
What if we want the type to be num only?
main(List<String> args) { Location l2 = Location<int>(10, 20); print(l2.x.runtimeType); // Wrong writing, type must inherit from num Location l3 = Location<String>('aaa', 'bbb'); print(l3.x.runtimeType); } class Location<T extends num> { T x; T y; Location(this.x, this.y); }
4.4. Definition of generic methods
Initially, Dart only supported generics in classes. Later, a new grammar called generic methods allowed the use of type parameters in methods and functions.
main(List<String> args) { var names = ['why', 'kobe']; var first = getFirst(names); print('$first ${first.runtimeType}'); // why String } T getFirst<T>(List<T> ts) { return ts[0]; }
V. Use of the Library
In Dart, you can import a library to use the functionality it provides.
The use of libraries can improve the reusability of code and make better combination of code.
Any dart file in Dart is a library, even if you don't use the keyword library declaration
5.1. Import of Libraries
The import statement is used to import a library followed by a string Uri to specify the library to be referenced. The syntax is as follows:
import 'Repository uri';
Common library URI s come in three different forms
- From dart standard editions, such as dart:io, dart:html, dart:math, dart: core (but this can be omitted)
//Dart: The prefix denotes Dart's standard library, such as dart:io, dart:html, dart:math import 'dart:io';
- Libraries imported using relative paths, usually referring to other dart files defined in their own projects
//Of course, you can also use dart files for relative or absolute paths to refer to import 'lib/student/student.dart';
- Some libraries managed by Pub package management tools, including their own configuration and some third-party libraries, usually use prefix package
//There are many powerful and practical libraries in Pub package management system, which can use prefix package: import 'package:flutter/material.dart';
Display and Hide Contents in Library Files
You can use show and hide keywords if you want to import only some content from the library or hide some content from the library deliberately.
- show keyword: you can display a member (shield others)
- Hide keyword: you can hide a member (show others)
import 'lib/student/student.dart' show Student, Person; import 'lib/student/student.dart' hide Person;
Name conflict between content in Library and current file
When each library has a naming conflict, you can use the as keyword to use the namespace
import 'lib/student/student.dart' as Stu; Stu.Student s = new Stu.Student();
5.2. Definition of Libraries
library keyword
Usually when defining a library, we can use the library keyword to give the library a name.
But now I find that the name of the library does not affect the import, because the import statement uses the string URI.
library math;
part keyword
Previously, when we used student.dart as a walkthrough, we just used this file as a library.
In development, if a library file is too large, it is not reasonable to save all the contents to a folder. We may hope to split the library. At this time, we can use the part keyword.
Officials no longer recommend the use of this method:
mathUtils.dart file
part of "utils.dart"; int sum(int num1, int num2) { return num1 + num2; }
dateUtils.dart file
part of "utils.dart"; String dateFormat(DateTime date) { return "2020-12-12"; }
utils.dart file
part "mathUtils.dart"; part "dateUtils.dart";
test_libary.dart file
import "lib/utils.dart"; main(List<String> args) { print(sum(10, 20)); print(dateFormat(DateTime.now())); }
export keyword
Officials do not recommend using the part keyword, so if the library is very large, how to manage it?
- Use export keyword to import each dart file as a library file separately
mathUtils.dart file
int sum(int num1, int num2) { return num1 + num2; }
dateUtils.dart file
String dateFormat(DateTime date) { return "2020-12-12"; }
utils.dart file
library utils; export "mathUtils.dart"; export "dateUtils.dart";
test_libary.dart file
import "lib/utils.dart"; main(List<String> args) { print(sum(10, 20)); print(dateFormat(DateTime.now())); }
Finally, you can manage your own libraries through Pub, which is not very necessary in project development, so I will not explain this way for the time being.
Note: All content will be published on the public number. In addition to Flutter, other technical articles will be updated, such as TypeScript, React, Node, uniapp, mpvue, data structure and algorithm, etc. Some of your own learning experiences will also be updated. Welcome your attention.