Object-Orientated Features in Google Dart for Java / .NET Developers

Google, Google Flutter, Object Orientated -

Object-Orientated Features in Google Dart for Java / .NET Developers

Object-Orientated Language Features

Modules

Unlike Java and C#, Dart allows you to declare multiple objects within a single Dart file.

This has made our example code a single cut-n-paste!

Private Classes, Variables & Methods

Unlike Java, Dart doesn't have the keywords public, protected, and private to specify the visibilities of fields or properties. If a class name, instance variable or method starts with an underscore, it's private and cannot be accessed outside the Dart file in which it is declared.

You should replace:

class ContactInfo {
  private String name;
  private String phone;
}
with

class ContactInfo {
  String _name;
  String _phone; 
}

Constructors

Default Constructor

If you do not specify a constructor, a default constructor will be created for you without arguments. If you do specify a constructor, the default constructor won’t be created for you.

Constructor Syntax Shortcut

If you want to set the value of an instance variable in a constructor, you can use the ‘this.[instance variable name]’ to set it in the constructor signature.

Example Code

class Name{
  String firstName;
  String lastName;
 
  Name(this.firstName, this.lastName);
}
 
main(){
  Name name = new Name('mark','smith');
  print(name.firstName);
  print(name.lastName);
}
Output

mark
smith

New Keyword

Dart doesn’t need you to use the ‘new’ keyword when invoking constructors. However, you can keep it if you want.

Example Code

void main() {
  Car car = Car("BMW","M3");
  print(car.getBadge());
 
  Car car2 = new Car("BMW","M3");
  print(car2.getBadge());
}
 
class Car{
  String _make;
  String _model;
 
  Car(this._make, this._model){}
 
  String getBadge(){
    return _make + " - " + _model;
  }
}
Output

BMW - M3
BMW - M3

Named Constructors

Dart allows named constructors and I have found them very useful indeed if you want to instantiate the same class in different ways. Named constructors (if named correctly) can also improve code readability & intent.

Example

A good example of a Flutter class that uses multiple named constructors is EdgeInsets:

  • fromLTRB
  • all
  • only
  • symmetric
  • fromWindowPadding
Example Code

class ProcessingResult{
  bool _error;
  String _errorMessage;
 
  ProcessingResult.success(){
    _error = false;
    _errorMessage = '';
  }
 
  ProcessingResult.failure(this._errorMessage){ //shortcut
    this._error = true;
  }
 
  String toString(){
    return 'Error: ' + _error.toString() + ' Message: ' + _errorMessage;
  }
}
 
void main() {
  print(ProcessingResult.success().toString());
  print(ProcessingResult.failure('it broke').toString());
}
Output

Error: false Message:
Error: true Message: it broke

Constructor Parameters

Constructors can accept different kinds of parameters, similar to methods.

Factory Constructors

You can use the factory keyword when implementing a constructor that doesn’t always create a new instance of its class.  The factory keyword allows you to return a variable at the end of the constructor. This is useful when you want the constructor to return an instance from a variable or a cache.

Example Code

class Printer{
  static final Printer _singleton = Printer._construct();
 
  factory Printer(){
    return _singleton;
  }
 
  Printer._construct(){
    print('private constructor');
  }
 
  printSomething(String text){
    print(text);
  }
   
}
 
void main() {
  Printer().printSomething("this");
  Printer().printSomething("and");
  Printer().printSomething("that");
}
Output

Note how the constructor was only invoked once.


private constructor
this
and
that

Instance Variables

Unspecified Visibility

You don’t have to specify the visibility of instance variables and if you don’t then they are made public.


class Name {
  String firstName;
  String lastName;
}

Default Values

The default values of instance variables are null.

Constructor and Method Parameters

Flutter is very flexible in regard to constructor & method parameters. There are several different kinds:

 

  1. Positional Required
  2. Positional Optional
  3. Named

1. Parameters - Positional Required

These are declared first.

These are required.

Constructor with required parameters:


class Car{
  String _make;
  String _model;
  Car(this._make,this._model){} 
}

