Flutter is becoming increasingly popular, so learn the basics of dart, the development language it uses, before learning about Flutter. This article focuses on Flutter

  1. Sample basic code for Dart
  2. The concept of the Dart
  3. Dart keyword
Sample basic code for Dart
// Define a method.printInteger(int aNumber) {
  print('The number is $aNumber.'); // Print to console.} // main entry function.main() { var number = 42; // Declare and initialize variables.printInteger(number); // Function call. }Copy the code
/ / comment

Dart annotation methods, more annotation can read my another article at https://www.jianshu.com/p/d1dae0d5c472

int

Data type, data type to see more at https://www.dartlang.org/guides/language/language-tour#built-in-types

print()

A convenient way to display output

‘… ‘ (or “…” )

String. Dart prefers **’… ‘* *

$variableName (or ${expression})

String interpolation: A string containing variables or expressions inside the string literal

var

A method of declaring a variable without specifying its type, one of the keywords

Dart Key Concepts

Keep the following facts and concepts in mind when learning DART:

  • Everything is an Object, everything you put in a variable is an Object, each Object is an instance of class, numbers, functions, and NULL are objects, and all objects inherit from the Object class. Int int int int int int int int int int

  • Although Dart is strongly typed, type annotations are optional because Dart can infer types. In the above code, the number is inferred to be of type int. To specify that no type is required, use the special type Dynamic.

  • Dart supports generic types such as List(a List of integers) or List(a List of objects of any type).

  • Dart supports top-level functions (such as main()), functions that are bound to classes or objects (static and instance methods, respectively), and functions that can be created within functions (nested or local).

  • Dart also supports top-level variables, as well as variables (static and instance variables) bound to classes or objects, sometimes called fields or properties.

  • Unlike Java, Dart does not have the keywords public, protected, and private. If an identifier begins with an underscore (_), it is private, otherwise it is public.

  • Identifiers can begin with a letter or underscore (_), followed by any combination of these characters plus numbers.

  • Dart has expressions (with run time values) and statements (without run time values). For example, the condition expression “Condition? Expr1: The value of expr2 is expr1 or expr2”. Compare this to the if-else statement, which does not have any values. Statements usually contain one or more expressions, but expressions cannot contain statements directly.

The keyword

Dart has 60 keywords, so this might be a bit long

abstract2 dynamic2 implements2 show1
as2 else import2 static2
assert enum in super
async1 export2 in2 super
await3 extends is sync1
break external2 library2 this
case factory2 mixin2 throw
catch false new true
class final null try
const finally on1 typedef2
continue for operator2 var
covariant2 Function2 part2 void
default get2 rethrow while
deferred2 hide1 return with
do if set2 yield3
  • Keywords with superscript 1 are context keywords that have meaning only in a particular place.
  • Keywords with superscript 2 are built-in identifiers, and to simplify porting JavaScript code to Dart, these are valid identifiers in most places, but they cannot be used as class or type names, nor as import prefixes.
  • Keywords with superscript 3 are new identifiers in the new release and are limited reserved words related to the asynchronous support added to Dart 1.0.

See the instructions below for details

abstract

The abstract modifier is used to define abstract classes that cannot be instantiated. Abstract classes can customize some interfaces. Abstract classes usually have abstract methods. Here is an example of an abstract class that declares abstract methods:

// This class is declared abstract and cannot be instantiated. Abstract class AbstractContainer {// Define constructors, variables, methods, etc... / / other... // Abstract methods. void updateChildren(); }Copy the code

The following is an example of implementing an abstract method:

// Abstract class Doer {voiddoSomething(); Class EffectiveDoer extends Doer {void} EffectiveDoer extends Doer {voiddoSomething() {// implement logic}}Copy the code
dynamic

Dynamic, as the name suggests, goes directly to the code first

void judge(dynamic arg){
    if (arg is bool){
      print('arg is bool');
    } else if (arg is String){
      print('arg is String');
    } else if (arg is int){
      print('arg is int');
    } else {
      print('arg is others'); }}Copy the code

Dynamic is the same as Object, i.e. the above code can be equivalent to the following code:

