Connect with us

Articles

7 Cool Features of Dart Language

Mixins, Cascade notation, and more.

Today’s article is a short revelation of the cool features that the dart language offers. More often these options are not necessary for simple apps but are a lifesaver when you want to improve your code by making it simple, clear, and concise.

With that in mind let’s go.

Cascade notation

Cascades (..?..) allow you to make a sequence of operations on the same object. This often saves you the step of creating a temporary variable and allows you to write more fluid code.

var paint = Paint();
paint.color = Colors.black;
paint.strokeCap = StrokeCap.round;
paint.strokeWidth = 5.0;

//above block of code when optimized
var paint = Paint()
  ..color = Colors.black
  ..strokeCap = StrokeCap.round
  ..strokeWidth = 5.0;

Abstract classes

Use the abstract modifier to define an abstract class (a class that can’t be instantiated). Abstract classes are useful for defining interfaces, often with some implementation.

// This class is declared abstract and thus
// can't be instantiated.
abstract class AbstractContainer {
  // Define constructors, fields, methods...

  void updateChildren(); // Abstract method.
}

Factory constructors

Use the factory keyword when implementing a constructor that doesn’t always create a new instance of its class.

class Logger {
  String name;
  Logger(this.name);
  factory Logger.fromJson(Map<String, Object> json) {
    return Logger(json['name'].toString());
  }
}

Named constructors

Use named constructors to implement multiple constructors for a class or to provide extra clarity:

class Points {
  final double x;
  final double y;
  
  //unnamed constructor
  Points(this.x, this.y);

  // Named constructor
  Points.origin(double x,double y)
      : x = x,
        y = y;
  
  // Named constructor
  Points.destination(double x,double y)
      : x = x,
        y = y;
}

Mixins

Mixins are a way of reusing a class’s code in multiple class hierarchies.

To implement a mixin, create a class that declares no constructors. Unless you want your mixin to be usable as a regular class, use the mixin keyword instead of class.

To use a mixin, use the with keyword followed by one or more mixin names.

To restrict the types that can use a mixin use the on keyword to specify the required superclass.

class Musician {}

//creating a mixin
mixin Feedback {
  void boo() {
    print('boooing');
  }

  void clap() {
    print('clapping');
  }
}

//only classes that extend or implement the Musician class
//can use the mixin Song
mixin Song on Musician {
  void play() {
    print('-------playing------');
  }

  void stop() {
    print('....stopping.....');
  }
}

//To use a mixin, use the with keyword followed by one or more mixin names
class PerformSong extends Musician with Feedback, Song {
  //Because PerformSong extends Musician,
  //PerformSong can mix in Song
  void awesomeSong() {
    play();
    clap();
  }

  void badSong() {
    play();
    boo();
  }
}

void main() {
  PerformSong().awesomeSong();
  PerformSong().stop();
  PerformSong().badSong();
}

Typedefs

A type alias — is a concise way to refer to a type. Commonly used to create a custom type that is used a lot in the project.

typedef IntList = List<int>;
List<int> i1=[1,2,3]; // normal way.
IntList i2 = [1, 2, 3]; // Same thing but shorter and clearer.

//type alias can have type parameters
typedef ListMapper<X> = Map<X, List<X>>;
Map<String, List<String>> m1 = {}; // normal way.
ListMapper<String> m2 = {}; // Same thing but shorter and clearer.

Extension methods

Extension methods, introduced in Dart 2.7, are a way to add functionality to existing libraries and code.

//extension to convert a string to a number
extension NumberParsing on String {
  int customParseInt() {
    return int.parse(this);
  }

  double customParseDouble() {
    return double.parse(this);
  }
}

void main() {
  //various ways to use the extension

  var d = '21'.customParseDouble();
  print(d);

  var i = NumberParsing('20').customParseInt();
  print(i);
}

Optional positional parameters

You can make positional parameters optional by wrapping them in brackets. Optional positional parameters are always last in a function’s parameter list. Their default value is null unless you provide another default value.

String joinWithCommas(int a, [int? b, int? c, int? d, int e = 100]) {
  var total = '$a';
  if (b != null) total = '$total,$b';
  if (c != null) total = '$total,$c';
  if (d != null) total = '$total,$d';
  total = '$total,$e';
  return total;
}

void main() {
  var result = joinWithCommas(1, 2);
  print(result);
}

unawaited_futures

When you want to start a fire-and-forget Future, the recommended way is to use unawaited

import 'dart:async';

Future doSomething() {
  return Future.delayed(Duration(seconds: 5));
}

void main() async {
  //the function is fired and awaited till completion
  await doSomething();

  // Explicitly-ignored
  //The function is fired and forgotten
  unawaited(doSomething());
}

If you come across another cool feature, please let me know so that I can top it up on this list.

That’s all for today.

Full Article: Dedan Ndungu @ Better Programming

Advertisement

Trending