flutter seems to be hot, right. First, get familiar with Dart's grammar, which is similar to most object-oriented languages, and it's easy to get started.
Flutter Learning Guide: Familiar with Dart Language
variable
Basic types
bool done = true; int num = 2; double x = 3.14; final bool visible = false; final int amount = 100; final double y = 2.7; const bool debug = true; const int sum = 42; const double z = 1.2;
Unlike other commonly used languages, Dart has no byte, char, float, and both int and double are 64 bits. Final, like final in Java, represents a runtime constant (assigning values while the program is running, and the values do not change after assignment). const represents a compile-time constant whose value is determined at compile time.
If you find it too cumbersome to write variable types every time, you should like Dart's type inference function:
var done = true; var num = 2; var x = 3.14; final visible = false; final amount = 100; final y = 2.7; const debug = true; const sum = 42; const z = 1.2; Dart Everything in it is an object, including int,Function. String var str = ' foo'; var str2 = str.toUpperCase(); var str3 = str.trim(); assert(str == str2); assert(!identical(str, str2));
String in Dart is the same as in Java, it is immutable object; the difference is, to detect whether the contents of two Strings are the same thing, we use== to compare; if we want to test whether two objects are the same object (indentity test), we use the identical function.
List, Map and Set
List
// Create objects using constructors // Same as var list = new List < int >(); var list = List<int>(); list.add(1); list.add(2); // By creating objects literally, generic parameters of list s can be inferred from variable definitions. // It is recommended to create objects in literal form var list2 = [1, 2]; // Without elements, explicitly specify the generic parameter as int var list3 = <int>[]; list3.add(1); list3.add(2); var list4 = const[1, 2]; // list4 points to a constant, and we can't add elements to it (we can't modify it). list4.add(3); // error // list4 itself is not a constant, so it can point to another object list4 = [4, 5]; // it's fine const list5 = [1, 2]; // Equivalent to const list 5 = const [1, 2]; list5.add(3); // error // Dart also provides for-in loops. // Because voice design takes this requirement into account, in is a key word in Dart. var list6 = [1, 3, 5, 7]; for (var e in list6) { print(e); }
In Dart 2, you can omit the new keyword and recommend omitting the new keyword when creating an object.
Set
var set = Set<String>(); set.add('foo'); set.add('bar'); assert(set.contains('foo'));
We can only create instances through the constructor of Set.
Map
var map = Map<String, int>(); // Add to map['foo'] = 1; map['bar'] = 3; // modify map['foo'] = 4; // When the corresponding key does not exist, return null if (map['foobar'] == null) { print('map does not contain foobar'); } var map2 = const { 'foo': 2, 'bar': 4, }; var map3 = <String, String>{};
dynamic and Object
As we said earlier, everything in Dart is an object. The parent class of all these objects is Object.
Object o = 'string'; o = 42; o.toString(); // We can only call methods supported by Object dynamic obj = 'string'; obj['foo'] = 4; // It can be compiled, but it throws NoSuchMethodError at runtime
Object and dynamic s both enable us to receive arbitrary types of parameters, but the difference between them is very large.
When using Object, we're just saying accept any type. What we need is an Object. The type system guarantees its type safety.
Using dynamic s tells the compiler that we know what we're doing without type checking. When we call a method that does not exist, we execute the noSuchMethod() method, which by default (implemented in Object) throws a NoSuchMethodError.
Dart provides a keyword is:
dynamic obj = <String, int>{}; if (obj is Map<String, int>) { // After type judgment, Dart knows that obj is a Map < String, int>. // So there's no need to cast obj's type here, even if we declare obj as Object. obj['foo'] = 42; } // Although Dart also provides as to enable us to do type mandatory conversions, it's safer to come in // Conversion, more recommend using is var map = obj as Map<String, int>;
Sentence
var success = true; if (success) { print('done'); } else { print('fail'); } for (var i = 0; i < 5; ++i) { print(i); } var sum = 0; var j = 1; do { sum += j; ++j; } while (j < 5); while (sum-- > 0) { print(sum); } var type = 1; switch (type) { case 0: // ... break; case 1: // .. break; case 2: // ... break; default: // ... break; }
Common if/else, do while, while, and switch are supported in Dart. Switch also supports String and enum. The most common function of a function looks the same as in Java:
int foo(int x) { return 0; } Dart Optional parameters are also supported: void main() { print(foo(2)); print(foo(1, 2)); } int foo(int x, [int y]) { // Yes, int can also be null. if (y != null) { return x + y; } return x; } // Result: // 2 // 3
Default parameters are also supported:
int foo(int x, [int y = 0]) { return x + y; }
You can also use named parameters:
void main() { print(foo(x: 1, y: 2)); // The order of named parameters can be arbitrary. print(foo(y: 3, x: 4)); // All named parameters are optional, and this call is legal, but it causes foo() to throw exceptions at runtime print(foo()); } int foo({int x, int y}) { return x + y; } //Named parameters can also have default parameters: void main() { print(foo(x: 1, y: 2)); print(foo()); } int foo({int x = 0, int y = 0}) { return x + y; }
If you want to tell the user that a named parameter is necessary, you can use the annotation @required:
int foo({@required int x, @required int y}) { return x + y; }
required: The API provided in the meta package
Functions can also be defined internally:
// Typeedef is an alias used in Dart to define function types typedef Adder = int Function(int, int); Adder makeAdder(int extra) { int adder(int x, int y) { return x + y + extra; } return adder; } void main() { var adder = makeAdder(2); print(adder(1, 2)); } // Result: // 5
For simple functions like the one above, we can also use lambda:
typedef Adder = int Function(int, int); Adder makeAdder(int extra) { return (int x, int y) { return x + y + extra; }; // If there is only one statement, we can use the following more concise form // return (int x, int y) => x + y + extra; } void main() { var adder = makeAdder(2); print(adder(1, 2)); }
In Dart, not only variables support type inference, but also lambda parameters support automatic inference. The above code can be further simplified as follows:
typedef Adder = int Function(int, int); Adder makeAdder(int extra) { // The type we want to return is Adder, so Dart knows that X and y are all int. return (x, y) => x + y + extra; } void main() { var adder = makeAdder(2); print(adder(1, 2)); }
One drawback is that Dart does not support overloading functions.
abnormal
Throw an exception:
throw Exception('put your error message here');
Capture exceptions:
try { // ... // Capture specific types of exceptions } on FormatException catch (e) { // ... // Catch specific types of exceptions, but do not need this object } on Exception { // .. // Capture all exceptions } catch (e) { // ... } finally { // ... }
Unlike Java, Dart can throw any type of object:
class
Define a class:
class Point2D { static const someConst = 2; int x; // Membership variables can also be final final int y; Point2D(int x, int y) { this.x = x; this.y = y; } }
Because this initialization is common, Dart provides a more concise way:
class point2d { int x; int y; point2d(this.x, this.y); } //In addition, the initializer list can be used to initialize the object: class Point2D { int x; int y; // Because it's in the initializer list, Dart knows that the first X is this.x. // The second x is the parameter of the constructor Point2D(int x, int y) : x = x, y = y { // ... } }
The initializer list executes before the body of the constructor runs.
Dart has garbage collection capabilities, and the use of objects is almost the same as in Java:
main() { var point = Point2D(1, 2); point.x = 4; print(point); } class Point2D { int x; int y; Point2D(this.x, this.y); // All classes inherit from Object, and toString() is the method in Object. @override String toString() { // Inserting a value inside a string can be done by ${expression} if // expression is a variable that can be omitted from curly brackets return "Point2D{x=$x, y=$y}"; } } // Result: // Point2D{x=4, y=2}
Dart uses the concept of package to manage source code and visibility. It does not have access control characters such as public and private, and by default, all symbols are public. If we don't want a variable to be visible to the outside of the package, we can use the underscore to name the variable.
class _Foo { // ... } class Bar { int _x; }
Next, we use Dart's access control to implement a Point with offset:
class OffsetPoint { int _x; int _y; int offset; OffsetPoint(int x, int y, int offset) : _x = x, _y = y, offset = offset {} // Define a getter int get x => _x + offset; // getter cannot have parameters, even parentheses are omitted int get y { return _y + offset; } // Define setter void set x (int x) => _x = x; void set y (int y) => _y = y; @override String toString() { return "OffsetPoint{x=$x, y=$y}"; } } main() { var point = OffsetPoint(1, 2, 10); // When using getter/setter, it's like a normal member variable print(point.x) print(point); point.x = 4; print(point); } // Result: // 11 // OffsetPoint{x=11, y=12} // OffsetPoint{x=14, y=12}
Inheritance in Dart is also simple:
class Point2D { int x; int y; Point2D(this.x, this.y); } class Point3D extends Point2D { int z; // The constructor of the parent class can only be called in the initializer list Point3D(int x, int y, int z): z = z, super(x, y) { } }
But object construction is different from Java and C++:
- The subclass initializer list is executed first, but only its member variables are initialized
- Initialize the member variables of the parent class to initialize the member variables of the parent class
- Function Body Executing Parent Constructor Function Body Executing Parent Constructor
- Function Body of Constructor such as Execution Function Body of Constructor such as Execution Function Body of Constructor
Based on this initialization order, it is recommended that super() be placed at the end of the initializer list. In addition, you cannot access this in the initializer list (that is, you can only call static methods).
Although Dart is a single inheritance, it also provides a certain degree of multiple inheritance support:
abstract class Bark { void bark() { print('woof'); } } class Point3D extends Point2D with Bark { int z; // The constructor of the parent class can only be called in the initializer list Point3D(int x, int y, int z): z = z, super(x, y) { } } // There are no other classes to inherit, so extend Bark directly class Foo extends Bark {} void main() { var p = Point3D(1, 2, 3); p.bark(); }
Dart calls classes that support multiple inheritance mixin s
generic paradigm
class Pair<S, T> { S first; T second; Pair(this.first, this.second); } void main() { var p = Pair('hello', 2); print(p is Pair<String, int>); // Is! Also Dart's operator, the following statement is the same as! (p is Pair < int, int >). // But is! It reads like English. print(p is! Pair<int, int>); print(p is Pair); } // Result: // true // true // true
Unlike Java, Dart's generic parameter types are preserved at run time.
Future
Dart is single-threaded, and the main thread is executed by an event loop (similar to Android's main thread). For asynchronous code, we use Future to get the results:
import 'dart:io'; void foo() { var file = File('path-to-your-file'); file.exists() .then((exists) => print('file ${exists ? 'exists' : 'not exists'}')) .catchError((e) => print(e)); } Dart 2 Provided async Function to simplify this programming paradigm. The effect of the following code is the same as that of the above code: void foo() async { var file = File('path-to-your-file'); try { var exists = await file.exists(); print('file ${exists ? 'exists' : 'not exists'}'); } catch (e) { print(e); } }
Note, however, that the above two pieces of code are not exactly the same:
// The import statement is used to import a package import 'dart:io'; void main() { foo(); bar(); } void bar() { var file = File('path-to-your-file'); file.exists() .then((exists) => print('bar: file ${exists ? 'exists' : 'not exists'}')) .catchError((e) => print(e)); print('bar: after file.exists() returned'); } void foo() async { var file = File('path-to-your-file'); try { var exists = await file.exists(); print('bar: file ${exists ? 'exists' : 'not exists'}'); print('bar: after file.exists() returned'); } catch (e) { print(e); } } // One possible result: // bar: after file.exists() returned // foo: file not exists // foo: after file.exists() returned // bar: file not exists
The key here is that in the bar function, when file.exists() is executed, the following statement will be executed immediately, while foo will wait for the result before continuing to execute.