void judge(Object arg){
    if (arg is bool){
      print('arg is bool');
    } else if (arg is String){
      print('arg is String');
    } else if (arg is int){
      print('arg is int');
    } else {
      print('arg is others'); }}Copy the code

In Dart, Dynamic and Object represent all types. The difference is that dynamic can be used to handle more complex indeterminate types, such as those outside Dart’s type system, or cases where values are interoperable or outside the scope of the static type system.

implements

Dart Dart Dart Dart Dart Dart Dart Dart Dart Dart Dart Dart Dart Dart Dart Dart Dart Dart

Class Person {// In this class, it is private and visible only to the current class. // Not an interface, just a constructor Person(this._name); // Interface String greet(String who) =>'Hello, $who. I am $_name.'; } // a Person class Impostor implements Person {// A Person class Impostor implements Person {// a Person class Impostor implements Person;' '; String greet(String who) =>'Hi $who. Do you know who I am? '; } // Just a test method String greetBob(Person Person) => person.greet('Bob');

void main() {
  print(greetBob(Person('Kathy'))); // Print -> Hello, Bob. I am Kathy.print(greetBob(Impostor())); Print -> Hi Bob. Do you know who I am? }Copy the code

Dart does not have Java interface functionality, so Impostor can use the implements keyword if it wants to implement the Person interface without inheriting the Person class. Implements interfaces for more than one class at a time:

class Point implements Comparable, Location {... }Copy the code
show & hide

Sometimes we import a library. If we only want to use part of the library, we can selectively import the library, for example:

// Import only foo import'package:lib1/lib1.dart' show foo;
Copy the code
// Import the entire library except foo import'package:lib2/lib2.dart' hide foo;
Copy the code
as, is, is!

As, is, and! The IS operator is handy for checking types at run time

  • As: Type conversion, also used to specify the library prefix
  • Is: Similar to Java instanceof
  • ! Is: the is operator is reversed, that is, not XXX

Code examples:

if(emp is Person) {// Type check emp.firstName ='Bob';
}
Copy the code
// If emp is Person, change firstName to Bod, otherwise an error is reported at runtime (emp as Person).firstname ='Bob';
Copy the code

If you import two libraries with conflicting identifiers (classes), you can specify prefixes for one or both libraries. For example, if library1 and library2 both have an Element class, then as could be used like this:

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart'as lib2; // Uses Element from lib1. Element element1 = Element(); // Uses Element from lib2. lib2.Element element2 = lib2.Element();Copy the code
if & else

Dart, like Java or other languages, supports if statements with optional ELSE statements:

if (isRaining()) {
  you.bringRainCoat();
} else if (isSnowing()) {
  you.wearJacket();
} else {
  car.putTopDown();
}
Copy the code
import

As with Java, import other packages using import. For example, Dart Web applications typically use the Dart: HTML library, which can be imported like this:

import 'dart:html';
Copy the code

If you just want to import a DART file in a package, you can do this:

import 'package:test/test.dart'; //指定导入test.dart(类似于Java中的test.java)
Copy the code
static

Use the static keyword to implement class-scoped variables and methods

Static variables (initialized only when used):

class Queue { static const initialCapacity = 16; / / void...}main() {
  assert(Queue.initialCapacity == 16);
}
Copy the code

The static method:

import 'dart:math';

class Point {
  num x, y;
  Point(this.x, this.y);

  static num distanceBetween(Point a, Point b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
  }
}

void main() { var a = Point(2, 2); var b = Point(4, 4); var distance = Point.distanceBetween(a, b); // Static method, do not instantiate assert(2.8 < distance && distance < 2.9)print(distance);
}
Copy the code
assert

Assert to assert; If the condition returns false, use an assert statement to interrupt normal execution, with the code:

// Interrupt assert(text! = null); Interrupt assert(number < 100) when number > 100; // If urlString is not a"https"Beginning assert (urlString startsWith ('https'));
Copy the code

To append a message to the assertion, enter a string in the second argument:

assert(urlString.startsWith('https'),
    'URL ($urlString) should start with "https".');
Copy the code
enum

An enumeration type (often called an enumeration or enumeration) is a special type used to represent a fixed number of constant values. The enum keyword is used to declare the enumeration type, for example:

enum Color { red, green, blue }
Copy the code

Each value in the enumeration has an index getter, which returns the zero-based position of the value in the enumeration declaration. For example, the first value has index 0 and the second value has index 1:

  print('red index: \${Color.red.index}'); // -> print red index: 0print('green index: \${Color.green.index}'); // -> print: green index: 1print('blue index: \${Color.blue.index}'); · //-> Print: blue index: 2Copy the code

To get a list of all the values in an enumeration, use the following methods:

List<Color> colors = Color.values;
Copy the code

You can use enumerations in switch statements, and you will receive a warning if you do not process all enumeration values:

var aColor = Color.blue;

switch (aColor) {
  case Color.red:
    print('Red as roses! ');
    break;
  case Color.green:
    print('Green as grass! ');
    break; Default: // Without this line of code, there will be a warningprint(aColor); // 'Color.blue'
}
Copy the code

Enumerated types have the following restrictions:

1. You cannot subclass, mix, or implement enumerations. 2. Enumeration cannot be instantiated explicitly.

for & in

You can iterate using a standard for loop, for example:

var message = StringBuffer('Dart is fun');
for (var i = 0; i < 5; i++) {
  message.write('! ');
}
Copy the code

Iterable classes like List and Set support for-in iteration using:

var list = [0, 1, 2];
  for (var x in list) {
    print(x); // 0 1 2}Copy the code

Is equal to:

  for (int i = 0; i < list.length; i++){
    print(list[i]); // 0 1 2}Copy the code
extend & super

Use extends to extend a class and super to call its parent class:

class Television {
  void turnOn() { _illuminateDisplay(); _activateIrSensor(); } // ··· ·} class extends Television {voidturnOn() { super.turnOn(); // Call the parent method _bootNetworkInterface(); _initializeMemory(); _upgradeApps(); } / /...}Copy the code
async & await
  • Async – > asynchronous
  • Await – > wait

The Dart library contains a number of functions that return Future or Stream objects. Future and Steam will be covered later, but I won’t go into that here. These functions are asynchronous: they return after setting up a potentially time-consuming operation, such as I/O, without waiting for the operation to complete.

The async and await keywords are used for asynchronous programming

The async keyword modifies a method that must return a Future object. Here is an example of code:

Future<String> getResult() async {returnawait getResultFromDb(); //await keyword declares the operation to be delayed and returns the result} Future<String>getResultFromDb() {// a lot of delayed operations // a lot of delayed operations // a lot of delayed operations // a lot of delayed operationsreturn new Future((){
      return 'This is server... '; }); } // print: result = This is server...print(getResult().then((result){
      print('result = $result');
  }));
