Dart

Introduction to Dart

Dart is an object-oriented programming language known for its versatility, asynchronous support, and association with Flutter for cross-platform app development.

  1. Origin and Purpose:
    • Dart is an object-oriented programming language developed by Google, first introduced in 2011.
    • It’s designed to be versatile and applicable for various types of software development.
  2. General-purpose:
    • Dart serves as a general-purpose language, used for web, mobile, and server applications.
    • This versatility makes it suitable for creating a wide range of software solutions.
  3. Client-side and Server-side:
    • One of Dart’s standout features is its support for both client-side and server-side programming.
    • This allows developers to use the same language for both front-end and back-end development.
  4. Asynchronous Programming:
    • Dart supports asynchronous programming through the use of “async” and “await” keywords.
    • This is crucial for building responsive and real-time applications, like web and mobile apps.
  5. Rich Libraries:
    • Dart comes with a rich set of libraries catering to different development areas, such as web, mobile, and server.
    • These libraries provide tools and functionalities that accelerate application development.
  6. Flutter Framework:
    • Dart is most notably associated with Flutter, Google’s UI framework for creating natively compiled applications.
    • Flutter uses Dart as its primary programming language, allowing developers to build cross-platform apps.
  7. Cross-platform Development:
    • Dart’s usage in Flutter makes it a popular choice for building apps that work on mobile, web, and desktop platforms.
    • This approach simplifies maintaining a single codebase for multiple platforms.
  8. Strong Tooling:
    • Dart offers strong tooling and development environments, enhancing productivity for programmers.
    • Tools like the Dart SDK and Dart DevTools aid in debugging, profiling, and code analysis.
  9. Open-source:
    • Dart is open-source, allowing the community to contribute, improve, and expand its features.
  10. Growing Ecosystem:
    • Over time, the Dart ecosystem has grown, with a supportive community, third-party libraries, and resources for learning.
  11. Modern Language Features:
    • Dart incorporates modern language features like strong typing, generics, and classes.
    • It aims to make code cleaner, more readable, and less error-prone.

In essence, Dart is a versatile programming language that supports client-side and server-side development, asynchronous programming, and has gained prominence through its association with the Flutter framework for cross-platform application development.

Installation

  1. Install Dart SDK from here
  2. Install any Editor like IntelliJ IDEA or VsCode

Basics

First Dart Program

void main() {
  print('Hello, World!');
}

Data Types

In Dart language, the below data types are used:

DescriptionKeywordDescription
Numbersint, double, numUsed to to store numerical value
StringsStringUsed to to store text value
BooleansboolUsed to store  Boolean true and false value
ListsListUsed to store an ordered group of items
MapsMapUsed to store a set of values as key-value pair
SetsSetUsed to store unordered list of unique values of same types
RunesrunesUsed to store Unicode values of String
NullnullIt represents null value

Operators

Operators in programming are symbols or keywords that represent a specific operation or computation to be performed on one or more operands (values, variables, or expressions). There are various types of operators in programming, such as:

  • Arithmetic Operators
  • Increment and Decrement Operators
  • Assignment Operators
  • Relational Operators
  • Logical Operators
  • Type Test Operators

Arithmetic Operators

SymbolOperator NameDescription
+AdditionFor adding two operands
-SubtractionFor subtracting two operands
-exprUnary MinusFor reversing the sign of the expression
*MultiplicationFor multiplying two operands
/DivisionFor dividing two operands and give output in double
~/Integer DivisionFor dividing two operands and give output in integer
%ModulusRemainder After Integer Division

Increment and Decrement Operators

Operator SymbolOperator NameDescription
++varPre IncrementIncrease Value By 1. var = var + 1 Expression value is var+1
--varPre DecrementDecrease Value By 1. var = var – 1 Expression value is var-1
var++Post IncrementIncrease Value By 1. var = var + 1 Expression value is var
var--Post DecrementDecrease Value By 1. var = var – 1 Expression value is var

Assignment Operators

Operator TypeDescription
=Assign a value to a variable
+=Adds a value to a variable
-=Reduces a value to a variable
*=Multiply value to a variable
/=Divided value by a variable

Relational Operators

Operator SymbolOperator NameDescription
>Greater thanUsed to check which operand is bigger and gives result as boolean
<Less thanUsed to check which operand is smaller and gives result as boolean
>=Greater than or equal toUsed to check which operand is bigger or equal and gives result as boolean
<=Less than or equal toUsed to check which operand is smaller or equal and gives result as boolean
==Equal toUsed to check operands are equal to each other and gives result as boolean
!=Not equal toUsed to check operand are not equal to each other and gives result as boolean

