“This is the 21st day of my participation in the Gwen Challenge in November. See details: The Last Gwen Challenge in 2021”

preface

When we write Dart code, variables and class members are used every day, but are they always used in the right way? In this article, we’ll show you how variables and class members can be used properly.

Rule 1: Use var and final the same way for local variables

For most local variables, you do not need to specify a type. Instead, var or final should be used. You should choose one of the following two rules:

  • For use that does not duplicate assignmentsfinalAnd other usesvar. This actually seems easy to follow, but it’s easy to ignore when you’re writing. One lesson is use firstfinalIf you need to reassign a value latervar.
  • For local variables, use onlyvar, even for local variables that are not reassigned, i.e. not used for local variablesfinal. Note that local changes here refer to local variables inside a function. Member variables of a class can of course be usedfinalModification. This one is easier to follow.

Once you choose one of the above, you should stick to it, or the obsessive-compulsive coder will look at your code and ask a bunch of questions — “Why final? Why don’t we use final here?” You’ll probably end up with an awkward “Uh…” “And found an excuse to put it off.

Rule 2: Do not store those computed variables

Computed variables are attributes that can be computed from other class members. There are a lot of problems when you store it, for example you may need to update the computed variable in the place of the associated property changes, which can cause bugs if you miss it. For example, the following example is a typical negative example.

// Error example
class Circle {
  double radius;
  double area;
  double circumference;

  Circle(double radius)
      : radius = radius,
        area = pi * radius * radius,
        circumference = pi * 2.0 * radius;
}
Copy the code

Both the area and the perimeter depend on the radius. There are two drawbacks to this approach:

  • Added two member attributes to cache area and perimeter, wasting storage space;
  • There is a hidden danger of out of sync, once the radius is changed, if the area and perimeter are not actively updated, there will be inconsistent situation. To solve this problem, update the area and perimeter as the radius changes:
// Error example
class Circle {
  double _radius;
  double get radius => _radius;
  set radius(double value) {
    _radius = value;
    _recalculate();
  }

  double _area = 0.0;
  double get area => _area;

  double _circumference = 0.0;
  double get circumference => _circumference;

  Circle(this._radius) {
    _recalculate();
  }

  void _recalculate() {
    _area = pi * _radius * _radius;
    _circumference = pi * 2.0* _radius; }}Copy the code

The code is stinky and long, right?The correct way is to use get to get the calculated properties. Isn’t this super neat?

// Correct example
class Circle {
  double radius;

  Circle(this.radius);

  double get area => pi * radius * radius;
  double get circumference => pi * 2.0 * radius;
}
Copy the code

Of course, this rule is not always set in stone. If your calculation is very complex and there are few variables to change, then declaring a member attribute to cache the result can save a lot of CPU overhead, i.e. space swap time. This time is can do so, specific how to choose their own according to the cost to judge.

Rule 3: Do not provide getters and setters for class members unless necessary

In languages such as Java or C#, it is often recommended to hide all member attributes and provide getters and setters to access them, because the two languages treat getters, setters, and direct access to attributes differently. In Dart, there is no difference between accessing a member and using getters and setters. Therefore, if a member is fully accessible externally (including getters and setters), there is no need to use getters and setters. Of course, if one member can only do a setter or getter, then you need to provide a separate method and mask the other.

// Correct example
class Box {
  Object? contents;
}

// Error example
class Box {
  Object? _contents;
  Object? get contents => _contents;
  set contents(Object?value) { _contents = value; }}Copy the code

Rule 4: Use final decorations for read-only members

If your member property is externally read-only, use the final modifier instead of providing getter access. This assumes, of course, that the member property is not reassigned after initialization.

// Correct example
class Box {
  final contents = [];
}

// Error example
class Box {
  Object? _contents;
  Object? get contents => _contents;
}
Copy the code

Rule 5: For simple computed return values, the => expression is preferred