Copy the code
export

Let’s look at the code for an official HTTP library: HTTP: ^0.12.0

export 'src/base_client.dart';
export 'src/base_request.dart';
export 'src/base_response.dart';
export 'src/byte_stream.dart';
export 'src/client.dart';
export 'src/exception.dart';
export 'src/multipart_file.dart';
export 'src/multipart_request.dart';
export 'src/request.dart';
export 'src/response.dart';
export 'src/streamed_request.dart';
export 'src/streamed_response.dart';
Copy the code

HTTP: / / HTTP: / / HTTP: / / HTTP: / / HTTP: / / HTTP: / / HTTP: / / HTTP: / / HTTP: / / HTTP: / / HTTP: / / HTTP: / / HTTP: / / HTTP: / / HTTP: / / HTTP: / / HTTP: / / HTTP

import 'package:http/http.dart' as http;
Copy the code

Dart: class browser_client.dart: class browser_client.dart: class browser_client.dart: class browser_client.dart: class browser_client.dart: class browser_client.dart: class browser_client.dart: class browser_client.dart: class browser_client.dart:

When we use it externally:

Is an error because the class does not have export, meaning it is not available externally.

interface

Has been removed

switch & case & default

The Dart switch statement can use integers, strings, or compile-time constants. Here is an example of string code:

