Skip to content

Basics of Dart and OOP

Praveen Varma edited this page Mar 22, 2022 · 13 revisions

Dart

Dart is an open-source general-purpose programming language. It is originally developed by Google. Dart is an object-oriented language with C-style syntax. It supports programming concepts like interfaces, classes, unlike other programming languages Dart doesn’t support arrays. Dart collections can be used to replicate data structures such as arrays, generics, and optional typing.

The following code shows a simple Dart program −

void main() {
   print("Dart language is easy to learn");
}

main( ) in Dart

The main() function is a predefined method in Dart. It is the most important and mandatory part of any Dart Program. Any Dart script requires the main() method for its execution. This method acts as the entry point for any Dart application. It is responsible for executing all library functions, user-defined statements, and user-defined functions.

void main()
{
    //main() function body 
}

Variables and Data types

Variable is named storage location and Data types simply refers to the type and size of data associated with variables and functions.

Dart uses var keyword to declare the variable. The syntax of var is defined below,

var name = 'Dart';

The final and const keyword are used to declare constants. They are defined as below −

void main() {
   final a = 12;
   const pi = 3.14;
   print(a);
   print(pi);
}

Dart language supports the following data types −

  • Numbers - It is used to represent numeric literals – Integer int and Double double.
  • Strings - It represents a sequence of characters. String String values are specified in either single or double quotes
  • Booleans - Dart uses the bool keyword to represent Boolean values – true and false.
  • Lists and Maps - It is used to represent a collection of objects. A simple List can be defined as below −
void main() {
  var list = [1,2,3,4,5];
  print(list);
}

Map can be defined as shown here −

void main() {
   var mapping = {'id': 1,'name':'Dart'};
   print(mapping);
}
  • Dynamic − If the variable type is not defined, then its default type is dynamic. The following example illustrates the dynamic type variable −
void main() {
   dynamic name = "Dart";
   print(name);
}

Decision making and loops

A decision making block evaluates a condition before the instructions are executed. Dart supports if, if..else and switch statements.

Loops are used to repeat a block of code until a specific condition is met. Dart supports for, for..in , while and do..while loops.

Let us understand a simple example about the usage of control statements and loops −

void main() {
   for( var i = 1 ; i <= 10; i++ ) {
      if(i%2==0) {
         print(i);
      }
   }
}

The above code prints the even numbers from 1 to 10.

Functions

Functions are the building blocks of readable, maintainable, and reusable code. A function is a set of statements to perform a specific task. Functions organize the program into logical blocks of code. Once defined, functions may be called to access code. This makes the code reusable. Moreover, functions make it easy to read and maintain the program’s code.

A function declaration tells the compiler about a function's name, return type, and parameters. A function definition provides the actual body of the function. Let us look into a simple function in Dart as shown here −

void main() {
   add(3,4);
}
void add(int a,int b) {
   int c;
   c = a+b;
   print(c);
}

The above function adds two values and produces 7 as the output.

Lambda functions

Lambda functions are a concise mechanism to represent functions. These functions are also called as Arrow functions. Syntax-

[return_type]function_name(parameters)=>expression;

Example:

void main() { 
   printMsg(); 
   print(test()); 
}  
printMsg()=>
print("hello"); 

int test()=>123;                       
// returning function

Concepts of OOP

Object-oriented programming combines a group of data attributes with functions or methods into a unit called an "object." Typically, OOP languages are class-based, which means that a class defines the data attributes and functions as a blueprint for creating objects, which are instances of the class.

A simple example would be a class representing a person. The person class would contain attributes to represent information such as the person’s age, name, height, etc. The class definition might also contain functions such as "sayMyName" which would simply print that person’s name to the screen.

class Person{

// attributes
int age;
String name;
double height;

// constructor
Person(String name){
this.name = name;
 }

// method or fucntion
void sayMyName(){
print("My name is $name");
 }
}

A family could be constructed by instantiating person objects from the class for each member of the family. Each person object would contain different data attributes since each person is unique.

void main(){
 Person p1 = Person("Praveen");
 p1.sayMyName();
 // prints `My name is Praveen`
}

This programming style is pervasive in popular programming languages such as Java, C++, Python, JavaScript and C# among others. By defining sets of classes that represent and encapsulate objects in a program, the classes can be organized into modules, improving the structure and organization of software programs.

Dart and OOP

Dart is an object-oriented programming language, and it supports all the concepts of object-oriented programming such as classes, object, inheritance, mixin, and abstract classes. As the name suggests, it focuses on the object and objects are the real-life entities. The Object-oriented programming approach is used to implement the concept like polymorphism, data-hiding, etc.

