Flutter Solves Dart

Posted by yelvington on Thu, 12 Sep 2019 15:04:57 +0200

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.

Topics: Android TypeScript React