var command = 'OPEN';
switch (command) {
  case 'CLOSED':
    executeClosed();
    break;
  case 'PENDING':
    executePending();
    break;
  case 'APPROVED':
    executeApproved();
    break;
  case 'DENIED':
    executeDenied();
    break;
  case 'OPEN':
    executeOpen();
    break; Default: // Condition executeUnknown() for other values; }Copy the code
sync & yield
  • Sync sync
  • Yield to generate

When we need to lazily generate a set of values (without a lot of the complex formulaic code that comes with manually defining iterable classes), we can consider using generator functions. Dart supports two generator functions built-in:

  • Synchrogenerator: Returns an Iterable object
  • Asynchronous generator: Returns a Stream object

Synchrogenerator: Marks the body of a function as sync * and uses yield to assign a value. The following example is an iterator that returns 0-n:

Iterable<int> naturalsTo(int n) sync* {
  print('start');
  int k = 0;
  while (k < n) yield k++;
  print('end'); } // use voidmain() {
  var it = naturalsTo(5).iterator;
  while(it.moveNext()) {
    print(it.current); Start value = 0 value = 1 value = 2 value = 3 value = 4 endCopy the code

When the naturalsTo method is called, Iterable is immediately returned and the iterator iterator can be retrieved. However, the naturalsTo function body is not executed immediately before the call is iterated. Iterator: var it = naturalsTo(5). Iterator: var it = naturalsTo(5). Iterator: var it = naturalsTo(5). Iterator: var it = naturalsTo(5). When you call naturalsTo to get an iterator of this Iterable, yield will yield a value each time you iterate through moveNext. When a function is run to yield, yield declares an evaluation expression, and when it returns the value, the function will continue to execute the function body on the next moveNext.

Asynchronous generator functions that mark the function body as async * and use yield statements to pass values:

Stream<int> asynchronousNaturalsTo(int n) async* {
  int k = 0;
  while(k < n) yield k++; } // use voidmain() {
  asynchronousNaturalsTo(5).listen((v) {
    print(v); }); } // print start 0 1 2 3 4 endCopy the code

Using an asynchronous generator, we can have “asynchronousNaturalsTo” return a string. Just like sync*, a call to “asynchronousNaturalsTo” returns a Stream immediately, but the body can only be called when listen listens for a Stream, The corresponding value is evaluated by the yield declaration evaluation expression.

If a generator uses recursion internally, yield * can be used to improve its performance:

Iterable<int> naturalsDownFrom(int n) sync* {
  if(n > 0) { yield n; yield* naturalsDownFrom(n - 1); }} // Use voidmain() {
  print(naturalsDownFrom(5)); } // Print (5, 4, 3, 2, 1)Copy the code

The naturalsDownFrom function still returns an Iterable. When 5 > 0, yield 5 is executed. The iterator yields a value of 5, which is then added to the current iterator using yield*.

break & continue

Jump out of the loop

while (true) {
  if (shutDownRequested()) break;
  processIncomingRequests();
}
Copy the code

Skip to the next iteration of the loop

or (int i = 0; i < candidates.length; i++) {
  var candidate = candidates[i];
  if (candidate.yearsExperience < 5) {
    continue;
  }
  candidate.interview();
}
Copy the code
external

The implementation of the presentation code is provided externally, and we define a class:

class Object {
  const Object();

  external bool operator ==(other);
  external int get hashCode; external String toString(); } // use voidmain() {
  Object object = new Object();
  print('to string = ${object.toString()}'); } // Prints to string = null // But if we remove toString, we print to string = Instance of'Object'// Dart prints toString by defaultCopy the code

External declares that these methods need to be implemented externally, and returns NULL if they are not

library & part

Use the library keyword to define the name of a library. Here we define a library to illustrate these two keywords:

library main; // Define the name of the current library as main import'dart:math'as math; // Declare that the following two files belong to the main library part'test/lib/liba.dart'; 
part 'test/lib/libb.dart';

class LibMain{
    static int max(int a, int b) => math.max(a, b);

    static String getParts() => LibA.TAG + "," + LibB.TAG;
}
Copy the code

liba.dart

part of main; Class LibA{static String TAG ='liba';
}
Copy the code

libb.dart

part of main; Class LibB{static String TAG ='libb';
}

Copy the code