Class

We use class keyword to create a class in Dart. Instance Variables and Instance Methods live on the object. this keyword inside an instance method points to the object itself, hence we can access a property of the object. A Class is a user-defined data type that describes the characteristics and behavior of it. To get all properties of the class, we must create an object of that class. The syntax of the class is given below.

class ClassName {  
    <fields>  
    <getter/setter>     
    <constructor>  
    <functions>  
}

A class with a constructor

A constructor is an instance method that is invoked when an object is created from the class. This is a good place to initialize instance variables. A constructor function has the same name as the class.

// simple Persona class with constructor
class Person {
  
  // instance variables with `null` value
  String firstName, lastName;
  
  // instance variable with `null` value
  int age;

  // constructor function
  Person( String firstName, String lastName, [ int age = 18 ] ) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
  }

  // instance methods
  String getFullName() {
    return this.firstName + " " + this.lastName; // `this` points to the object itself
  }
}

Usage in main( )

/**
 * A constructor function of the class is implicitly called by the Dart when an object is created from it.
 * The default constructor method has the same name as the class.
 * 
 * A constructor function is generally used to initialize instance variables,
 * with values passed by the user while creating an object. 
 */
void main() {

  // create `p1` object
  Person p1 = new Person( 'Praveen', 'Varma' );
  print( "Full name: ${ p1.getFullName() }" );
  print( "Age: ${ p1.age }" );
}

In the above example, our constructor function Person needs two required positional arguments while age has a default 18 value. Dart provides a shorthand syntax for a constructor function to set instance variables.

Person( this.firstName, this.lastName, [ this.age = 18 ] );

This is the preferred way to write a constructor whose only job is to set initial values to the instance variables. Optional parameters must have a default value, else null will be set by the constructor even if the default value was given in instance variable declaration in the class.

Named constructors

Dart provides multiple constructors on a class. Apart from default constructors, other constructors must have a name. While creating an object from a class, we need to use the name named constructor.

/**
 * Dart supports having multiple constructor functions in a class.
 * But only one default constructor function is allowed which will be called implicitly.
 * Other constructor function must be registered with `MyClass.constructorName() {}` syntax.
 *
 * While creating an object, we can specify a constructor function using below syntax.
 * -> var obj = new MyClass.constructorName();
 *
 * When it comes to a constructor function whose only job is to initialize instance variables,
 * Dart provides an easier syntax to write default or named constructor function.
 * -> MyClass( this.instVarOne, this.instVarTwo, [ this.instVarThree = 'DefaultVal' ] );
 */
void main() {

  // create `p1` object
  Person p1 = Person.initWithUpperCase( 'Praveen', 'Varma' );
  print( "Full name: ${ p1.getFullName() }" );
}

// simple Person class with multiple constructor functions
class Person {
  
  // instance variables with `null` value
  String firstName, lastName;
  
  // instance variable with `null` value
  int age;

  // default constructor function
  Person( this.firstName, this.lastName, [ this.age = 18 ] );

  // named constructor: initWithUpperCase
  Person.initWithUpperCase( String firstName, String lastName, [ int age = 18 ] ) {
    this.firstName = firstName.toUpperCase();
    this.lastName = lastName.toUpperCase();
    this.age = age;
  }

  // instance methods
  String getFullName() {
    return this.firstName + " " + this.lastName;
  }
}

Apart from the name, there is no difference between default and named constructors. We can also use the shorthand instance variable assignment syntax for the named constructors as well.

Getters and setters

A combination of the getter and the setter methods are used to transform and/or encapsulate instance variables. In Dart, the getter is an instance method specified by get keyword. This method does not take any arguments, hence it does not contain parentheses (). While the setter method is specified by set keyword which receives value to be set as an argument.

/**
 * A getter function is called when it is accessed like an instance variable on the object.
 * A setter function is called when a value is assigned to object with the same variable name.
 *
 * We use `get` keyword to define a `getter` function.
 * Getter function does not take any arguments and returns a value.
 * Hence getter function does not have parentheses. They can also be fat-arrow functions for simplicity.
 * -> String get funcName { return_statement; }
 * -> String get funcName => return_statement;
 *
 * We use `set` keyword to define a `setter` function.
 * A setter function takes one arguments which is the value user is trying to set and returns no value (void).
 * -> void set funcName () { statements; }
 * -> void set funcName => statement;
 */