Logical Operators

Operator TypeDescription
&&This is ‘and’, return true if all conditions are true
||This is ‘or’. Return true if one of the conditions is true
!This is ’not’. return false if the result is true and vice versa

Type Test Operators

Operator SymbolOperator NameDescription
isisGives boolean value true if the object has a specific type
is!is notGives boolean value false if the object has a specific type

Conditions and Loops

User Input

In Dart, to take input from stdin object has to use and will have to import dart:io library. A sample example is given for taking input from user.

import 'dart:io';

void main() {
  stdout.write("Enter your name: "); // Display a prompt
  String name = stdin.readLineSync(); // Read user input
  stdout.write("Enter your age: ");
  int age = int.parse(
      stdin.readLineSync()!); // Read and parse user input as an integer
  print("Hello, $name! You are $age years old.");
}

By default user input is string type. If you want to read other data types (like integers or double numbers), you’ll need to parse the input accordingly.

Functions

Dart functions are blocks of code that perform specific tasks or operations, allowing you to organize and reuse code in a structured manner. They can take input values (parameters), process them, and produce output values (return).

int addNumbers(int a, int b) {
  return a + b;
}

void main() {
  int num1 = 5;
  int num2 = 7;

  int sum = addNumbers(num1, num2); // Calling the function

  print("Total: $sum");
}

If want to pass optional parameter just enclose the optional variables in square brackets [].

int addNumbers(int a, int b, [int? unit]) {
  if (unit != null)
    return (a + b) * unit;
  else
    return (a + b);
}

void main() {
  int num1 = 5;
  int num2 = 7;
  int unit = 50;
  int sum1 = addNumbers(num1, num2); // Calling the function
  int sum2 = addNumbers(
      num1, num2, unit); // Calling the function with optional parameter
  print("Total cost for single unit: $sum1");
  print("Total cost for multiple unit: $sum2");
}

Null Safety

Null safety is a feature in Dart that helps developers avoid null pointer exceptions by providing stronger type checks at compile time. It was introduced in Dart 2.12, and it allows developers to write safer and more reliable code.

Null safety introduces two new types of variables: nullable and non-nullable. A nullable variable can be assigned a value or null, while a non-nullable variable can only be assigned a value.

In Dart’s null safety feature, there are two new symbols that are introduced to represent nullable and non-nullable variables. These symbols are:

  • ? – The ? symbol indicates that a variable is nullable. This symbol is placed after the type of the variable. For example, String? indicates that the variable can hold a String value or null.
  • ! – The ! symbol indicates that a variable is non-nullable. This symbol is placed after the type of the variable. For example, String! indicates that the variable can only hold a non-null String value.

The below example will clear you about Null Safety.

// Nullable variable
String? nullableString = "Hello World";
nullableString = null; // Valid assignment

// Non-nullable variable
String nonNullableString = "Hello World";
nonNullableString = null; // Invalid assignment, will generate a compile-time error

In this example, nullableString is a nullable variable that can be assigned a value or null. The ? symbol after the String type indicates that the variable is nullable. In the first assignment, nullableString is assigned the value "Hello World", which is valid. In the second assignment, nullableString is assigned null, which is also valid.

On the other hand, nonNullableString is a non-nullable variable that can only be assigned a value. It does not have the ? symbol after the String type, indicating that the variable is non-nullable. In the first assignment, nonNullableString is assigned the value "Hello World", which is valid. In the second assignment, nonNullableString is assigned null, which is not valid and will generate a compile-time error.

Late Keyword In Dart

late keyword is used to declare a variable which is non-nullable and will be initialized at a later time.

late int age;
void main() {
  // assigning value to late variable
  age = 30;
  print(age);
}

OOP

OOP in Dart

Dart is a modern programming language that supports OOP concepts. Some of the key features of OOP in Dart are:

  1. Classes: Classes are the fundamental building blocks of OOP in Dart. A class can have fields (data members) and methods (functions), and it can also extend other classes to inherit their properties and behavior.
  2. Inheritance: In Dart, a class can inherit properties and behavior from another class using the ‘extends’ keyword. This allows for code reuse and promotes a more modular design.
  3. Encapsulation: Encapsulation is the practice of hiding implementation details of an object and only exposing the necessary information to other objects. In Dart, you can use the ‘getters’ and ‘setters’ to control access to class fields.
  4. Polymorphism: Polymorphism is the concept of treating objects of different types as if they are of the same type. In Dart, you can achieve polymorphism through inheritance and interface implementation.
  5. Interfaces: An interface is a contract that defines a set of methods that a class must implement. In Dart, you can define an interface using the ‘implements’ keyword.