2. Parameters - Positional Optional

These are declared second. You can make parameters optional, by using the square brackets. If an optional parameter is not supplied, it has a null value.

Example Code


void main() {
  Car car1 = Car("Nissan","350Z");
  Car car2 = Car("Nissan");
}
 
class Car{
  String _make;
  String _model;
  Car(this._make,[this._model]){
    print('${_make} ${_model}');
  } 
}

Output


    Nissan 350Z
    Nissan null

3. Parameters - Named

All named parameters are optional.

These are declared last.

You can make parameters named, by using the curly brackets.

If a named parameter is not supplied, it has a null value.

Example Code


void main() {
  Car car1 = Car("Nissan", model:"350Z", color: "yellow");
  Car car2 = Car("Nissan", color:"red");
  Car car3 = Car("Nissan");
}
 
class Car{
  String make;
  String model;
  String color;
  Car(this.make,{this.model,this.color}){
    print('${make}${getOptional(model)}${getOptional(color)}');
  } 
 
  String getOptional(String str) {
    return str == null ? "" : " " + str;
  }
}

Output


Nissan 350Z yellow
Nissan red
Nissan

Required Decorator

You can add the ‘@required’ decorator to named parameters to make them required.

This is not a part of Dart, but it is part of Flutter. Therefore, it won’t work with Dartpad.

Example Code

We define a constructor for SelectButton that requires both ‘text’ and ‘onTap’ named parameters.


SelectButton({@required this.text, @required this.onTap});

If you declare a named parameter as ‘@required’ and the developer writes code that does not supply that parameter:


SelectButton(text: "YES"),

then the following compilation error occurs:


warning: The parameter 'onTap' is required. (missing_required_param at [yes_no] lib/main.dart:58)

Interfaces

Dart uses implicit interfaces.

Example Code


abstract class IsSilly {
  void makePeopleLaugh();
}
 
class Clown implements IsSilly {
  void makePeopleLaugh() {
    // Here is where the magic happens
  }
}
 
class Comedian implements IsSilly {
  void makePeopleLaugh() {
    // Here is where the magic happens
  }
}

Further Reading

https://www.dartlang.org/guides/language/language-tour - implicit-interfaces

Constants vs Finals

You can use either to tell Dart that the variable value is not going to change. However there are some subtle differences between the two:

const

This sets the variables value at compile time. Its value cannot be changed at run time. If you declare multiple constants with the same value, Dart ends up internally using the same object for all. Objects inside a const cannot be changed. For example, if you have a const collection, everything in it must also be const, recursively. 

Use constants for variables who’s value you know at the time of writing the code & that will never change.

final

This sets the variables value at run time. Its value can be assigned once. A final variable or field must have an initializer. Once assigned a value, a final variable's value cannot be changed afterwards.

Objects inside a final can possibly be changed. For example, if you have a final field containing a collection, that collection can still be mutable.

Use finals for variables who’s value you know at the time of initialization & that will never change once set.

Other

Method Cascades

Method cascades can help with the brevity of your code.

Example Code


class Logger {
  void log(dynamic v){
    print(DateTime.now().toString() + ' ' + v);
  }
}
main(){ 
  // Without method cascades
  new Logger().log('program started');
  new Logger().log('doing something');
  new Logger().log('program finished');
 
  // With method cascades
  new Logger()
    ..log('program started')
    ..log('going something')
    ..log('program finished');
}

Output


2018-12-30 09:28:39.686 program started
2018-12-30 09:28:39.686 doing something
2018-12-30 09:28:39.686 program finished
2018-12-30 09:28:39.686 program started
2018-12-30 09:28:39.686 going something
2018-12-30 09:28:39.686 program finished

toString

You can use the ‘toString’ method to convert an object to its string representation. This is like other languages.

However, Dart also gives you some other similar convenience methods:

  • toStringAsFixed – converts object to double then outputs double with specified number of digits after decimal point. Quite useful!
  • toStringAsPrecision – converts object to double then outputs double with specified precision.