After importing main,

import 'lib/main.dart';
Copy the code

Dart and libb.dart declare classes that are externally available:

Part can split libraries into multiple Dart files, but it is recommended to avoid using it to create libraries as much as possible because it can make the code difficult to read and modify. Dart is recommended to create a “main” library file directly under lib/< package name >.dart and use a main file to manage all public apis.

this

Similar to this in Java, use the this keyword to refer to the current instance

factory

The factory keyword can be used when implementing a constructor that does not always create a new instance, as in the following example, which may return an instance, or an instance of a subclass, from the cache.

class Logger {
  final String name;
  bool mute = false; Static final map <String, Logger> _cache = <String, Logger>{}; Factory Logger(String name) {if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final logger = Logger._internal(name);
      _cache[name] = logger;
      returnlogger; }} // An internal constructor logger._internal (this.name); voidlog(String msg) {
    if(! mute)print(msg); }}Copy the code

Note that constructors that use the Factory modifier cannot use this, just as we cannot use this in Java static functions

mixin & with & on

Support for the mixin keyword was introduced in Dart 2.1, and we can see the official description: Mixins are a way to reuse class code in multiple class hierarchies. Key information:

  • Multiple class hierarchies
  • Reuse class code

I only briefly describe the function and usage of this keyword:

  • Mixin literally means mixin. To use a mixin, use the with keyword followed by the name of one or more mixins
  • Sometimes we need to create a class that needs to use different methods of different classes, so we need to use mixin methods. Dart can only inherit one class, and to use an interface, we must implement that interface in other classes.
// Programmers like to write code.code() {print('I am a programmer, i like coding.'); }} // Class Singer like singing {singing() {print('I am a singer, i like singing.'); }} // the Programmer is a Programmermain() { Mixin mixin = Mixin(); mixin.code(); mixin.singing(); } // Print: I am a programmer, I like coding. I am a musician, I like singing.Copy the code

Note that the classes Programmer and Singer cannot declare constructors that include named functions:

What happens when we use mixins to call the same interface of different classes? Let’s look at the following code:

class A {
  name() {print('I am a student.');
  }
}

class B{
  name() {print('I am a teacher.');
  }
}

class AB with A, B{

}

class BA with B, A{

}

void main() { new AB().name(); new BA().name(); } // Print: I am a teacher. I am a student.Copy the code

So you can see that the same interface here is name, and eventually the interface of the last class of with is called.

If we need to specify what classes can be mixed in, we can use the mixin+ ON method to specify:

// Mixn defines a class that allows a Flutter to be mixed with a Programmer{flu() {print('This is in flutter.'); Class B extends with Flutter{} new B ().flu(); // Print This isin flutter.
Copy the code

Finally, a more detailed explanation of mixins can be found in:

  • www.dartlang.org/articles/la…
  • www.jianshu.com/p/a578bd2c4…
throw

Throw an exception:

throw FormatException('Expected at least 1 section');
Copy the code

Can also throw any arbitrary object exception:

throw 'Out of llamas! ';
Copy the code

class Throw{
  @override
  String toString() {
    return 'Are you ok? ';
  }
}

void main() {
  throw new Throw();
}
Copy the code

try & catch & finally & on & rethrow

Exception catching:

// Usually void is usedmain() {
  try{
    throw "You are wrong.";
  }catch (e){
    print('catch exception: '+e); }} // Print: catch exception: You are wrong.Copy the code

You can catch some kind of exception using ON

class ExceptionA{
  @override
  String toString() {
    return 'This is exception a.';
  }
}

class ExceptionB{
  @override
  String toString() {
    return 'This is exception b.'; } } throwCatchException(Object object){ try{ throw object; } on ExceptionA{// Specify an exception classprint("It's exception a."); } on ExceptionB catch(e){// Specify an exception class and get an exception objectprint(e);
  } on Exception catch (e){
    print(e);
  }
}

void main() {
  throwCatchException(new ExceptionA());
  throwCatchException(new ExceptionB());
  throwCatchException(new Exception(' ')); } // Print: It's exception a.
This is exception b.
Exception: 
Copy the code