Overall, OOP in Dart provides a powerful set of tools for creating modular, maintainable, and extensible software systems. By leveraging these concepts, developers can build software that is more robust, scalable, and easy to understand and modify over time.

Class & Object in Dart

In Dart, a class is a blueprint or template for creating objects that have properties and behavior. To define a class in Dart, you use the class keyword followed by the class name, as shown below:

class MyClass {
  // class members go here
}

Inside the curly braces of the class definition, you can define properties and methods that are associated with the class. For example, you can define a class with a single property and method like this:

class Person {
  String name;

  void sayHello() {
    print('Hello, my name is $name');
  }
}

In this example, the Person class has a single property called name, which is a string, and a method called sayHello() that prints a message to the console using the name property.

To create an instance of a class, you use the new keyword followed by the class name and any constructor arguments, if necessary, like this:

var person = new Person();
person.name = 'John';
person.sayHello();  // prints "Hello, my name is John"

In this example, we create a new Person object and set its name property to “John”. We then call the sayHello() method, which prints a message to the console using the name property.

Classes in Dart can also inherit from other classes using the extends keyword. This allows you to create more specialized classes that share properties and behavior with their parent class. You can also define interfaces using the implements keyword, which allows you to specify a set of methods that a class must implement.

Constructor in Dart

In Dart, a constructor is a special method that is used to create and initialize objects of a class. The constructor has the same name as the class and can optionally take in parameters that are used to initialize the object’s properties.

There are two types of constructors in Dart: named constructors and default constructors.

A default constructor is a constructor with no parameters. If you do not define a constructor for a class, Dart provides a default constructor that takes no arguments.

A named constructor is a constructor that has a name other than the class name. Named constructors are useful when you want to provide multiple ways to create objects of a class, or when you want to create objects using different initialization logic.

A sample Code with Class, Object and Constructor is given below:

class Person {
  String name;
  int age;
  
  // Default constructor
  Person(this.name, this.age);
  
  // Named constructor
  Person.fromBirthYear(String name, int birthYear) {
    this.name = name;
    this.age = DateTime.now().year - birthYear;
  }
  
  void sayHello() {
    print("Hello, my name is $name and I'm $age years old.");
  }
}

void main() {
  var person1 = new Person("John", 30);
  var person2 = new Person.fromBirthYear("Jane", 1990);
  
  person1.sayHello();
  person2.sayHello();
}

In this example, we define a Person class with two constructors. The first constructor is a default constructor that takes in two parameters, name and age, and uses them to initialize the object’s properties.

The second constructor is a named constructor called fromBirthYear that takes in a name parameter and a birthYear parameter. This constructor uses the DateTime.now().year property to calculate the person’s age based on their birth year and sets the name and age properties accordingly.

In the main() function, we create two Person objects, one using the default constructor and the other using the named constructor. We then call the sayHello() method on each object to print a message to the console.

By using constructors in Dart, you can create objects with the appropriate initial state and behavior, which makes your code more flexible and easier to maintain.

Encapsulation In Dart

Encapsulation is one of the fundamental principles of object-oriented programming (OOP). It refers to the practice of hiding the implementation details of an object from the outside world and providing a public interface for accessing and manipulating the object’s state.

In Dart, encapsulation can be achieved by using private and public members. Private members are those that can only be accessed within the class that defines them, while public members can be accessed from outside the class.

To define a private member in Dart, you can prefix its name with an underscore (_) character. For example, consider the following Person class:

class Person {
  String _name;
  
  Person(String name) {
    _name = name;
  }
  
  void sayHello() {
    print("Hello, my name is $_name.");
  }
}

In this example, we define a private property called _name that can only be accessed within the Person class. We also define a constructor that takes in a name parameter and sets the _name property. Finally, we define a sayHello() method that uses the _name property to print a message to the console.

By making the _name property private, we prevent external code from directly accessing or modifying it. Instead, we provide a public method (sayHello()) that can be used to access the property indirectly. This way, we can control how the property is accessed and ensure that the object’s state remains consistent.

In summary, encapsulation is an important concept in Dart that helps to improve code organization, readability, and maintainability. By hiding implementation details and providing a public interface, you can create more robust and flexible classes that can be used in a variety of contexts.

Inheritance In Dart

Inheritance is a fundamental concept in object-oriented programming (OOP) that allows you to create new classes based on existing classes. In Dart, you can create a subclass that inherits properties and methods from a superclass by using the extends keyword.

Here is an example of how to create a class hierarchy using inheritance:

class Animal {
  String name;
  
  Animal(String name) {
    this.name = name;
  }
  
  void makeSound() {
    print("Unknown sound");
  }
}

