Flutter Series - Dart for Flutter
Tags: flutter, dart
Published: 2020-12-24 20:49:28

Do you want to build cool mobile apps that you can show to the world? Do you find native mobile development difficult? Do you want to build feature-rich app quickly? 
If your answer to any of the above question is "yes", then don't worry **Flutter** is here for you. In this series, I will try to cover all the concepts of that you are required to know to build an app. Starting from the very basic ie. **Dart** language itself.

##Flutter
According to Google, creator of the framework:
`Flutter SDK is Google's UI toolkit for crafting beautiful, natively compiled applications for mobile, web, and desktop from a single codebase.`
In simple words, it is a framework to build applications with beautiful UI that provides native performance. You can build apps for different platform such as mobile, web or desktop(support for even more platform is coming soon) using **Flutter** and that too with a single codebase🚀.

In this series, there will be a number of posts. Each covering certain concepts which you should to know to take full advantage of **Flutter**. This series will be structured in a way so that you incremently learn new concepts and use it in your app. Today's post will be about the basics of **Dart**, language in which flutter apps are written. 

#### Let's get started

## Dart
**Dart** is an object oriented, high level language like python, java or c++. It is used to build mobile, web and desktop applications.

Before starting I want to point out that you can run all the below example in DartPad [Open DartPad](https://dartpad.dev/).
 Just make sure to write all your code in main() function.

So let's start with all the dart concepts you should know to start with flutter.

#### Variables
You can define variables in Dart using `var` keyword:
```dart
var name = 'Bob';
```
Dart compiler implicitly infer variable **name** to be of `String` datatype.

You can explicitly declare the datatype of a variable like this:
```dart
String name = 'Bob';
```
Dart is **statically-typed** language, meaning type checking is done at compile time. Whenever you try to assign variable of a particular datatype with data of different type, it will result in assignment error.

For example:
```dart
var name = 'Bob';
name = 1;
```
which result in:
```
Error: A value of type 'int' can't be assigned to a variable of type 'String'.
```
If a variable isn’t restricted to a single type, specify the Object or dynamic type:
```dart
dynamic name = 'Bob';
```
You can assign `name` variable to any other type at a later stage without any error like:
```dart
name = 123;
```
Uninitialized variables have an initial value of null.
```dart
int count;
print(count);
```
Output:
```dart
null
```
**Final and const**
If you want to make a variable read-only, declare it as final using `final` keyword. A final variable can be set only once.
Changing it again results in `Error: a final variable can only be set once.`
Example: 
```dart
final name = 'Bob'; // Without a type annotation
final String nickname = 'Bobby';
```
If you try to change **name** variable it will result in error.
```dart
name = 'Alice';
```
Output:
```
Error: Can't assign to the final variable 'name'.
```
**const** is used for variables that you want to be compile-time constants. const variables can only assigned with a expression which always returns a constant value.
Example:
```dart
const a = 2 * 3;
```
This is valid because `2 * 3` will always return a constant value ie. `6`.
But if you try to do something like this:
```dart
int a = 3;
const b = 2 * a;
```
Results in:
```
Error: Not a constant expression.
```
because value of variable `a` can change. So, this expression is not constant.
You can’t change the value of a const variable just like final variable:
```dart
const a = 1;
a = 2; // Error: Constant variables can't be assigned a value.
```
If you want to define a class variable(variable defined at class level) as `const`, define it as `static const`.

**Built-in types in dart**
The Dart language has special support for the following types:
- numbers
- strings
- booleans
- lists (also known as arrays)
- sets
- maps
- runes (for expressing Unicode characters in a string)
- symbols

**Numbers**
Dart numbers come in two types ie. **int** and **double**.
**int** represents integer value while **double** represents floating or decimal point values.
Example:
```dart
int count = 5;
double temperature = 25.5;
```
**String**
A Dart string is a sequence of characters. You can use either single or double quotes to create a string:
```dart
String s1 = 'Single quotes work well for string literals.';
String s2 = "Double quotes work just as well.";
String s3 = 'It\'s easy to escape the string delimiter.';
String s4 = "It's even easier to use the other delimiter.";
```
You can put the value of an expression inside a string by using `${expression}`. If the expression is an identifier, you can skip the {}.
Example:
```dart
double temperature = 16.0;
String s1 = 'Current temperature: ${(temperature * 9/5)+ 32}Fahrenheit';
int count = 10;
String s2 = 'Total count: $count';
```
You can concatenate strings using adjacent string literals or the + operator:
```dart
String s1 = 'String ' 'concatenation';
String s2 = 'String ' + 'concatenation';
print(s1);
print(s2);
```
Output:
```
String concatenation
String concatenation
```
String type have a number of in-built methods(functions) which you can use to modify the string.

**toUpperCase()**: Converts all characters in the string to upper case.

**toLowerCase()**: Converts all characters in the string to lower case.

**substring(int startIndex, [int? endIndex])**: Returns the substring of this string that extends from startIndex, inclusive, to endIndex, exclusive.

**contains(Pattern other, [int startIndex = 0])**: Returns true if the string contains a match of **other** string.

**compareTo(String other)**: Compares this string to other. It returns an integer based on comparison.

**indexOf(Pattern pattern, [int start = 0])**: Returns the position of the first match of pattern in this string, starting at start, inclusive.

**startsWith(Pattern pattern, [int index = 0])**: Returns true if this string starts with a match of pattern.

**endsWith(String other)**: Returns true if this string ends with other.

Example:
```dart
String s = 'Hello from dart world';
print(s.toUpperCase());
print(s.toLowerCase());
print(s.substring(0,5));
print(s.contains('dart'));
print(s.compareTo('Hello World'));
print(s.indexOf('dart'));
print(s.startsWith('Hello'));
print(s.endsWith('dart'));
```
Output:
```
HELLO FROM DART WORLD
hello from dart world
Hello
true
1
11
true
false
```
> Parameters in [] are optional.

There are many more in-built string methods in dart which I have not covered. You can find all the other methods [here](https://api.flutter.dev/flutter/dart-core/String-class.html).

**Conditional property access(?)**
if `s` is null or uninitialized, it will cause exception `Cannot read property of null`. To avoid that dart has introduced a conditional property access operator(?). You can use it like `String s2 = s?.toUpperCase();`.
So if `s` is **null**, then instead of resulting in exception `s2` will become **null** too.

**Null-aware operator(??)**
Null-aware operator(??) returns the expression on its left unless that expression’s value is null, in which case it evaluates and returns the expression on its right. In the above example if you want to assign a default value, use it with null-aware opeator(??):
```dart
String s;
String s2 = s?.toUpperCase() ?? "Empty String";
```
Output:
```
Empty String
```
**Booleans**
To represent boolean values, Dart has a type named `bool`. Only two values can be assigned to it `true` and `false`.
Example:
```dart
bool a = true;
String s = '';
bool isEmpty = s.isEmpty;
```
Unlike other languages, you cannot use non boolean values as a condition. `if(nonBooleanValue)` is invalid. 

**Lists**
In Dart, [List](https://api.dart.dev/stable/2.10.4/dart-core/List-class.html) is a collection of similar objects like arrays in other languages, so most people just call them lists.
Here is a simple list:
```dart
var list = [1, 2, 3];
```
Dart compiler infer type of `list` variable as `List<int>`.If you try to add non-integer objects to this list, the analyzer or runtime raises an error.
`Error: A value of type 'String' can't be assigned to a variable of type 'int'.`

You can define type of list explicitly as `List<type>`. For example list of int can be assigned a type `List<int>`.

List index starts at 0 and goes upto `length of the list - 1`.
You can use `.length` to find length of string.
Example:
```dart
List<int> list = [1, 2, 3, 4, 5];
print(list.length);
```
Output:
```
5
```
You can combine two lists using + operator:
```dart
List<int> list = [1, 2];
List<int> list1 = [3,4];
List<int> list2 = list + list1;
print(list2);
```
Output:
```
[1, 2, 3, 4]
```
Dart 2.3 introduced the **spread operator** (...).
For example, you can use the spread operator (...) to insert all the values of a list into another list:
```dart
var list = [1, 2, 3];
var list2 = [0, ...list];
print(list2);
```
Output:
```
[0, 1, 2, 3]
```
If `list` is **null**. It will cause exception. To avoid that you need to first check for **null** and then do the operation. This also can be achieved using a null-aware spread operator (...?):
```dart
var list;
var list2 = [0, ...?list];
print(list2);
```
Output:
```
[0]
```

Dart 2.3 also introduced collection if and collection for, which you can use to build collections using conditionals (if) and repetition (for).

```dart
bool promoActive = true;
var nav = [
  'Home',
  'Furniture',
  'Plants',
  if (promoActive) 'Outlet'
];
var listOfInts = [1, 2, 3];
var listOfStrings = [
  '#0',
  for (var i in listOfInts) '#$i'
];
print(nav);
print(listOfStrings);
```
Output
```
[Home, Furniture, Plants, Outlet]
[#0, #1, #2, #3]
```
You can use `.add()` method to add values at the end of list:
```dart
var list = [1, 2, 3];
var list2 = list.add(4);
print(list2);
```
Output:
```
[1, 2, 3, 4]
```

**Map**
A map is an object that associates keys and values. Both keys and values can be any type of object. Each key occurs only once, but you can use the same value multiple times. Dart support for maps is provided by map literals and the [Map](https://api.dart.dev/stable/2.10.4/dart-core/Map-class.html) type.
Example:
```dart
var elements = {
  // Key:    Value
  'first': 'hydrogen',
  'second': 'helium',
  'third': 'lithium'
};

var nobleGases = {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};
```
Dart infer type of `elements` as `Map<String, String>` and `nobleGases` as `Map<int,String>`.If you try to add the wrong type of value to either map, the analyzer or runtime raises an error. 
You can create the same objects using a Map constructor:
```dart
var elements = Map();
elements['first'] = 'hydrogen';
elements['second'] = 'helium';
elements['third'] = 'lithium';

var nobleGases = Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';
```
You can add new key-pair value to existing map as:
`elements['fourth'] = 'beryllium';`
You can retrieve a value from map using its key.
`String thirdElement = elements['third'];`
If you look for a key that isn’t in a map, you get a null in return:
`print(elements['fifth']);`
Output:
`null`
Use .length to get the number of key-value pairs in the map:
```dart
Map<int, String> numberToString = {
  1: 'one',
  2: 'two',
  3: 'three',
  4: 'four',
  5: 'five'
};
print(numberToString.length);
```
Output:
```
5
```
As of Dart 2.3, maps support spread operators (... and ...?) and collection if and for, just like lists do:
```dart
Map<String,int> map = {
  "violet": 1,
  "indigo": 2,
  "blue": 3
};
Map<String, int> map2 = {
  "green": 4,
  "yellow": 5,
  "orange": 6,
  "red": 7
};
Map<String, int> coloursOfRainbow = {...map, ...map2};
print(coloursOfRainbow);
```
Output:
```
{violet: 1, indigo: 2, blue: 3, green: 4, yellow: 5, orange: 6, red: 7}
```

**Conditional statement**
Dart support `if` and `else` statement, like any other high level language.
`if` statement contains a boolean expression. Followed by a block of statement which execute only when boolean condition is true. `if` block can be followed by an `else` block which only executes when boolean condition for `if` block is not satisfied. 
```dart 
if(boolean_expression){ 
   // statement(s) will execute if the Boolean expression is true. 
} else { 
   // statement(s) will execute if the Boolean expression is false. 
}
```

**Conditional expressions**
Dart lets you concisely evaluate expressions that might otherwise require if-else statements:
`condition ? expr1 : expr2`
If condition is true, evaluates expr1 (and returns its value); otherwise, evaluates and returns the value of expr2.
Example:
```dart
int age = 21;
String result = age >= 18 ? 'Eligible to vote' : 'Not Eligible to vote';
print(result);
```
Output:
```
Eligible to vote
```

**Loops**
Loops are used execute a series of statements repeatedly for a number of times.Dart support generally 4 types of looping statement.

**1. for loop**
The `for` loop is used when we want to execute block of code known times. In Dart, basic for loop is similar as it is in C. The loop takes a variable as iterator and assign it with an initial value, and iterate through the loop body as long as the test condition is **true**.
Syntax:
```dart
for(initialization;conditions;increment/decrement) {
  //body of loop
}
```
Example:
```dart
int count = 5;
for(int i = 1; i <= count; i++) {
  print('#$i');
}
```
Output:
```
#1
#2
#3
#4
#5
```
**2. for..in loop**
The `for..in` loop takes an object as iterator, and iterates through the elements one at a time in the sequence. In each iteration an element is fetched and stored in the loop variable. When there is no more element in iterator, loop is terminated.
Syntax:
```dart
for(var loopVariable in iterator){
  //body of loop
}
```
Example:
```dart
var list = [1, 2, 3, 4];
for(var element in list){
  print(element);
}
```
Output:
```
1
2
3
4
```

**3. while loop**
The `while` loop will execute a block of statement as long as a test expression is **true**.
Syntax:
```dart
while(condition) {
  //body of loop
}
```
Example:
```dart
int count = 4;
int i = 0;
while(i < count) {
  print(i);
  i++;
}
```
Output:
```
0
1
2
3
```

**4. do..while loop**
The `do…while` statement executes loop statements and then test the condition for next iteration and executes next only if condition is **true**.
```dart
int count = 4;
int i = 0;
do{
  print(i);
  i++;
}while(i < count);
```
Output:
```
0
1
2
3
```

**Functions**
Functions are block of reusable code that are used to peform a single related action. Function may or may not take inputs and may or may not return an output. Dart is a true object-oriented language, so even functions are objects and have a type, Function. This means that functions can be assigned to variables or passed as arguments to other functions. 

Syntax:
```dart
returntype name(parameters) {
  //body
}
```
Example:
```dart
bool isEligibleToVote(int age){
return age >= 18;
}
bool result = isEligibleToVote(16);
bool result2 = isEligibleToVote(21);
print(result);
print(result2);
```
Output:
```
false
true
```
If function does not return anything, use `void` as return type.
> Although Dart recommends type annotation, function would still work if you omit the return type.

For functions that contain just one expression, you can use a shorthand syntax:
`bool isEligibleToVote(int age) => age >= 18;` 
is equivalent to
```dart
bool isEligibleToVote(int age) {
  return age >= 18;
}
```

**Parameters**
A function can have any number of required positional parameters. These can be followed either by named parameters or by optional positional parameters (but not both).
Positional paramters Example:
```dart
int sum(int a, int b,[int c]) {
  return a + b + (c ?? 0);
}
print(sum(1, 3, 5));
print(sum(1, 3));
```
Output:
```
9
4
```
Here `a` and `b` are required positional parameters while c in optional positional parameters.
[] represents optional positional parameters.

**Named parameters**
Named parameters are optional unless they’re specifically marked as required.
When defining a function use{type1 param1, type2 param2,...} to specify named parameters.
When calling a function, you can specify named parameters using paramName: value. For example:
```dart
void display({String name, int age}) {
 print('$name is $age years old.');
}
display(name: 'Mark', age: 16);
```
Output:
```
Mark is 18 years old.
```
Although named parameters are a kind of optional parameter, you can annotate them with [@required](https://pub.dev/documentation/meta/latest/meta/required-constant.html) to indicate that the parameter is mandatory — that users must provide a value for the parameter. For example:
```dart
void display({String name, @required int age}) {
 print('$name is $age years old.');
}
```
To use the `@required` annotation, depend on the meta package and import `package:meta/meta.dart`.

**Default parameters value**
Your function can use `=` to define default values for both named and positional parameters. The default values must be compile-time constants. If no default value is provided, the default value is `null`.
`void enableFlags({bool bold = false, bool hidden = false}) {...}`

```dart
String say(String from, String msg,
    [String device = 'carrier pigeon']) {
  var result = '$from says $msg with a $device';
  return result;
}
print(say('Bob', 'Hello'));
```
Output:
```
Bob says Hello with a carrier pigeon.
```

**The main() function**
Every app must have a top-level `main()` function, which serves as the entrypoint to the app. The `main()` function returns void and has an optional `List<String>` parameter for arguments.
Example:
```dart
void main() {
  print('Hello world from main function.');
}
```
Output:
```
Hello world from main function.
```

**Anonymous functions**
You can also create a nameless function called an **anonymous function**. An anonymous function looks similar to a named function— zero or more parameters, separated by commas and optional type annotations, between parentheses.
`([[Type] param1[, …]]) {
  //body of function
};`
Example:
```dart
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});
```
Output:
```
0: apples
1: bananas
2: oranges
```
Here 
`(item) {
  print('${list.indexOf(item)}: $item');
})` is anonymous function. It is executed for each element of the list. Anonymous function can also be assigned to a variable.

**List.forEach() function**
The `forEach()` method receives a function as an argument and executes it once for each array element. It returns `null`.
Example:
```dart
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});
```
Output:
```
0: apples
1: bananas
2: oranges
```

**List.map() function**
The `map` method receives a function as a parameter. Then it applies it on each element and returns an new [Iterable](https://dart.dev/codelabs/iterables) populated with the results of calling the provided function. This Iterable can be converted to list by calling `toList()` method on it.
Example:
```dart
var list = [1, 2, 3];
var squaredList = list.map((i) {
  return i * i;
}).toList();
print(squaredList);
```
Output:
```
[1, 4, 9]
```

**Cascade notation (..)**
Cascades `..` allow you to make a sequence of operations on the same object. In addition to function calls, you can also access fields on that same object. This often saves you the step of creating a temporary variable and allows you to write more fluid code.
Example:
```dart
var list = [1, 2, 3, 4];
list
  ..add(5)
  ..add(6)
  ..first = 2;
print(list);
```
Output:
```
[2, 2, 3, 4, 5, 6]
```
Here `list.add(5)` method is applied on list which adds `5` at the end of list, then `list.add(6)` method is applied on list which adds `6` at the end of list and at last `list.first = 2` which makes first element as `2`.
>`list.first` is used to retrieve or change first element of list. Simliarly, `list.last` is used to retrieve or change last element of list.

**Classes**
Dart is an object-oriented language with classes and mixin-based inheritance. Every object is an instance of a class and all classes descend from [Object](https://api.dart.dev/stable/2.10.4/dart-core/Object-class.html).
A class can be created using `class` keyword.
Example :
```dart 
class Point {
...
}
```
**Instance variables**
Here how declare instance variable:
```dart
class Point {
  double x;
  double y;
}
```
>All uninitialized instance variables have the value `null`.

**Constructors**
Declare a constructor by creating a function with the same name as its class. Constructors are generally used to initialize instance variable of a class:
```dart
class Point {
  double x, y;

  Point(double x, double y) {
    // There's a better way to do this, stay tuned.
    this.x = x;
    this.y = y;
  }
}
```
> The `this` keyowrd is used to refer to current instance of the class.

The pattern of assigning a constructor argument to an instance variable is so common, Dart has syntactic sugar to make it easy:
```dart
class Point {
  double x, y;
  Point(this.x, this.y);
}
```

**Named constructor**
Use a named constructor to implement multiple constructors for a class or to provide extra clarity:
```dart 
class Point {
  double x, y;

  Point(this.x, this.y);

  // Named constructor
  Point.origin() {
    x = 0;
    y = 0;
  }
}
```

**Initializer list**
You can initialize instance variables before the constructor body runs. Use `:` after constructor parameter list to use initializer and separate initializers with commas.
Example:
```dart 
class Point {
  double x, y;

  Point(this.x, this.y);

  // Named constructor
  Point.origin() {
    x = 0;
    y = 0;
  }

  //Initializer list
  Point.fromJson(Map<String, dynamic> parsedJson) :
    this.x = parsedJson['x']
    this.y = parsedJson['y'] {
    //body of the constructor
  }
}
```

**Methods**
Functions defined within class which represents a specify purpose are called **methods**. Methods of a class have access to instance variables and this.
The distanceTo() method in the following sample is an example of an instance method:
```dart
import 'dart:math';

class Point {
  double x, y;

  Point(this.x, this.y);

  double distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    return sqrt(dx * dx + dy * dy);
  }
}
```
> `import` keyword is used to import objects and methods from other packages like built-in packages or from other files. In this example we are importing `sqrt()` function from `dart:math` package.

**Instance of a class**
Instance of a class can be created by calling constructor the class. This instance contains property and methods of the class. Those property and methods can be accessed using `.` operator.
Example:
```dart
import 'dart:math';

class Point {
  double x, y;

  Point(this.x, this.y);

  double distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    return sqrt(dx * dx + dy * dy);
  }
}
void main() {
  Point point = Point(2.0, 4.0);
  Point point2 = Point(2.0, 6.0);
  print(point2.x);
  print(point2.y);
  print(point2.distanceTo(point));
}
```
Output:
```
2.0
6.0
2.0
```
> Unlike some other languages, `new` keyword is optional while creating instance of a class.

**Static variables**
Static variables (class variables) are useful for class-wide state and constants:
```dart
class Point {
  static const originX= 0.0;
  static const originY = 0.0;
  //...
}
```

**Static methods**
Static methods (class methods) don’t operate on an instance, and thus don’t have access to `this` ie. they can access current instance properties. They do, however, have access to static variables. They can be accessed directly from class without the need of creating an instance.
Example:
```dart
class Point {
  double x, y;
  static const originX= 0.0;
  static const originY = 0.0;

  Point(this.x, this.y);

  static String currentOrigin() {
    return 'Current origin: ($originX, $originY)';
  }
}
void main() {
  print(Point.currentOrigin());
}
```
Output:
```
Current origin: (0.0, 0.0)
```

**Public and Private members**
Properties and methods of a class are public, meaning they can be accessed from outside class using instance of the class.
Example:
```dart
class Point {
  double x, y;

  Point(this.x, this.y);
}
void main() {
  Point point = Point(3.0, 6.0);
  print(point.x);
  print(point.y);
}
```
Output:
```
3.0
6.0
```
If you want to make an property or method as private, use `_` before property or method name. Unlike other languages, here private doesn't mean it is available only to the class it is in, private means it is available in the library.

**Getters and setters**
Getters and setters are special methods that provide read and write access to an object’s properties. Getters are used to read an object's properties while setters are set an object property. Each instance variable has an implicit getter, plus a setter if appropriate. You can create additional properties by implementing getters and setters, using the `get` and `set` keywords:
```dart
class Point {
  double _x, _y;

  Point(this._x, this._y);
  
  double get xCoordinate => this._x;
  double get yCoordinate => this._y;

  void set XCoordinate(double x) => this._x = x;
  void set YCoordinate(double y) => this._y = y;
}
void main() {
  Point point = Point(3.0, 6.0);
  point.XCoordinate = 4.0;
  point.YCoordinate = 8.0;
  print(point.xCoordinate);
  print(point.yCoordinate);
}
```
Output:
```
4.0
8.0
```

**Inheritance**
Like any other object oriented language, Dart supports **inheritance**. A class can inherits from other class. Class which inherits are called child class or subclass and class which is being inherited is called parent or super class. Subclass inherits all the property of the parent class.
Inheritance is achieved using `extends` keyword.
In the coming flutter tutorials, you will quite often come across example like this:
```dart
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
  ...
}
```
Here `Home` class is inheriting properties and methods from `StatelessWidget` class defined in `package:flutter/material.dart` package.

**Method overriding**
You can override method defined in parent class by defining method with same name and parameter list as the method in parent class. You can use the `@override` annotation to indicate that you are intentionally overriding a member:
```dart
import 'package:flutter/material.dart';
class Home extends StatelessWidget
{
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}
```
Here we are overriding `build(BuildContext context)` method defined in parent class ie. `StatelessWidget` class.

**Enumerated types**
Enumerated types, often called enumerations or enums, are a special kind of class used to represent a fixed number of constant values.

**Using enums**
Declare an enumerated type using the `enum` keyword:

`enum Color { red, green, blue }`
Each value in an enum has an `index` getter, which returns the zero-based position of the value in the enum declaration. For example, the first value has index 0, and the second value has index 1.
To get a list of all of the `values` in the enum, use the enum’s values constant.
Example:
```dart
enum Color { red, green, blue}
void main() {
  List<Color> colors = Color.values;
  print(colors);
  print(Color.red.index);
  print(Color.green.index);
  print(Color.blue.index);
}
```
Output:
```
[Color.red, Color.green, Color.blue]
0
1
2
```

**Assert**
During development, use an assert statement — assert(condition, optionalMessage); — to disrupt normal execution if a boolean condition is false.
```dart
String text;
assert(text != null, 'Text is null');
```
Output:
```
Failed assertion: line 3 pos 10: 'text != null': Text is null
```
>In production code, assertions are ignored, and the arguments to assert aren’t evaluated.

**Exception**
Your Dart code can throw and catch exceptions. Exceptions are errors indicating that something unexpected happened. You can raise manually using `throw` keyword.
`throw Exception('this is a sample exception')`

**Exception handling**
Exception handling is an important part of development because unhadled exception can break an app. Exception handling allows you to handled those exception before any harm is done.
In Dart, exception handling is done using `try..catch` statement.

**try statement**
In `try` block, we generally put lines of code which may result in exception.

**catch statement**
Catching, or capturing, an exception stops the exception from propagating (unless you rethrow the exception). Catching an exception gives you a chance to handle it:
```dart
int x = 8;
int y = 0;
try {
  res = x ~/ y; 
  print(res);
} on IntegerDivisionByZeroException { 
  print('Cannot divide by zero'); 
} catch (e) {
  print('Other types of exception');
}
```
Output:
```
Cannot divide by zero
```
>use `on` keyword to accept exception of a specific type while `catch (e)` without any specific exception catches all types of exception.

**Finally**
To ensure that some code runs whether or not an exception is thrown, use a finally clause.  
```dart
try {
  //Code that may result in exception.
} catch (e) {
  // Handle the exception first.
} finally {
  // Then clean up.
  //This code always runs irrespective of whether any exception occured or not.
}
```
>We are going to use exception handling a lot when we will be trying fetch data from internet or when trying to access a device functionality like gps or camera.

**Reference**
Most of the example in the tutorial can be found on [Official Dart Language Tour] page(https://dart.dev/guides/language/language-tour). You can refer that page for an even more in-depth tutorial of the language.

## Conclusion
So this was a comprehensive tour of the dart language. I tried to cover up most of the concepts, we will be frequently using in the series. This tutorial can also be used as a guide or reference for the future.
This was quite a long tutorial, so in inadvertently some errors or typos may have crept in the tutorial. I would very much appreciate, if someone can point out those errors and typos. 

So you all in the next tutorial which will be
>Basics of Flutter

Thank you and Goodbye👋👋.
![Goodbye](https://media.giphy.com/media/KctrWMQ7u9D2du0YmD/giphy.gif)