You can specify one or two parameters for catch(), the first being the exception object thrown and the second being the StackTrace object:

void main() {
  try{
    throw 'This is a exception.';
  }catch (e, s){
    print('e ${e}');
    print('s ${s}'); }} // Print: e This is a exception.s#0 main (file:///E:/flutter/projects/flutter/test/test.dart:5:5)
#1 _startIsolate.
      
        (dart:isolate/runtime/libisolate_patch.dart:289:19)
      
#2 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)
Copy the code

If you want exceptions to propagate, use the rethrow interface

void misbehave() {
  try {
    dynamic foo = true;
    print(foo++); } catch (e) {print('misbehave() partially handled ${e.runtimeType}.'); rethrow; Allow the caller to see the exception}} voidmain() {
  try {
    misbehave();
  } catch (e) {
    print('main() finished handling ${e.runtimeType}.'); }} // Print: misbehave() partially handled NoSuchMethodError. Main () finished handling NoSuchMethodError.Copy the code

To make sure some code runs, whether or not an exception is thrown, use the finally clause. If no catch clause matches the exception, the exception is thrown after the finally clause is run:

class ExceptionA{
  @override
  String toString() {
    return 'This is exception a.';
  }
}

class ExceptionB{
  @override
  String toString() {
    return 'This is exception b.'; }}finallyMethod() {print('finally method.');
}

void main() {
  try {
    throw new ExceptionA();
  } on ExceptionB catch (e) {
    print(e); } finally{ finallyMethod(); }} // Print: finally method. Unhandled Exception: This is exception A.#0 main (file:///E:/flutter/projects/flutter/test/test.dart:21:5)
#1 _startIsolate.
      
        (dart:isolate/runtime/libisolate_patch.dart:289:19)
      
#2 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)
Copy the code
false & true

Dart has a type called bool to represent Boolean values, and only two objects have bool types: true and false, both of which are compile-time constants

new

Creating a class instance

class

Declare a class

final & const

Final states that a variable can only be initialized once, as in Java usage

Const declares a variable that is a compile-time constant. Constant variables cannot be assigned. If const is at the class level, it is marked as static const. When we want a variable not to be changed, we can declare it as const.

typedef

A typedef is used to specify a name for a Function type. Because in Dart, a Function is also an object, it is common to use Function to refer to all functions. Let’s take a look at the following example (without using a Function alias) :

class SortedCollection { Function compare; SortedCollection(int f(Object a, Object b)) {compare = f; SortedCollection(int f(Object a, Object b)); }} // sort(Object a, Object b) => 0; voidmain() { SortedCollection coll = SortedCollection(sort); Assert (coll.compare is Function); assert(coll.compare is Function); assert(coll.compare is Function); // There is no doubt about ittrue, that is, execution is not interrupted}Copy the code

We can use a typedef to declare a function type:

Typedef compare = int Function(Object a, Object b); class SortedCollection { Compare compare; SortedCollection(this.compare); } sort(Object a, Object b) => 0;} sort(a, b) => 0; voidmain() {
  SortedCollection coll = SortedCollection(sort);
  assert(coll.compare is Function);  //True
  assert(coll.compare is Compare); //True
}
Copy the code

Currently typedefs can only be used to declare function types,

operator

If you wanted to define a Vector class, you could use the following operators:

< + | []
> / ^ [] =
< = ~ / & ~
> = * << = =
% >>

Here is an example of a class that overrides the + and – operators:

class Vector {
  final int x, y;

  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

// Operator == and hashFor details, see note below. // ···} voidmain() {
  final v = Vector(2, 3);
  final w = Vector(2, 3);
  final vw = v + w;
  final ww = v - w;

  print('vw -> (${vw.x}, ${vw.y})');
  print('ww -> (${ww.x}, ${ww.y})'); } // print: vw -> (4, 6) ww -> (0, 0)Copy the code
var

Use var to declare a variable without specifying its type

var name = 'Bob'; // Declare a variable as a string variableCopy the code

But once a declaration is assigned to a type, it cannot be assigned to another type

covariant

When refactoring a method from a class, it is forced to narrow its arguments from the parent class to its subclass.