void main() {

  var p1 = Person( "Praveen Varma" );
  print( p1.fullName );

  // change name
  p1.fullName = "Alan Walker";
  print( p1.fullName );
}

class Person {
  
   // _ (underscore) prefix makes variables private to the library
  String _firstName, _lastName;
  
  // default constructor
  Person( String name ) {
    var nameParts = name.split(" "); // split name by a space
    this._firstName = nameParts[ 0 ];
    this._lastName = nameParts[ 1 ];
  }

  // getter function for `fullName` property
  String get fullName {
    return "${ this._firstName } ${ this._lastName }";
  }

  // setter function for `fullName` property
  void set fullName( String name ) {
    var nameParts = name.split(" "); // split name by a space
    this._firstName = nameParts[ 0 ];
    this._lastName = nameParts[ 1 ];
  }
}

In the above example, a getter can also be written using Fat Arrow function syntax like below but without any arguments.

String get fullName => "${ this._firstName } ${ this._lastName }";

Object

An object is a real-life entity such as a table, human, car, etc. The object has two characteristics - state and behavior. Let's take an example of a car which has a name, model name, price and behavior moving, stopping, etc. The object-oriented programming offers to identify the state and behavior of the object.

We can access the class properties by creating an object of that class. In Dart, The object can be created by using a new keyword followed by class name. The syntax is given below.

var objectName = new ClassName(<constructor_arguments>)  

Inheritance

Dart supports inheritance, which is used to create new classes from an existing class. The class that to be extended is called parent /superclass, and the newly created class is called child/subclass. Dart provides extends keyword to inherit the properties of parent class in child class. The syntax is given below.

class child_class_name extends parent_class_name

Example:

/**
 * A Dart class can inherit instance variables and instance methods of another class.
 * Using `extend` keyword, a class basically extends the properties and behaviours of another class.
 *
 * Apart from static properties and constructor functions, everything else is inherited.
 * A class that is inherting another class is called sub-class while the class that it inherits from is called `super-class`.
 *
 * When a class is inheried, Dart provides `super` keyword in `sub-class` to point to `super-class`.
 * `super` when used as a function calls the `default` constructor of the `super-class`.
 * We use `super.namedConstructor()` syntax to call a named constructor of the `super-class`.
 * A `sub-class` can access instance methods of the `super-class` using `super.methodName` syntax.
 *
 * When a `super-class` constructor function or method is called from the `sub-class`,
 * `this` object inside those functions is still pointing to the `sub-class` object.
 */
void main() {
  
  // create an Employee object
  var e = Employee( 'Ross', 'Geller', 1000 );
  print( 'Employee e : ${ e.firstName } ${ e.lastName }, salary: ${ e.salary }' );

  // create Student object
  var s = Student( 'Praveen', 'Varma', 418 );
  print( 'Student s : ${ s.firstName } ${ s.lastName }, salary: ${ s.score }%' );

  // create Person object
  var p = Person( 'Alan', 'Walker' );
  print( 'Person p : ${ p.firstName } ${ p.lastName }' );
  

}

// [super-class]
// Person class with basic details
class Person {
  String firstName, lastName;

  // default constructor
  Person( this.firstName, this.lastName );

  // named constructor
  Person.withUpperCase( String firstName, String lastName ) {
    this.firstName = firstName.toUpperCase();
    this.lastName = lastName.toUpperCase();
  }
}

// [sub-class]
// Employee class shares feature of Person class
class Employee extends Person {
  int salary;

  // default constructor calls the default constructor of `Person` class using `super` function
  // constructor of the `Person` is executed first before the constructor of the `Employee` class
  // arguments of the `sub-class` default constructor function are available in `super` call
  Employee( String firstName, String lastName, this.salary ): super( firstName, lastName );
}

// Student class shares feature of Person class
class Student extends Person {
  num score;

  // default constructor calls `Person.withUpperCase` constuctor function of `Person` class
  Student( String firstName, String lastName, num marks ): super.withUpperCase( firstName, lastName ) {
    this.score = num.parse( ( (marks / 500) * 100 ).toStringAsFixed( 6 ) ); // limit decimal places to 2
  }
}

When we inherit a class, its methods are also available on the sub-class. The super() function calls the super-class constructor and super keyword points to the super-class. We can also call super-class instance methods using super.superClassMethod() syntax.

This is an overview of Object Oriented Programming in Dart which is required for Flutter development, and ofcourse there is much more to learn, as OOP in general, itself is one of the vast and amazing concepts of programming!