This is actually a guide for simplifying code and improving readability. When using a simple expression to return a calculated property or call a method, the => expression is much more concise.

// Correct example
double get area => (right - left) * (bottom - top);

String capitalize(String name) =>
    '${name[0].toUpperCase()}${name.substring(1)}';
Copy the code

Of course, assuming several lines of code to evaluate the return value, it is better to use the normal function form.

// Correct example
Treasure? openChest(Chest chest, Point where) {
  if (_opened.containsKey(chest)) return null;

  var treasure = Treasure(where);
  treasure.addAll(chest.contents);
  _opened[chest] = treasure;
  return treasure;
}

// Error example
Treasure? openChest(Chest chest, Point where) => _opened.containsKey(chest)
    ? null: _opened[chest] = (Treasure(where).. addAll(chest.contents));Copy the code

In fact, the principle is to decide which approach to choose based on the readability of your code, rather than copying it mechanically.You can also use the => form for member attributes, such as the following to make the code more concise.

num get x => center.x;
set x(num value) => center = Point(value, center.y);
Copy the code

Do not use this. To access members unless you want to distinguish it from arguments of the same name

Using this. To access class members is common in many languages, but is not recommended in Dart. There are only two cases where you need to use this for accessing members:

  • Constructor arguments that use the this. table name constructor are used to set member variables;
  • Parameter names in other functions have the same name as class member attributes and need to be usedthis.To distinguish.
// Correct example
class Box {
  Object? value;

  void clear() {
    update(null);
  }
  
  String toString() {
    return 'Box: $value';
  }

  void update(Object? value) {
    this.value = value; }}// Error example
class Box {
  Object? value;

  void clear() {
    this.update(null);
  }
  
  String toString() {
    return 'Box: The ${this.value}';
  }

  void update(Object? value) {
    this.value = value; }}Copy the code

Other examples of using this. Include using constructors to construct named constructors:

class ShadeOfGray {
  final int brightness;

  ShadeOfGray(int val) : brightness = val;
	
  // Use the constructor
  ShadeOfGray.black() : this(0);

  // Use the named constructor
  ShadeOfGray.alsoBlack() : this.black();
}
Copy the code

Whenever possible, this class is initialized at member declaration time

If member attributes do not depend on construction parameters, they can be initialized at declaration time, resulting in less code and avoiding duplicate code because the class has multiple constructors.

// Correct example
class ProfileMark {
  final String name;
  final DateTime start = DateTime.now();

  ProfileMark(this.name);
  ProfileMark.unnamed() : name = ' ';
}

// Error example
class ProfileMark {
  final String name;
  final DateTime start;

  ProfileMark(this.name) : start = DateTime.now();
  ProfileMark.unnamed()
      : name = ' ',
        start = DateTime.now();
}
Copy the code

Some members cannot be initialized initially because they depend on other members or need to call methods to initialize them. At this point, it should be modified with late, at which point you can access this for initialization.

class _AnimatedModelBarrierDemoState extends State<AnimatedModelBarrierDemo>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller =
      AnimationController(duration: const Duration(seconds: 1), vsync: this);

  lateAnimation<Color? > _colorAnimation = ColorTween( begin: Colors.black.withAlpha(50),
    end: Colors.black.withAlpha(80),
  ).animate(_controller);
  
  // ...
}
Copy the code

conclusion

As you can see, there are actually many different ways to write code.Tens of thousands of ways to write, the first specification; Code is not standard, colleague two lines of tears“. “Code farmers why bother code farmers”, with guidelines, can write high-quality code.

I am dao Code Farmer with the same name as my wechat official account. This is a column about the introduction and practice of Flutter, providing systematic learning articles about Flutter. See the corresponding source code here: The source code of Flutter Introduction and Practical column. If you have any questions, please add me to the wechat account: island-coder.

👍🏻 : feel the harvest please point a praise to encourage!

🌟 : Collect articles, easy to look back!

💬 : Comment exchange, mutual progress!