Animal class extends Animal {void chase(Animal x) {}} class Mouse extends Animal {getName() {return 'mouse'; }} class Cat extends Animal {void chase(Mouse Mouse) {print('cat chase ${mouse.getName()}'); }} // An error is reported indicating that the rewrite type does not matchtest/test.dart:12:20: Error: The parameter 'mouse' of the method 'Cat::chase' has type #lib1::Mouse, which does not match the corresponding type in the overridden method (#lib1::Animal).
Change to a supertype of #lib1::Animal (or, for a covariant parameter, a subtype).
  void chase(Mouse mouse) {
                   ^
test/test.dart:2:8: Context: This is the overridden method ('chase').
  void chase(Animal x) {}
       ^
Copy the code

Using the covariant keyword:

class Animal {
  void chase(Animal x) {}
}

class Mouse extends Animal {
  getName() {return 'mouse';
  }
}

class Cat extends Animal {
  void chase(covariant Mouse mouse) {
    print('cat chase ${mouse.getName()}');
  }
}

void main(){ new Cat().chase(new Mouse()); } // Print cat Chase mouseCopy the code

Covariant means covariant, that is, I negotiate with the compiler, and this parameter narrowing change is intentionally done by me, so don’t throw exceptions.

Function

Dart is a true object-oriented language, so even functions are objects and have type functions. This means that functions can be assigned to variables or passed as arguments to other functions.

Int add(int a, int b) => a+b; handle(Functionfunction) {print('handle: ${function(1, 2)}'); / / printfunction} voidmain(){ handle(add); // Call handle and pass the add function} // Print handle: 3Copy the code
void

In Dart 1, void is only available as a return type for functions (such as void main()), but in Dart 2 it has been generalized and can be used elsewhere, such as Future

Void cannot be used for anything, and assigning something to void is invalid:

void foo() {}
void main() { var bar = foo(); / / null}Copy the code

The expression here has a type of ‘void’, and therefore cannot be used. -> This expression is of type ‘void’ and cannot be used

In a function, indicates that the function does not return a value.

In practice, void is commonly used to mean “any element I don’t care about” or, more commonly, “omit”, as in a Future or Stream.

get & set

In Java, getters and setters can be a headache. If some classes have enough properties, they can easily be hundreds or thousands of lines. But in Dart, set and GET keys are provided to provide read and write access to object properties.

class Rectangle { num left, top, width, height; Rectangle(this.left, this.top, this.width, this.height); // Define two computable attributes right and bottom. num get right => left + width;set right(num value) => left = value - width;

  num get bottom => top + height;
  set bottom(num value) => top = value - height;
}

void main() {
  var rect = Rectangle(3, 4, 20, 15);
  
  print('right: ${rect.right}');
  rect.right = 100;
  print('right: ${rect.right}');

  print('bottom: ${rect.bottom}');
  rect.bottom = 120;
  print('bottom: ${rect.bottom}'); } // Print: right: 23 right: 100 bottom: 19 bottom: 120Copy the code

Dart uses get and set to implement getters and setters

With get and set, we can start with instance variables and wrap methods in get/set methods without changing the code of external calls.

while & do while

While: Evaluates the condition before the loop executes

while(! isDone()) {doSomething();
}
Copy the code

Do-while: Evaluates the condition after the loop has started execution

do {
  printLine();
} while(! atEndOfPage());Copy the code
deferred

Deferred is used to declare a lazy loading of a library, usually called lazy loading. Allows you to load the library only when you need to use it.

Dart class calculate {static String name = calculate"Cal";

  static printf(String s){
    print('cal: $s'); } int add(int a, int b) => a + b; } // file test.dart import'calculate.dart'deferred as cal; // Declare that the library will be lazily loaded and named CAL voidmain() {
  print('1');
  greet();
  print('2');
  print('3'); Future greet() async {await cal.loadLibrary(); Future greet() async {await cal.loadLibrary();print('cal name: ${cal.Calculate.name}');
  print('add(1, 2) = ${new cal.Calculate().add(1, 2)}');
  cal.Calculate.printf('ss'); CAL name: CAL add(1, 2) = 3 CAL: ssCopy the code
return

Used to return a value in a function