class Dog extends Animal {
  Dog(String name) : super(name);
  
  void makeSound() {
    print("Woof!");
  }
}

void main() {
  var animal = new Animal("Unknown animal");
  var dog = new Dog("Fido");
  
  animal.makeSound();
  dog.makeSound();
}

In this example, we define a Animal class with a name property and a makeSound() method that prints “Unknown sound” to the console. We then define a Dog subclass that extends the Animal class and overrides the makeSound() method to print “Woof!” to the console.

In the main() function, we create an Animal object and a Dog object, and call the makeSound() method on each object. When we call animal.makeSound(), the Animal class’s implementation of makeSound() is executed and “Unknown sound” is printed to the console. When we call dog.makeSound(), the Dog class’s implementation of makeSound() is executed and “Woof!” is printed to the console.

By using inheritance in Dart, you can create more specialized classes that share common functionality with other classes. This can help to improve code organization, reduce redundancy, and make your code easier to maintain.

Enum In Dart

In Dart, an enum (short for “enumeration”) is a special data type that represents a fixed set of constant values.

enum Color { red, green, blue }

void main() {
  var set_color = Color.red;
  switch (set_color) {
    case Color.red:
      print("You have set red color.");
      break;
    case Color.green:
      print("You have set green color.");
      break;
    case Color.blue:
      print("You have set blue color.");
      break;
  }
}

Factory Constructor

In Dart, a factory constructor is a special type of constructor that is used to create objects differently from the usual constructor mechanism. Other generative constructors only create an instance of the class. But, the factory constructor can return an instance of the class or even subclass.

class MyClass {
  final int value;

  // Regular constructor
  MyClass(this.value);

  // Factory constructor
  factory MyClass.custom(int customValue) {
    return MyClass(customValue);
  }
}

void main() {
  final myObj1 = MyClass(20); // Using the regular constructor
  final myObj2 = MyClass.custom(400); // Using the factory constructor

  print("From regular constructor: ${myObj1.value}"); 
  print("From regular constructor: ${myObj2.value}"); 
}
show Output

From regular constructor: 20
From regular constructor: 400

Some key points about factory constructor:

  • factory keyword is used to define a factory constructor.
  • Factory constructor must return an instance of the class or sub-class.
  • You can’t use this keyword inside factory constructor.
  • It can’t access instance members of the class.
  • It returns an instance of the same class or sub-class.

Asynchronous Programming

Asynchronous programming: Asynchronous programming is like being able to start a task that might take a while, but instead of waiting around for it to finish, your program can go on doing other things and come back to check on the task later.

most of the modern programming languages support asynchronous programming like Dart, Python, Go, JavaScript, Java, Swift, Kotlin, Rust, C#, Ruby etc.

We will discuss about some keys which are used in asynchronous programming in dart which are:

  1. Futures: A Future represents a value that may be available at some point in the future, but the program will be continue with the rest statement.
  2. Async and Await: The async keyword is used to declare an asynchronous function, and the await keyword is used to pause the execution of a function until the awaited Future completes.
  3. Streams: Streams are a way to handle sequences of asynchronous events.

The below code is showing that the task will be completed without waiting for Task 3 since it will need 4 second.

void main() async {
  
  print("Task 1");
  print("Task 2");
  // This readData() function will sleep for 4 seconds but the rest statement will be continued.
  Future readData() async {
    return Future.delayed(Duration(seconds: 4), () => print('Task 3'));
  }
  readData();
  print("Task 4");
  print("Task 5");
}
Show Output

Task 1
Task 2
Task 4
Task 5
Task 3

The below code is showing that the task will be completed orderly and waiting for 4 second to complete Task 3 then rest tasks will be completed.

void main() async {
  // This function will sleep for 4 seconds.
  print("Task 1");
  print("Task 2");
  Future<String> readData() async {
    return Future.delayed(Duration(seconds: 4), () => 'Task 3');
  }

  // This code will wait for the `readData()` function to complete before
  // printing the result.
  String data = await readData();
  print(data);
  print("Task 4");
  print("Task 5");
}
Show Output

Task 1
Task 2
Task 3
Task 4
Task 5

Error Handling

Error handling in Dart involves managing exceptions and errors that may occur during program execution. Dart provides mechanisms to handle errors and exceptions through its try-catch-finally syntax. Here’s how error handling works in Dart:

void main() {
  try {
    divide(10,0);
  } catch (e) {
    print("Caught an exception: $e");
  }
}
double divide(int a, int b) {
  if (b == 0) {
    throw ArgumentError("Cannot divide by zero");
  }
  return a / b;
}

File Handling

Next Steps