-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Welcome to the dart-getting-started wiki!
Dart is an open-source, scalable programming language, with robust libraries and runtimes, for building web, server, and mobile apps. This project enables you to get started with the dart
programming.
Dart is an object-oriented language with C-style syntax which can optionally trans compile into JavaScript. It supports a varied range of programming aids like interfaces, classes, collections, generics, and optional typing.
Dart can be extensively used to create single-page applications. Single-page applications apply only to websites and web applications. Single-page applications enable navigation between different screens of the website without loading a different webpage in the browser. A classic example is GMail
─ when you click on a message in your inbox, browser stays on the same webpage, but JavaScript code hides the inbox and brings the message body on screen.
Google has released a special build of Chromium
– the Dart VM
. Using Dartium means you don’t have to compile your code to JavaScript until you’re ready to test on other browsers.
The following table compares the features of Dart and JavaScript.
Feature | Dart |JavaScript
Type system | Optional, dynamic | Weak, dynamic
Classes | Yes, single inheritance | Prototypical
Interfaces | Yes, multiple interfaces | No
Concurrency | Yes, with isolates | Yes, with HTML5 web workers
This tutorial provides a basic level understanding of the Dart programming language.
This chapter discusses setting up the execution environment for Dart on the Windows platform.
You may test your scripts online by using the online editor at https://dartpad.dartlang.org/. The Dart Editor executes the script and displays both HTML as well as console output. The online editor is shipped with a set of preset code samples.
A screenshot of the Dartpad
editor is given below −
Dartpad also enables to code in a more restrictive fashion. This can be achieved by checking the Strong mode option on the bottom right of the editor. Strong mode helps with −
- Stronger static and dynamic checking
- Idiomatic JavaScript code generation for better interoperability.
You may try the following example using Dartpad
void main() {
print('hello world');
}
The code will display the following output
hello world
In this section, let us see how to set up the local environment.
Examples of a few editors include Windows Notepad, Notepad++, Emacs, vim or vi, etc. Editors may vary from one Operating System to another. The source files are typically named with the extension ".dart".
The current stable version of Dart is 1.21.0
. The ``dart sdk
can be downloaded from −
A screenshot of the Dart SDK installation is given below −
On completion of the SDK installation, set the PATH environment variable to −
<dart
-sdk-path>\bin
To verify if Dart has been successfully installed, open the command prompt and enter the following command −
Dart
If installation is successful, it will show the dart
runtime.
A plethora of IDEs support scripting in Dart. Examples include Eclipse, IntelliJ
, and WebStorm
from Jet brains.
Given below are the steps for configuring the Dart environment using WebStrom IDE
.
The installation file for WebStorm can be downloaded from https://www.jetbrains.com/webstorm/download/#section=windows-version.
The WebStorm installation file is available for Mac OS, Windows and Linux.
After downloading the installation files, follow the steps given below −
-
Install the Dart SDK: Refer to the steps listed above
-
Create a new Dart project and configure Dart support
-
To create a new Dart project,
-
Click
Create New Project
from the Welcome Screen -
In the next dialog box, click
Dart
-
-
If there is no value specified for the
Dart SDK
path, then provide the SDK path. For example, the SDK path may be<
dartinstallation directory>/
dart/dartsdk
.
To add a Dart file to the Project −
- Right-click on the Project
- New → Dart File
- Enter the name of the Dart Script
A screenshot of the WebStorm Editor is given below −
The dart2js
tool compiles Dart code to JavaScript. Compiling Dart code to JS enables running the Dart script on browsers that do not support the Dart VM.
The dart2js tool is shipped as a part of the Dart SDK and can be found in the /dartsdk/bin folder
.
To compile Dart to JavaScript, type the following command in the terminal
dart2js - - out = <output_file>.js <dart
_script>.dart
Adarsh:hello-world adarshmaurya$ ls -la
total 8
drwxr-xr-x 3 adarshmaurya staff 96 Dec 8 03:10 .
drwxr-xr-x 9 adarshmaurya staff 288 Dec 8 05:23 ..
-rw-r--r-- 1 adarshmaurya staff 38 Dec 8 02:56 hello_world.dart
Adarsh:hello-world adarshmaurya$ dart2js -o hello_world.js hello_world.dart
Compiled 7,353,116 characters Dart to 10,407 characters JavaScript in 0.46 seconds
Dart file (hello_world.dart) compiled to JavaScript: hello_world.js
Adarsh:hello-world adarshmaurya$ ls -la
total 48
drwxr-xr-x 6 adarshmaurya staff 192 Dec 8 05:32 .
drwxr-xr-x 9 adarshmaurya staff 288 Dec 8 05:23 ..
-rw-r--r-- 1 adarshmaurya staff 38 Dec 8 02:56 hello_world.dart
-rw-r--r-- 1 adarshmaurya staff 10407 Dec 8 05:32 hello_world.js
-rw-r--r-- 1 adarshmaurya staff 275 Dec 8 05:32 hello_world.js.deps
-rw-r--r-- 1 adarshmaurya staff 1040 Dec 8 05:32 hello_world.js.map
Adarsh:hello-world adarshmaurya$
// Generated by dart2js (full emitter, strong), the Dart to JavaScript compiler version: 2.1.0.
// The code supports the following hooks:
// dartPrint(message):
// if this function is defined it is called instead of the Dart [print]
// method.
//
// dartMainRunner(main, args):
// if this function is defined, the Dart [main] method will not be invoked
// directly. Instead, a closure that will invoke [main], and its arguments
// [args] is passed to [dartMainRunner].
//
// dartDeferredLibraryLoader(uri, successCallback, errorCallback):
// if this function is defined, it will be called when a deferred library
// is loaded. It should load and eval the javascript of `uri`, and call
// successCallback. If it fails to do so, it should call errorCallback with
// an error.
//
// defaultPackagesBase:
// Override the location where `package:` uris are resolved from. By default
// they are resolved under "packages/" from the current window location.
(function() {
// /* ::norenaming:: */
var supportsDirectProtoAccess = function() {
var cls = function() {
};
cls.prototype = {p: {}};
var object = new cls();
if (!(object.__proto__ && object.__proto__.p === cls.prototype.p))
return false;
try {
if (typeof navigator != "undefined" && typeof navigator.userAgent == "string" && navigator.userAgent.indexOf("Chrome/") >= 0)
return true;
if (typeof version == "function" && version.length == 0) {
var v = version();
if (/^\d+\.\d+\.\d+\.\d+$/.test(v))
return true;
}
} catch (_) {
}
return false;
}();
function map(x) {
x = Object.create(null);
x.x = 0;
delete x.x;
return x;
}
// The global objects start as so-called "slow objects". For V8, this
// means that it won't try to make map transitions as we add properties
// to these objects. Later on, we attempt to turn these objects into
// fast objects by calling "convertToFastObject" (see
// [emitConvertToFastObjectFunction]).
var A = map();
var B = map();
var C = map();
var D = map();
var E = map();
var F = map();
var G = map();
var H = map();
var J = map();
var K = map();
var L = map();
var M = map();
var N = map();
var O = map();
var P = map();
var Q = map();
var R = map();
var S = map();
var T = map();
var U = map();
var V = map();
var W = map();
var X = map();
var Y = map();
var Z = map();
function Isolate() {
}
init();
// Constructors are generated at runtime.
function setupProgram(programData, metadataOffset, typesOffset) {
"use strict";
function processStatics(descriptor, processedClasses) {
var properties = Object.keys(descriptor);
for (var i = 0; i < properties.length; i++) {
var property = properties[i];
if (property === "^")
continue;
var element = descriptor[property];
var firstChar = property.charCodeAt(0);
var previousProperty;
if (firstChar === 43) {
mangledGlobalNames[previousProperty] = property.substring(1);
var flag = descriptor[property];
if (flag > 0)
descriptor[previousProperty].$reflectable = flag;
if (element && element.length)
init.typeInformation[previousProperty] = element;
} else if (firstChar === 42) {
globalObject[previousProperty].$defaultValues = element;
var optionalMethods = descriptor.$methodsWithOptionalArguments;
if (!optionalMethods)
descriptor.$methodsWithOptionalArguments = optionalMethods = {};
optionalMethods[property] = previousProperty;
} else if (typeof element === "function") {
globalObject[previousProperty = property] = element;
functions.push(property);
} else if (element.constructor === Array) {
} else {
}
}
}
var functionCounter = 0;
if (!init.libraries)
init.libraries = [];
if (!init.mangledNames)
init.mangledNames = map();
if (!init.mangledGlobalNames)
init.mangledGlobalNames = map();
if (!init.statics)
init.statics = map();
if (!init.typeInformation)
init.typeInformation = map();
var libraries = init.libraries;
var mangledNames = init.mangledNames;
var mangledGlobalNames = init.mangledGlobalNames;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var length = programData.length;
var processedClasses = map();
processedClasses.collected = map();
processedClasses.pending = map();
processedClasses.constructorsList = [];
processedClasses.combinedConstructorFunction = "function $reflectable(fn){fn.$reflectable=1;return fn};\n" + "var $desc;\n";
for (var i = 0; i < length; i++) {
var data = programData[i];
var name = data[0];
var uri = data[1];
var metadata = data[2];
var globalObject = data[3];
var descriptor = data[4];
var isRoot = !!data[5];
var fields = descriptor && descriptor["^"];
if (fields instanceof Array)
fields = fields[0];
var classes = [];
var functions = [];
processStatics(descriptor, processedClasses);
libraries.push([name, uri, classes, functions, metadata, fields, isRoot, globalObject]);
}
}
Isolate.functionThatReturnsNull = function() {
};
var dart = [["dart2js._js_primitives", "dart:_js_primitives",, H, {
"^": "",
printString: function(string) {
if (typeof dartPrint == "function") {
dartPrint(string);
return;
}
if (typeof console == "object" && typeof console.log != "undefined") {
console.log(string);
return;
}
if (typeof window == "object")
return;
if (typeof print == "function") {
print(string);
return;
}
throw "Unable to print message: " + String(string);
}
}], ["", "hello_world.dart",, E, {
"^": "",
main: function() {
H.printString("Hello World");
}
}, 1]];
setupProgram(dart, 0, 0);
// getInterceptor methods
// Output contains no constant list.
var $ = Isolate.$isolateProperties;
// No constants in program.
$ = null;
init.isHunkLoaded = function(hunkHash) {
return !!$dart_deferred_initializers$[hunkHash];
};
init.deferredInitialized = new Object(null);
init.isHunkInitialized = function(hunkHash) {
return init.deferredInitialized[hunkHash];
};
init.initializeLoadedHunk = function(hunkHash) {
var hunk = $dart_deferred_initializers$[hunkHash];
if (hunk == null)
throw "DeferredLoading state error: code with hash '" + hunkHash + "' was not loaded";
hunk($globals$, $);
init.deferredInitialized[hunkHash] = true;
};
init.deferredLibraryParts = {};
init.deferredPartUris = [];
init.deferredPartHashes = [];
// Empty type-to-interceptor map.
// No lazy statics.
Isolate = Isolate.$finishIsolateConstructor(Isolate);
$ = new Isolate();
init.metadata = [];
init.types = [];
function convertToFastObject(properties) {
function MyClass() {
}
MyClass.prototype = properties;
new MyClass();
return properties;
}
function convertToSlowObject(properties) {
properties.__MAGIC_SLOW_PROPERTY = 1;
delete properties.__MAGIC_SLOW_PROPERTY;
return properties;
}
A = convertToFastObject(A);
B = convertToFastObject(B);
C = convertToFastObject(C);
D = convertToFastObject(D);
E = convertToFastObject(E);
F = convertToFastObject(F);
G = convertToFastObject(G);
H = convertToFastObject(H);
J = convertToFastObject(J);
K = convertToFastObject(K);
L = convertToFastObject(L);
M = convertToFastObject(M);
N = convertToFastObject(N);
O = convertToFastObject(O);
P = convertToFastObject(P);
Q = convertToFastObject(Q);
R = convertToFastObject(R);
S = convertToFastObject(S);
T = convertToFastObject(T);
U = convertToFastObject(U);
V = convertToFastObject(V);
W = convertToFastObject(W);
X = convertToFastObject(X);
Y = convertToFastObject(Y);
Z = convertToFastObject(Z);
function init() {
Isolate.$isolateProperties = Object.create(null);
init.allClasses = map();
init.getTypeFromName = function(name) {
return init.allClasses[name];
};
init.interceptorsByTag = map();
init.leafTags = map();
init.finishedClasses = map();
Isolate.$finishIsolateConstructor = function(oldIsolate) {
var isolateProperties = oldIsolate.$isolateProperties;
function Isolate() {
var staticNames = Object.keys(isolateProperties);
for (var i = 0; i < staticNames.length; i++) {
var staticName = staticNames[i];
this[staticName] = isolateProperties[staticName];
}
var lazies = init.lazies;
var lazyInitializers = lazies ? Object.keys(lazies) : [];
for (var i = 0; i < lazyInitializers.length; i++)
this[lazies[lazyInitializers[i]]] = null;
function ForceEfficientMap() {
}
ForceEfficientMap.prototype = this;
new ForceEfficientMap();
for (var i = 0; i < lazyInitializers.length; i++) {
var lazyInitName = lazies[lazyInitializers[i]];
this[lazyInitName] = isolateProperties[lazyInitName];
}
}
Isolate.prototype = oldIsolate.prototype;
Isolate.prototype.constructor = Isolate;
Isolate.$isolateProperties = isolateProperties;
Isolate.functionThatReturnsNull = oldIsolate.functionThatReturnsNull;
return Isolate;
};
}
// BEGIN invoke [main].
(function(callback) {
if (typeof document === "undefined") {
callback(null);
return;
}
if (typeof document.currentScript != 'undefined') {
callback(document.currentScript);
return;
}
var scripts = document.scripts;
function onLoad(event) {
for (var i = 0; i < scripts.length; ++i)
scripts[i].removeEventListener("load", onLoad, false);
callback(event.target);
}
for (var i = 0; i < scripts.length; ++i)
scripts[i].addEventListener("load", onLoad, false);
})(function(currentScript) {
init.currentScript = currentScript;
if (typeof dartMainRunner === "function")
dartMainRunner(E.main, []);
else
E.main([]);
});
// END invoke [main].
})();
//# sourceMappingURL=hello_world.js.map
This command produces a file that contains the JavaScript equivalent of your Dart code. A complete tutorial on using this utility can be found on the official Dart website.
Syntax defines a set of rules for writing programs. Every language specification defines its own syntax. A Dart program is composed of −
- Variables and Operators
- Classes
- Functions
- Expressions and Programming Constructs
- Decision Making and Looping Constructs
- Comments
- Libraries and Packages
- Typedefs
- Data structures represented as Collections / Generics
Let us start with the traditional “Hello World” example −
main() {
print("Hello World!");
}
The main()
function is a predefined method in Dart. This method acts as the entry point to the application. A Dart script needs the main()
method for execution. print()
is a predefined function that prints the specified string or value to the standard output i.e. the terminal.
The output of the above code will be −
Hello World!
You can execute a Dart program in two ways −
- Via the terminal
- Via the WebStorm IDE
To execute a Dart program via the terminal −
- Navigate to the path of the current project
- Type the following command in the Terminal window
dart
file_name.dart
To execute a Dart program via the WebStorm IDE −
-
Right-click the Dart script file on the IDE. (The file should contain the
main()
function to enable execution) -
Click on the
‘Run <file_name>’
option.
One can alternatively click the
button or use the shortcut Ctrl+Shift+F10
to execute the Dart Script.
Dart command-line options are used to modify Dart Script execution. Common commandline options for Dart include the following −
Command-Line Option & Description
1 -c or --c
: Enables both assertions and type checks (checked mode).
2 --version
: Displays VM version information.
3 --packages <path>
: Specifies the path to the package resolution configuration file.
4 -p <path>
: Specifies where to find imported libraries. This option cannot be used with --packages.
5 -h or --help
: Displays help.
Dart programs run in two modes namely −
- Checked Mode
- Production Mode (Default)
It is recommended to run the Dart VM in checked mode
during development and testing, since it adds warnings and errors to aid development and debugging process. The checked mode enforces various checks like type-checking etc. To turn on the checked mode, add the -c or –-checked option before the script-file name while running the script.
However, to ensure performance benefit while running the script, it is recommended to run the script in the production mode
.
Consider the following Test.dart
script file −
void main() {
int n = "hello";
print(n);
}
Run the script by entering −
dart
Test.dart
Though there is a type-mismatch the script executes successfully as the checked mode is turned off. The script will result in the following output −
hello
Now try executing the script with the "- - checked" or the "-c" option −
dart
-c Test.dart
Or,
dart
-- checked Test.dart
The Dart VM will throw an error stating that there is a type mismatch.
Adarsh:hello-world adarshmaurya$ dart check-type-mismatch.dart
check-type-mismatch.dart:2:11: Error: A value of type 'dart.core::String' can't be assigned to a variable of type 'dart.core::int'.
Try changing the type of the left hand side, or casting the right hand side to 'dart.core::int'.
int n = "hello";
^
Adarsh:hello-world adarshmaurya$
Identifiers are names given to elements in a program like variables, functions etc. The rules for identifiers are −
Identifiers can include both, characters and digits. However, the identifier cannot begin with a digit.
-
Identifiers cannot include special symbols except for underscore (_) or a dollar sign ($).
-
Identifiers cannot be keywords.
-
They must be unique.
-
Identifiers are case-sensitive.
-
Identifiers cannot contain spaces.
The following tables lists a few examples of valid and invalid identifiers −
Valid identifiers | Invalid identifiers
firstName | Var
first_name | first name
num1 |first-name
$result| 1number
Keywords have a special meaning in the context of a language. The following table lists some keywords in Dart.
abstract 1
continue
false
new
this
as 1
default
final
null
throw
assert
deferred 1
finally
operator 1
true
async 2
do
for
part 1
try
async* 2
dynamic 1
get 1
rethrow
typedef 1
await 2
else
if
return
var
break
enum
implements 1
set 1
void
case
export 1
import 1
static 1
while
catch
external 1
in
super
with
class
extends
is
switch
yield 2
const
factory 1
library 1
sync* 2
yield* 2
Dart ignores spaces, tabs, and newlines that appear in programs. You can use spaces, tabs, and newlines freely in your program and you are free to format and indent your programs in a neat and consistent way that makes the code easy to read and understand.
Dart is case-sensitive. This means that Dart differentiates between uppercase and lowercase characters.
Each line of instruction is called a statement. Each dart
statement must end with a semicolon (;). A single line can contain multiple statements. However, these statements must be separated by a semicolon.
Comments are a way to improve the readability of a program. Comments can be used to include additional information about a program like author of the code, hints about a function/ construct etc. Comments are ignored by the compiler.
Dart supports the following types of comments −
-
Single-line comments ( // )
− Any text between a "//" and the end of a line is treated as a comment -
Multi-line comments (/* */)
− These comments may span multiple lines.
// this is single line comment
/* This is a
Multi-line comment
*/
Dart is an Object-Oriented language. Object Orientation is a software development paradigm that follows real-world modelling. Object Orientation considers a program as a collection of objects that communicate with each other via mechanism called methods.
-
Object
− An object is a real-time representation of any entity. As per Grady Brooch, every object must have three features −-
State
− described by the attributes of an object. -
Behavior
− describes how the object will act. -
Identity
− a unique value that distinguishes an object from a set of similar such objects.
-
-
Class
− A class in terms of OOP is a blueprint for creating objects. A class encapsulates data for the object. -
Method
− Methods facilitate communication between objects.
class TestClass {
void disp() {
print("Hello World");
}
}
void main() {
TestClass c = new TestClass();
c.disp();
}
The above example defines a class TestClass
. The class has a method disp()
. The method prints the string “Hello World” on the terminal. The new keyword creates an object of the class. The object invokes the method disp()
.
The code should produce the following output
−
Hello World
One of the most fundamental characteristics of a programming language is the set of data types it supports. These are the type of values that can be represented and manipulated in a programming language.
The Dart language supports the following types−
- Numbers
- Strings
- Booleans
- Lists
- Maps
Numbers in Dart are used to represent numeric literals. The Number Dart come in two flavours −
-
Integer
− Integer values represent non-fractional values, i.e., numeric values without a decimal point. For example, the value "10" is an integer. Integer literals are represented using theint
keyword. -
Double
− Dart also supports fractional numeric values i.e. values with decimal points. The Double data type in Dart represents a 64-bit (double-precision) floating-point number. For example, the value "10.10". The keyworddouble
is used to represent floating point literals.
Strings represent a sequence of characters. For instance, if you were to store some data like name, address etc. the string data type should be used. A Dart string is a sequence of UTF-16 code units. Runes
are used to represent a sequence of UTF-32 code units.
The keyword String
is used to represent string literals. String values are embedded in either single or double quotes.
The Boolean data type represents Boolean values true and false. Dart uses the bool
keyword to represent a Boolean value.
The data types list and map are used to represent a collection of objects. A List
is an ordered group of objects. The List data type in Dart is synonymous to the concept of an array in other programming languages. The Map
data type represents a set of values as key-value pairs. The ``dart: core` library enables creation and manipulation of these collections through the predefined List and Map classes respectively.
Dart is an optionally typed language. If the type of a variable is not explicitly specified, the variable’s type is dynamic
. The dynamic
keyword can also be used as a type annotation explicitly.
A variable is “a named space in the memory” that stores values. In other words, it acts a container for values in a program. Variable names are called identifiers. Following are the naming rules for an identifier −
-
Identifiers cannot be keywords.
-
Identifiers can contain alphabets and numbers.
-
Identifiers cannot contain spaces and special characters, except the underscore (_) and the dollar ($) sign.
-
Variable names cannot begin with a number.
A variable must be declared before it is used. Dart uses the var keyword to achieve the same. The syntax for declaring a variable is as given below −
var name = 'Smith';
All variables in dart
store a reference to the value rather than containing the value. The variable called name contains a reference to a String object with a value of “Smith”.
Dart supports type-checking
by prefixing the variable name with the data type. Type-checking ensures that a variable holds only data specific to a data type. The syntax for the same is given below −
String name = 'Smith';
int num = 10;
Consider the following example −
void main() {
String name = 1;
}
The above snippet will result in a warning since the value assigned to the variable doesn’t match the variable’s data type.
Warning: A value of type 'String' cannot be assigned to a variable of type 'int'
All uninitialized variables have an initial value of null. This is because Dart considers all values as objects. The following example illustrates the same −
void main() {
int num;
print(num);
}
Null
Variables declared without a static type are implicitly declared as dynamic. Variables can be also declared using the dynamic keyword in place of the var keyword.
The following example illustrates the same.
void main() {
dynamic x = "tom";
print(x);
}
tom
The final
and const
keyword are used to declare constants. Dart prevents modifying the values of a variable declared using the final or const keyword. These keywords can be used in conjunction with the variable’s data type or instead of the var keyword.
The const
keyword is used to represent a compile-time constant. Variables declared using the const
keyword are implicitly final.
final variable_name
OR
final data_type variable_name
const variable_name
OR
const data_type variable_name
void main() {
final val1 = 12;
print(val1);
}
12
void main() {
const pi = 3.14;
const area = pi*12*12;
print("The output is ${area}");
}
The above example declares two constants, pi
and area
, using the const
keyword. The area
variable’s value is a compile-time constant.
The output is 452.15999999999997
Note
− Only const
variables can be used to compute a compile time constant. Compile-time constants are constants whose values will be determined at compile time
Dart throws an exception if an attempt is made to modify variables declared with the final
or const keyword. The example given below illustrates the same −
void main() {
final v1 = 12;
const v2 = 13;
v2 = 12;
}
The code given above will throw the following error as output
−
Unhandled exception:
cannot assign to final variable 'v2='.
NoSuchMethodError: cannot assign to final variable 'v2='
#0 NoSuchMethodError._throwNew (`dart:core-patch/errors_patch.dart:178)
#1 main (file: Test.dart:5:3)
#2 _startIsolate.<anonymous closure> (`dart:isolate-patch/isolate_patch.dart:261)
#3 _RawReceivePortImpl._handleMessage (`dart:isolate-patch/isolate_patch.dart:148)
An expression is a special kind of statement that evaluates to a value. Every expression is composed of −
-
Operands
− Represents the data -
Operator
− Defines how the operands will be processed to produce a value.
Consider the following expression – "2 + 3". In this expression, 2 and 3 are operands
and the symbol "+" (plus) is the operator
.
In this chapter, we will discuss the operators that are available in Dart.
- Arithmetic Operators
- Equality and Relational Operators
- Type test Operators
- Bitwise Operators
- Assignment Operators
- Logical Operators
The following table shows the arithmetic operators supported by Dart.
Operators & Meaning
1 +
Add
2 −
Subtract
3-expr
Unary minus, also known as negation (reverse the sign of the expression)
4 *
Multiply
5 /
Divide
6 ~/
Divide, returning an integer result
7 %
Get the remainder of an integer division (modulo)
8 ++
Increment
9 --
Decrement
Relational Operators tests or defines the kind of relationship between two entities. Relational operators return a Boolean value i.e. true/ false.
Assume the value of A is 10 and B is 20.
Operator | Description | Example
\> | Greater than | (A > B) is False
< | Lesser than | (A < B) is True
\>= | Greater than or equal to | (A >= B) is False
<= | Lesser than or equal to | (A <= B) is True
\== | Equality | (A==B) is True
!= | Not equal | (A!=B) is True
These operators are handy for checking types at runtime.
Operator | Meaning
is | True if the object has the specified type
is! | False if the object has the specified type
The following table lists the bitwise operators available in Dart and their role −
Operator | Description |Example
Bitwise AND| a & b | Returns a one in each bit position for which the corresponding bits of both operands are ones.
Bitwise OR| a | b | Returns a one in each bit position for which the corresponding bits of either or both operands are ones.
Bitwise XOR | a ^ b | Returns a one in each bit position for which the corresponding bits of either but not both operands are ones.
Bitwise NOT | ~ a | Inverts the bits of its operand.
Left shift | a ≪ b |Shifts a in binary representation b (< 32) bits to the left, shifting in zeroes from the right.
Signpropagating right shift | a ≫ b | Shifts a in binary representation b (< 32) bits to the right, discarding bits shifted off.
The following table lists the assignment operators available in Dart.
Operator & Description
1 `=(Simple Assignment )`
Assigns values from the right side operand to the left side operand
`Ex:C = A + B will assign the value of A + B into C
2 `??=`
Assign the value only if the variable is null
3 `+=(Add and Assignment)`
It adds the right operand to the left operand and assigns the result to the left operand.
`Ex: C += A is equivalent to C = C + A
4 `─=(Subtract and Assignment)`
It subtracts the right operand from the left operand and assigns the result to the left operand.
`Ex: C -= A is equivalent to C = C – A
5 `*=(Multiply and Assignment)`
It multiplies the right operand with the left operand and assigns the result to the left operand.
`Ex: C *= A is equivalent to C = C * A
6 `/=(Divide and Assignment)`
It divides the left operand with the right operand and assigns the result to the left operand.
Note
− Same logic applies to Bitwise operators, so they will become ≪=, ≫=, ≫=, ≫=, |= and ^=.
Logical operators are used to combine two or more conditions. Logical operators return a Boolean value. Assume the value of variable A is 10 and B is 20.
Operator | Description | Example
&& | `And` − The operator returns true only if all the expressions specified return true | (A > 10 && B > 10) is False.
|| | `OR` − The operator returns true if at least one of the expressions specified return true | (A > 10 || B > 10) is True.
! | `NOT` − The operator returns the inverse of the expression’s result. For E.g.: !(7>5) returns false | !(A > 10) is True.
Dart has two operators that let you evaluate expressions that might otherwise require ifelse statements −
If condition is true, then the expression evaluates expr1
(and returns its value); otherwise, it evaluates and returns the value of expr2
.
If expr1
is non-null, returns its value; otherwise, evaluates and returns the value of expr2
The following example shows how you can use conditional expression in Dart −
void main() {
var a = 10;
var res = a > 12 ? "value greater than 10":"value lesser than or equal to 10";
print(res);
}
It will produce the following output −
value lesser than or equal to 10
Let’s take another example −
void main() {
var a = null;
var b = 12;
var res = a ?? b;
print(res);
}
It will produce the following output −
12
At times, certain instructions require repeated execution. Loops are an ideal way to do the same. A loop represents a set of instructions that must be repeated. In a loop’s context, a repetition is termed as an iteration
.
The following figure illustrates the classification of loops −
Let’s start the discussion with Definite Loops. A loop whose number of iterations are definite/fixed is termed as a definite loop
.
Loop & Description
1 for loop
The for
loop is an implementation of a definite loop. The for loop executes the code block for a specified number of times. It can be used to iterate over a fixed set of values, such as an array
2 for…in Loop
The for...in loop is used to loop through an object's properties.
Moving on, let’s now discuss the indefinite loops. An indefinite loop is used when the number of iterations in a loop is indeterminate or unknown. Indefinite loops can be implemented using −
1 The while
loop executes the instructions each time the condition specified evaluates to true. In other words, the loop evaluates the condition before the block of code is executed.
2 The do…while
loop is similar to the while loop except that the do...while loop doesn’t evaluate the condition for the first time the loop executes.
Let us now move on and discuss the Loop Control Statements
of Dart.
1 The break
statement is used to take the control out of a construct. Using break
in a loop causes the program to exit the loop. Following is an example of the break
statement.
2 The continue
statement skips the subsequent statements in the current iteration and takes the control back to the beginning of the loop.
A label
is simply an identifier followed by a colon (:) that is applied to a statement or a block of code. A label can be used with break
and continue
to control the flow more precisely.
Line breaks are not allowed between the ‘continue’
or ‘break’
statement and its label name. Also, there should not be any other statement in between a label name and an associated loop.
void main() {
outerloop: // This is the label name
for (var i = 0; i < 5; i++) {
print("Innerloop: ${i}");
innerloop:
for (var j = 0; j < 5; j++) {
if (j > 3 ) break ;
// Quit the innermost loop
if (i == 2) break innerloop;
// Do the same thing
if (i == 4) break outerloop;
// Quit the outer loop
print("Innerloop: ${j}");
}
}
}
The following output
is displayed on successful execution of the above code.
Innerloop: 0
Innerloop: 0
Innerloop: 1
Innerloop: 2
Innerloop: 3
Innerloop: 1
Innerloop: 0
Innerloop: 1
Innerloop: 2
Innerloop: 3
Innerloop: 2
Innerloop: 3
Innerloop: 0
Innerloop: 1
Innerloop: 2
Innerloop: 3
Innerloop: 4
void main() {
outerloop: // This is the label name
for (var i = 0; i < 3; i++) {
print("Outerloop:${i}");
for (var j = 0; j < 5; j++) {
if (j == 3){
continue outerloop;
}
print("Innerloop:${j}");
}
}
}
The following output is displayed on successful execution of the above code.
Outerloop: 0
Innerloop: 0
Innerloop: 1
Innerloop: 2
Outerloop: 1
Innerloop: 0
Innerloop: 1
Innerloop: 2
Outerloop: 2
Innerloop: 0
Innerloop: 1
Innerloop: 2
A conditional/decision-making construct evaluates a condition before the instructions are executed.
Conditional constructs in Dart are classified in the following table.
1 An if
statement consists of a Boolean expression followed by one or more statements.
2 An if
can be followed by an optional else
block. The else
block will execute if the Boolean expression tested by the if
block evaluates to false.
3 The else…if ladder
is useful to test multiple conditions. Following is the syntax of the same.
4 The switch statement evaluates an expression, matches the expression’s value to a case clause and executes the statements associated with that case.
Dart numbers can be classified as −
-
int
− Integer of arbitrary size. Theint
data type is used to represent whole numbers. -
double
− 64-bit (double-precision) floating-point numbers, as specified by the IEEE 754 standard. Thedouble
data type is used to represent fractional numbers
The num
type is inherited by the int
and double
types. The ``dart core library
allows numerous operations on numeric values.
The syntax for declaring a number is as given below −
int var_name; // declares an integer variable double var_name; // declares a double variable
void main() {
int num1 = 10;
// declare an integer
double num2 = 10.50;
// declare a double value
print(num1);
print(num2);
}
It will produce the following output −
10
10.5
Note
− The Dart VM
will throw an exception if fractional values are assigned to integer variables.
The parse()
static function allows parsing a string containing numeric literal into a number. The following illustration demonstrates the same −
void main() {
print(num.parse('12'));
print(num.parse('10.91'));
}
The above code will result in the following output −
12
10.91
The parse function throws a FormatException
if it is passed any value other than numerals. The following code shows how to pass an alpha-numeric value to the parse()
function.
void main() {
print(num.parse('12A'));
print(num.parse('AAAA'));
}
The above code will result in the following output −
Unhandled exception:
FormatException: 12A
#0 num.parse (`dart:core/num.dart:446)
#1 main (file:///D:/Demos/numbers.dart:4:13)
#2 _startIsolate.<anonymous closure> (`dart:isolatepatch/isolate_patch.dart:261)
#3 _RawReceivePortImpl._handleMessage (`dart:isolatepatch/isolate_patch.dart:148)
The following table lists the properties supported by Dart numbers.
Property & Description
1 hashcode
Returns a hash code for a numerical value.
2 isFinite
True if the number is finite; otherwise, false.
3 isInfinite
True if the number is positive infinity or negative infinity; otherwise, false.
4 isNan
True if the number is the double Not-a-Number value; otherwise, false.
5 isNegative
True if the number is negative; otherwise, false.
6 sign
Returns minus one, zero or plus one depending on the sign and numerical value of the number.
7 isEven
Returns true if the number is an even number.
8 isOdd
Returns true if the number is an odd number.
Given below are a list of commonly used methods supported by numbers −
Method & Description
1 abs
Returns the absolute value of the number.
2 ceil
Returns the least integer no smaller than the number.
3 compareTo
Compares this to other number.
4 Floor
Returns the greatest integer not greater than the current number.
5 remainder
Returns the truncated remainder after dividing the two numbers.
6 Round
Returns the integer closest to the current numbers.
7 toDouble
Returns the double equivalent of the number.
8 toInt
Returns the integer equivalent of the number.
9 toString
Returns the string equivalent representation of the number.
10 truncate
Returns an integer after discarding any fractional digits.
The String data type represents a sequence of characters. A Dart string is a sequence of UTF 16 code units.
String values in Dart can be represented using either single or double or triple quotes. Single line strings are represented using single or double quotes. Triple quotes are used to represent multi-line strings.
The syntax of representing string values in Dart is as given below −
String variable_name = 'value'
OR
String variable_name = ''value''
OR
String variable_name = '''line1 line2'''
OR
String variable_name= ''''''line1 line2''''''
The following example illustrates the use of String data type in Dart.
void main() {
String str1 = 'this is a single line string';
String str2 = "this is a single line string";
String str3 = '''this is a multiline line string''';
String str4 = """this is a multiline line string""";
print(str1);
print(str2);
print(str3);
print(str4);
}
It will produce the following Output
−
this is a single line string
this is a single line string
this is a multiline line string
this is a multiline line string
Strings are immutable. However, strings can be subjected to various operations and the resultant string can be a stored as a new value.
The process of creating a new string by appending a value to a static string is termed as concatenation
or interpolation
. In other words, it is the process of adding a string to another string.
The operator plus (+) is a commonly used mechanism to concatenate / interpolate strings.
void main() {
String str1 = "hello";
String str2 = "world";
String res = str1+str2;
print("The concatenated string : ${res}");
}
It will produce the following output
−
The concatenated string : Helloworld
You can use "${}" can be used to interpolate the value of a Dart expression within strings. The following example illustrates the same.
void main() {
int n=1+1;
String str1 = "The sum of 1 and 1 is ${n}";
print(str1);
String str2 = "The sum of 2 and 2 is ${2+2}";
print(str2);
}
It will produce the following output
−
The sum of 1 and 1 is 2
The sum of 2 and 2 is 4
The properties listed in the following table are all read-only.
Property & Description
1 codeUnits
Returns an unmodifiable list of the UTF-16 code units of this string.
2 isEmpty
Returns true if this string is empty.
3 Length
Returns the length of the string including space, tab and newline characters.
The String class in the ``dart: core library` also provides methods to manipulate strings. Some of these methods are given below −
Methods & Description
1 toLowerCase()
Converts all characters in this string to lower case.
2 toUpperCase()
Converts all characters in this string to upper case.
3 trim()
Returns the string without any leading and trailing whitespace.
4 compareTo()
Compares this object to another.
5 replaceAll()
Replaces all substrings that match the specified pattern with a given value.
6 split()
Splits the string at matches of the specified delimiter and returns a list of substrings.
7 substring()
Returns the substring of this string that extends from startIndex, inclusive, to endIndex, exclusive.
8 toString()
Returns a string representation of this object.
9 codeUnitAt()
Returns the 16-bit UTF-16 code unit at the given index.
Dart provides an inbuilt support for the Boolean data type. The Boolean data type in DART supports only two values – true and false. The keyword bool is used to represent a Boolean literal in DART.
The syntax for declaring a Boolean variable in DART is as given below −
bool var_name = true;
OR
bool var_name = false
void main() {
bool test;
test = 12 > 5;
print(test);
}
It will produce the following output
−
true
Unlike JavaScript, the Boolean data type recognizes only the literal true as true. Any other value is considered as false. Consider the following example −
var str = 'abc';
if(str) {
print('String is not empty');
} else {
print('Empty String');
}
The above snippet, if run in JavaScript, will print the message ‘String is not empty’ as the if construct will return true if the string is not empty.
However, in Dart, str
is converted to false as str != true. Hence the snippet will print the message ‘Empty String’ (when run in unchecked mode).
The above snippet if run in checked
mode will throw an exception. The same is illustrated below −
void main() {
var str = 'abc';
if(str) {
print('String is not empty');
} else {
print('Empty String');
}
}
It will produce the following output
, in Checked Mode
−
Unhandled exception:
type 'String' is not a subtype of type 'bool' of 'boolean expression' where
String is from `dart:core
bool is from `dart:core
#0 main (file:///D:/Demos/Boolean.dart:5:6)
#1 _startIsolate.<anonymous closure> (`dart:isolate-patch/isolate_patch.dart:261)
#2 _RawReceivePortImpl._handleMessage (`dart:isolate-patch/isolate_patch.dart:148)
It will produce the following output
, in Unchecked Mode
−
Empty String
Note
− The WebStorm
IDE runs in checked mode, by default.
A very commonly used collection in programming is an array
. Dart represents arrays in the form of List
objects. A List
is simply an ordered group of objects. The ``dart:core` library provides the List class that enables creation and manipulation of lists.
The logical representation of a list in Dart is given below −
-
test_list
− is the identifier that references the collection. -
The list contains in it the values 12, 13, and 14. The memory blocks holding these values are known as
elements
. -
Each element in the List is identified by a unique number called the
index
. The index starts fromzero
and extends up ton-1
wheren
is the total number of elements in the List. The index is also referred to as thesubscript
.
Lists can be classified as −
- Fixed Length List
- Growable List
Let us now discuss these two types of lists
in detail.
A fixed length list’s length cannot change at runtime. The syntax for creating a fixed length list is as given below −
Step 1 − Declaring a list
The syntax for declaring a fixed length list is given below −
var list_name = new List(initial_size)
The above syntax creates a list of the specified size. The list cannot grow or shrink at runtime. Any attempt to resize the list will result in an exception.
Step 2 − Initializing a list
The syntax for initializing a list is as given below −
lst_name[index] = value;
void main() {
var lst = new List(3);
lst\[0\] = 12;
lst\[1\] = 13;
lst\[2\] = 11;
print(lst);
}
It will produce the following output
−
\[12, 13, 11\]
A growable list’s length can change at run-time. The syntax for declaring and initializing a growable list is as given below −
Step 1 − Declaring a List
var list_name = [val1,val2,val3]
--- creates a list containing the specified values
OR
var list_name = new List()
--- creates a list of size zero
Step 2 − Initializing a List
The index / subscript is used to reference the element that should be populated with a value. The syntax for initializing a list is as given below −
list_name[index] = value;
The following example shows how to create a list of 3 elements.
void main() {
var num_list = \[1,2,3\];
print(num_list);
}
It will produce the following output
−
[1, 2, 3]
The following example creates a zero-length list using the empty List() constructor
. The add()
function in the List
class is used to dynamically add elements to the list.
void main() {
var lst = new List();
lst.add(12);
lst.add(13);
print(lst);
}
It will produce the following output
−
[12, 13]
The following table lists some commonly used properties of the List
class in the ``dart:core library`.
Methods & Description
1 first
Returns the first element case.
2 isEmpty
Returns true if the collection has no elements.
3 isNotEmpty
Returns true if the collection has at least one element.
4 length
Returns the size of the list.
5 last
Returns the last element in the list.
6 reversed
Returns an iterable object containing the lists values in the reverse order.
7 Single
Checks if the list has only one element and returns it.
In this chapter, we will discuss how to carry out some basic operations on Lists, such as −
Basic Operation & Description
1 Inserting Elements into a List
Mutable Lists can grow dynamically at runtime. The List.add()
function appends the specified value to the end of the List and returns a modified List object.
2 Updating a list
Lists in Dart can be updated by −
3 Removing List items
The following functions supported by the List class in the `dart:core library can be used to remove the item(s) in a List.
The Map object is a simple key/value pair. Keys and values in a map may be of any type. A Map is a dynamic collection. In other words, Maps can grow and shrink at runtime.
Maps can be declared in two ways −
- Using Map Literals
- Using a Map constructor
To declare a map using map literals, you need to enclose the key-value pairs within a pair of curly brackets "{ }"
.
Here is its syntax
−
var identifier = { key1:value1, key2:value2 [,…..,key_n:value_n] }
To declare a Map using a Map constructor, we have two steps. First, declare the map and second, initialize the map.
The syntax
to declare a map
is as follows −
var identifier = new Map()
Now, use the following syntax to initialize the map
−
map_name[key] = value
void main() {
var details = {'Usrname':'tom','Password':'pass@123'};
print(details);
}
It will produce the following output
−
{Usrname: tom, Password: pass@123}
void main() {
var details = {'Usrname':'tom','Password':'pass@123'};
details\['Uid'\] = 'U1oo1';
print(details);
}
It will produce the following output
−
{Usrname: tom, Password: pass@123, Uid: U1oo1}
void main() {
var details = new Map();
details\['Usrname'\] = 'admin';
details\['Password'\] = 'admin@123';
print(details);
}
It will produce the following output
−
{Usrname: admin, Password: admin@123}
Note
− A map value can be any object including NULL.
The Map
class in the `dart:core package defines the following properties −
Property & Description
1 Keys
Returns an iterable object representing keys
2 Values
Returns an iterable object representing values
3 Length
Returns the size of the Map
4 isEmpty
Returns true if the Map is an empty Map
5 isNotEmpty
Returns true if the Map is an empty Map
Following are the commonly used functions for manipulating Maps in Dart.
1 addAll()
Adds all key-value pairs of other to this map.
2
Removes all pairs from the map.
3
Removes key and its associated value, if present, from the map.
4
Applies f to each key-value pair of the map.
Symbols in Dart are opaque, dynamic string name used in reflecting out metadata from a library. Simply put, symbols are a way to store the relationship between a human readable string and a string that is optimized to be used by computers.
Reflection is a mechanism to get metadata of a type at runtime like the number of methods in a class, the number of constructors it has or the number of parameters in a function. You can even invoke a method of the type which is loaded at runtime.
In Dart reflection specific classes are available in the ``dart:mirrors` package. This library works in both web applications and command line applications.
Symbol obj = new Symbol('name');
// expects a name of class or function or library to reflect
The name
must be a valid public Dart member name, public constructor name, or library name.
Consider the following example. The code declares a class Foo
in a library foo_lib
. The class defines the methods m1, m2,
and m3
.
library foo_lib;
// libarary name can be a symbol
class Foo {
// class name can be a symbol
m1() {
// method name can be a symbol
print("Inside m1");
}
m2() {
print("Inside m2");
}
m3() {
print("Inside m3");
}
}
The following code loads Foo.dart
library and searches for Foo class, with help of Symbol type. Since we are reflecting the metadata from the above library the code imports dart:mirrors
library.
import 'dart:core';
import 'dart:mirrors';
import 'Foo.dart';
main() {
Symbol lib = new Symbol("foo_lib");
//library name stored as Symbol
Symbol clsToSearch = new Symbol("Foo");
// class name stored as Symbol
if(checkIf_classAvailableInlibrary(lib, clsToSearch))
// searches Foo class in foo_lib library
print("class found..");
}
bool checkIf_classAvailableInlibrary(Symbol libraryName, Symbol className) {
MirrorSystem mirrorSystem = currentMirrorSystem();
LibraryMirror libMirror = mirrorSystem.findLibrary(libraryName);
if (libMirror != null) {
print("Found Library");
print("checkng...class details..");
print("No of classes found is : ${libMirror.declarations.length}");
libMirror.declarations.forEach((s, d) => print(s));
if (libMirror.declarations.containsKey(className)) return true;
return false;
}
}
Note that the line libMirror.declarations.forEach((s, d) => print(s)); will iterate across every declaration in the library at runtime and prints the declarations as type of Symbol
.
This code should produce the following output
−
Found Library
checkng...class details..
No of classes found is : 1
Symbol("Foo") // class name displayed as symbol
class found.
Let us now consider displaying the number of instance methods in a class. The predefined class ClassMirror
helps us to achieve the same.
import 'dart:core';
import 'dart:mirrors';
import 'Foo.dart';
main() {
Symbol lib = new Symbol("foo_lib");
Symbol clsToSearch = new Symbol("Foo");
reflect_InstanceMethods(lib, clsToSearch);
}
void reflect_InstanceMethods(Symbol libraryName, Symbol className) {
MirrorSystem mirrorSystem = currentMirrorSystem();
LibraryMirror libMirror = mirrorSystem.findLibrary(libraryName);
if (libMirror != null) {
print("Found Library");
print("checkng...class details..");
print("No of classes found is : ${libMirror.declarations.length}");
libMirror.declarations.forEach((s, d) => print(s));
if (libMirror.declarations.containsKey(className)) print("found class");
ClassMirror classMirror = libMirror.declarations\[className\];
print("No of instance methods found is ${classMirror.instanceMembers.length}");
classMirror.instanceMembers.forEach((s, v) => print(s));
}
}
This code should produce the following output
−
Found Library
checkng...class details..
No of classes found is : 1
Symbol("Foo")
found class
No of instance methods found is 8
Symbol("==")
Symbol("hashCode")
Symbol("toString")
Symbol("noSuchMethod")
Symbol("runtimeType")
Symbol("m1")
Symbol("m2")
Symbol("m3")
You can convert the name of a type like class or library stored in a symbol back to string using MirrorSystem
class. The following code shows how you can convert a symbol to a string.
import 'dart:mirrors';
void main(){
Symbol lib = new Symbol("foo_lib");
String name_of_lib = MirrorSystem.getName(lib);
print(lib);
print(name_of_lib);
}
It should produce the following output
−
Symbol("foo_lib")
foo_lib
Strings are a sequence of characters. Dart represents strings as a sequence of Unicode UTF-16 code units. Unicode is a format that defines a unique numeric value for each letter, digit, and symbol.
Since a Dart string is a sequence of UTF-16 code units, 32-bit Unicode values within a string are represented using a special syntax. A rune
is an integer representing a Unicode code point.
The String class in the ``dart:corelibrary provides mechanisms to access
runes`. String code units / runes can be accessed in three ways −
- Using String.codeUnitAt() function
- Using String.codeUnits property
- Using String.runes property
Code units in a string can be accessed through their indexes. Returns the 16-bit UTF-16 code unit at the given index.
String.codeUnitAt(int index);
import 'dart:core';
void main(){
f1();
}
f1() {
String x = 'Runes';
print(x.codeUnitAt(0));
}
It will produce the following output
−
82
This property returns an unmodifiable list of the UTF-16 code units of the specified string.
String. codeUnits;
import 'dart:core';
void main(){
f1();
}
f1() {
String x = 'Runes';
print(x.codeUnits);
}
It will produce the following output
−
\[82, 117, 110, 101, 115\]
This property returns an iterable of Unicode code-points of this string.Runes
extends iterable.
String.runes
Example
void main(){
"A string".runes.forEach((int rune) {
var character=new String.fromCharCode(rune);
print(character);
});
}
It will produce the following output
−
A
s
t
r
i
n
g
Unicode code points are usually expressed as \\uXXXX
, where XXXX is a 4-digit hexadecimal value. To specify more or less than 4 hex digits, place the value in curly brackets. One can use the constructor of the Runes class in the `dart:core library for the same.
main() {
Runes input = new Runes(' \\u{1f605} ');
print(new String.fromCharCodes(input));
}
It will produce the following output
−
An enumeration is used for defining named constant values. An enumerated type is declared using the enum
keyword.
enum enum_name {
enumeration list
}
Where,
- The enum_name specifies the enumeration type name
- The enumeration list is a comma-separated list of identifiers
Each of the symbols in the enumeration list stands for an integer value, one greater than the symbol that precedes it. By default, the value of the first enumeration symbol is 0.
enum Status {
none,
running,
stopped,
paused
}
enum Status {
none,
running,
stopped,
paused
}
void main() {
print(Status.values);
Status.values.forEach((v) => print('value: $v, index: ${v.index}'));
print('running: ${Status.running}, ${Status.running.index}');
print('running index: ${Status.values\[1\]}');
}
It will produce the following output
−
\[Status.none, Status.running, Status.stopped, Status.paused\]
value: Status.none, index: 0
value: Status.running, index: 1
value: Status.stopped, index: 2
value: Status.paused, index: 3
running: Status.running, 1
running index: Status.running
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.
1 A function definition specifies what and how a specific task would be done.
2 A function must be called so as to execute it.
3 Functions may also return value along with control, back to the caller.
4 Parameters are a mechanism to pass values to functions.
Optional parameters can be used when arguments need not be compulsorily passed for a function’s execution. A parameter can be marked optional by appending a question mark to its name. The optional parameter should be set as the last argument in a function.
We have three types of optional parameters in Dart −
Parameter & Description
1 To specify optional positional parameters, use square [] brackets.
2 Unlike positional parameters, the parameter's name must be specified while the value is being passed. Curly brace {} can be used to specify optional named parameters.
3 Function parameters can also be assigned values by default. However, such parameters can also be explicitly passed values.
Recursion is a technique for iterating over an operation by having a function call to itself repeatedly until it arrives at a result. Recursion is best applied when you need to call the same function repeatedly with different parameters from within a loop.
void main() {
print(factorial(6));
}
factorial(number) {
if (number <= 0) {
// termination case
return 1;
} else {
return (number * factorial(number - 1));
// function invokes itself
}
}
It should produce the following output
−
720
Lambda functions are a concise mechanism to represent functions. These functions are also called as Arrow functions.
\[return_type\]function_name(parameters)=>expression;
void main() {
printMsg();
print(test());
}
printMsg()=>
print("hello");
int test()=>123;
// returning function
It should produce the following output
−
hello 123
An
interface
defines the syntax that any entity must adhere to. Interfaces define a set of methods available on an object. Dart does not have a syntax for declaring interfaces. Class declarations are themselves interfaces in Dart.
Classes
should use the implements keyword to be able to use an interface. It is mandatory for the implementing class to provide a concrete implementation of all the functions of the implemented interface. In other words, a class must redefine every function in the interface it wishes to implement.
class identifier implements interface_name
In the following program, we are declaring a class Printer
. The ConsolePrinter
class implements the implicit interface declaration for the Printer
class. The main
function creates an object of the ConsolePrinter
class using the new
keyword. This object is used to invoke the function print_data
defined in the ConsolePrinter
class.
void main() {
ConsolePrinter cp= new ConsolePrinter();
cp.print_data();
}
class Printer {
void print_data() {
print("__________Printing Data__________");
}
}
class ConsolePrinter implements Printer {
void print_data() {
print("__________Printing to Console__________");
}
}
It should produce the following output
−
__________Printing to Console__________
A class can implement multiple interfaces. The interfaces are separated by a comma. The syntax
for the same is given below −
class identifier implements interface-1,interface_2,interface_4…….
The following example
shows how you can implement multiple interfaces in Dart −
void main() {
Calculator c = new Calculator();
print("The gross total : ${c.ret_tot()}");
print("Discount :${c.ret_dis()}");
}
class Calculate_Total {
int ret_tot() {}
}
class Calculate_Discount {
int ret_dis() {}
}
class Calculator implements Calculate_Total,Calculate_Discount {
int ret_tot() {
return 1000;
}
int ret_dis() {
return 50;
}
}
It should produce the following output
−
The gross total: 1000
Discount:50
Dart is an object-oriented language. It supports object-oriented programming features like classes, interfaces, etc. A class
in terms of OOP is a blueprint for creating objects. A class
encapsulates data for the object. Dart gives built-in support for this concept called class
.
Use the class
keyword to declare a class
in Dart. A class definition starts with the keyword class followed by the class name
; and the class body enclosed by a pair of curly braces. The syntax for the same is given below −
class class_name {
<fields>
<getters/setters>
<constructors>
<functions>
}
The class
keyword is followed by the class name. The rules for identifiers must be considered while naming a class.
A class definition can include the following −
-
Fields
− A field is any variable declared in a class. Fields represent data pertaining to objects. -
Setters and Getters
− Allows the program to initialize and retrieve the values of the fields of a class. A default getter/ setter is associated with every class. However, the default ones can be overridden by explicitly defining a setter/ getter. -
Constructors
− responsible for allocating memory for the objects of the class. -
Functions
− Functions represent actions an object can take. They are also at times referred to as methods.
These components put together are termed as the data members
of the class.
class Car {
// field
String engine = "E1001";
// function
void disp() {
print(engine);
}
}
The example declares a class Car
. The class has a field named engine
. The disp()
is a simple function that prints the value of the field engine
.
To create an instance of the class, use the new
keyword followed by the class name. The syntax for the same is given below −
var object_name = new class_name([ arguments ])
-
The
new
keyword is responsible for instantiation. -
The right-hand side of the expression invokes the constructor. The constructor should be passed values if it is parameterized.
var obj = new Car("Engine 1")
A class’s attributes and functions can be accessed through the object. Use the ‘.’ dot notation (called as the period
) to access the data members of a class.
//accessing an attribute
obj.field_name
//accessing a function
obj.function_name()
Take a look at the following example to understand how to access attributes and functions in Dart −
void main() {
Car c= new Car();
c.disp();
}
class Car {
// field
String engine = "E1001";
// function
void disp() {
print(engine);
}
}
The output
of the above code is as follows −
E1001
A constructor is a special function of the class that is responsible for initializing the variables of the class. Dart defines a constructor with the same name as that of the class. A constructor is a function and hence can be parameterized. However, unlike a function, constructors cannot have a return type. If you don’t declare a constructor, a default no-argument constructor
is provided for you.
Class_name(parameter_list) {
//constructor body
}
The following example shows how to use constructors in Dart −
void main() {
Car c = new Car('E1001');
}
class Car {
Car(String engine) {
print(engine);
}
}
It should produce the following output
−
E1001
Dart provides named constructors
to enable a class define multiple constructors
. The syntax of named constructors is as given below −
Class_name.constructor_name(param_list)
The following example shows how you can use named constructors in Dart −
void main() {
Car c1 = new Car.namedConst('E1001');
Car c2 = new Car();
}
class Car {
Car() {
print("Non-parameterized constructor invoked");
}
Car.namedConst(String engine) {
print("The engine is : ${engine}");
}
}
It should produce the following output
−
The engine is : E1001
Non-parameterized constructor invoked
The this
keyword refers to the current instance of the class. Here, the parameter name and the name of the class’s field are the same. Hence to avoid ambiguity, the class’s field is prefixed with the this
keyword. The following example explains the same −
The following example explains how to use the this
keyword in Dart −
void main() {
Car c1 = new Car('E1001');
}
class Car {
String engine;
Car(String engine) {
this.engine = engine;
print("The engine is : ${engine}");
}
}
It should produce the following output
−
The engine is : E1001
Getters
and Setters
, also called as accessors
and mutators
, allow the program to initialize and retrieve the values of class fields respectively. Getters or accessors are defined using the get
keyword. Setters or mutators are defined using the set
keyword.
A default getter/setter is associated with every class. However, the default ones can be overridden by explicitly defining a setter/ getter. A getter has no parameters and returns a value, and the setter has one parameter and does not return a value.
Return_type get identifier
{
}
set identifier
{
}
The following example shows how you can use getters
and setters
in a Dart class −
class Student {
String name;
int age;
String get stud_name {
return name;
}
void set stud_name(String name) {
this.name = name;
}
void set stud_age(int age) {
if(age<= 0) {
print("Age should be greater than 5");
} else {
this.age = age;
}
}
int get stud_age {
return age;
}
}
void main() {
Student s1 = new Student();
s1.stud_name = 'MARK';
s1.stud_age = 0;
print(s1.stud_name);
print(s1.stud_age);
}
This program code should produce the following output
−
Age should be greater than 5
MARK
Null
Dart supports the concept of Inheritance which is the ability of a program to create new classes from an existing class. The class that is extended to create newer classes is called the parent class/super class. The newly created classes are called the child/sub classes.
A class inherits from another class using the ‘extends’ keyword. Child classes inherit all properties and methods except constructors from the parent class
.
class child_class_name extends parent_class_name
Note
− Dart doesn’t support multiple inheritance.
In the following example, we are declaring a class Shape
. The class is extended by the Circle
class. Since there is an inheritance relationship between the classes, the child class, i.e., the class Car
gets an implicit access to its parent class data member.
void main() {
var obj = new Circle();
obj.cal_area();
}
class Shape {
void cal_area() {
print("calling calc area defined in the Shape class");
}
}
class Circle extends Shape {}
It should produce the following output
−
calling calc area defined in the Shape class
Inheritance can be of the following three types −
-
Single
− Every class can at the most extend from one parent class. -
Multiple
− A class can inherit from multiple classes. Dart doesn’t support multiple inheritance. -
Multi-level
− A class can inherit from another child class.
The following example shows how multi-level inheritance works −
void main() {
var obj = new Leaf();
obj.str = "hello";
print(obj.str);
}
class Root {
String str;
}
class Child extends Root {}
class Leaf extends Child {}
//indirectly inherits from Root by virtue of inheritance
The class Leaf
derives the attributes from Root and Child classes by virtue of multi-level inheritance. Its output
is as follows −
hello
Method Overriding is a mechanism by which the child class redefines a method in its parent class. The following example illustrates the same −
void main() {
Child c = new Child();
c.m1(12);
}
class Parent {
void m1(int a){ print("value of a ${a}");}
}
class Child extends Parent {
@override
void m1(int b) {
print("value of b ${b}");
}
}
It should produce the following output
−
value of b 12
The number and type of the function parameters must match while overriding the method. In case of a mismatch in the number of parameters or their data type, the Dart compiler throws an error. The following illustration explains the same −
import 'dart:io';
void main() {
Child c = new Child();
c.m1(12);
}
class Parent {
void m1(int a){ print("value of a ${a}");}
}
class Child extends Parent {
@override
void m1(String b) {
print("value of b ${b}");
}
}
It should produce the following output
−
value of b 12
The static
keyword can be applied to the data members of a class, i.e., fields
and methods
. A static variable retains its values till the program finishes execution. Static members are referenced by the class name.
class StaticMem {
static int num;
static disp() {
print("The value of num is ${StaticMem.num}") ;
}
}
void main() {
StaticMem.num = 12;
// initialize the static variable }
StaticMem.disp();
// invoke the static method
}
It should produce the following output
−
The value of num is 12
The super
keyword is used to refer to the immediate parent of a class. The keyword can be used to refer to the super class version of a variable, property,
or method
. The following example illustrates the same −
void main() {
Child c = new Child();
c.m1(12);
}
class Parent {
String msg = "message variable from the parent class";
void m1(int a){ print("value of a ${a}");}
}
class Child extends Parent {
@override
void m1(int b) {
print("value of b ${b}");
super.m1(13);
print("${super.msg}") ;
}
}
It should produce the following output
−
value of b 12
value of a 13
message variable from the parent class
Object-Oriented Programming defines an object as “any entity that has a defined boundary.” An object has the following −
-
State
− Describes the object. The fields of a class represent the object’s state. -
Behavior
− Describes what an object can do. -
Identity
− A unique value that distinguishes an object from a set of similar other objects. Two or more objects can share the state and behavior but not the identity.
The period operator (.)
is used in conjunction with the object to access a class’ data members.
Dart represents data in the form of objects. Every class in Dart extends the Object class. Given below is a simple example of creating and using an object.
class Student {
void test_method() {
print("This is a test method");
}
void test_method1() {
print("This is a test method1");
}
}
void main() {
Student s1 = new Student();
s1.test_method();
s1.test_method1();
}
It should produce the following output
−
This is a test method
This is a test method1
The above example invokes the methods in the class. However, every time a function is called, a reference to the object is required. The cascade operator
can be used as a shorthand in cases where there is a sequence of invocations.
The cascade ( .. ) operator can be used to issue a sequence of calls via an object. The above example can be rewritten in the following manner.
class Student {
void test_method() {
print("This is a test method");
}
void test_method1() {
print("This is a test method1");
}
}
void main() {
new Student()
..test_method()
..test_method1();
}
It should produce the following output
−
This is a test method
This is a test method1
This function returns a string representation of an object. Take a look at the following example to understand how to use the toString
method.
void main() {
int n = 12;
print(n.toString());
}
It should produce the following output
−
12
Dart, unlike other programming languages, doesn’t support arrays. Dart collections can be used to replicate data structures like an array. The `dart:core library and other classes enable Collection support in Dart scripts.
Dart collections can be basically classified as −
Dart collection & Description
1 A List is simply an ordered group of objects. The dart:core
library provides the List class that enables creation and manipulation of lists.
-
Fixed Length List
− The list’s length cannot change at run-time. -
Growable List
− The list’s length can change at run-time.
void main() {
List logTypes = new List();
logTypes.add("WARNING");
logTypes.add("ERROR");
logTypes.add("INFO");
// iterating across list
for(String type in logTypes){
print(type);
}
// printing size of the list
print(logTypes.length);
logTypes.remove("WARNING");
print("size after removing.");
print(logTypes.length);
}
The output of the above code is as given below −
WARNING
ERROR
INFO
3
size after removing.
2
2 Set represents a collection of objects in which each object can occur only once. The dart:core
library provides the Set class to implement the same.
Syntax:
Identifier = new Set()
or Identifier = new Set.from(Iterable)
void main() {
Set numberSet = new Set();
numberSet.add(100);
numberSet.add(20);
numberSet.add(5);
numberSet.add(60);
numberSet.add(70);
print("Default implementation :${numberSet.runtimeType}");
// all elements are retrieved in the order in which they are inserted
for(var no in numberSet) {
print(no);
}
}
It should produce the following output −
100
20
5
60
70
Illustration: Set.from()
void main() {
Set numberSet = new Set.from([12,13,14]);
print("Default implementation :${numberSet.runtimeType}");
// all elements are retrieved in the order in which they are inserted
for(var no in numberSet) {
print(no);
}
}
It should produce the following output −
12
13
14
A HashSet is an unordered hash-table based Set implementation. The syntax for the same is − Identifier = new HashSet()
import 'dart:collection';
void main() {
Set numberSet = new HashSet();
numberSet.add(100);
numberSet.add(20);
numberSet.add(5);
numberSet.add(60);
numberSet.add(70);
print("Default implementation :${numberSet.runtimeType}");
for(var no in numberSet){
print(no);
}
}
It should produce the following output −
60
20
100
5
70
Adding Multiple Values to a HashSet
The addAll()
function allows adding multiple values to the HashSet. The following example illustrates the same −
Example:
import 'dart:collection';
void main() {
Set numberSet = new HashSet();
numberSet.addAll([100,200,300]);
print("Default implementation :${numberSet.runtimeType}");
for(var no in numberSet){
print(no);
}
}
It should produce the following output −
Default implementation :_HashSet
200
300
100
Removing Values from a HashSet
The remove()
function removes the value passed to it. The clear()
function removes all the entries from the HashSet.
import 'dart:collection';
void main() {
Set numberSet = new HashSet();
numberSet.addAll([100,200,300]);
print("Printing hashet.. ${numberSet}");
numberSet.remove(100);
print("Printing hashet.. ${numberSet}");
numberSet.clear();
print("Printing hashet.. ${numberSet}");
}
It should produce the following output −
Printing hashet.. {200, 300, 100}
Printing hashet.. {200, 300}
Printing hashet.. {}
3 The Map object is a simple key/value pair. Keys and values in a map may be of any type. A Map is a dynamic collection. In other words, Maps can grow and shrink at runtime. The Map class in the `dart:core library provides support for the same.
void main() {
var details = new Map();
details['Usrname']='admin';
details['Password']='admin@123';
print(details);
}
It should produce the following output −
{Usrname: admin, Password: admin@123}
A HashMap is a hash table based implementation of Map. When you iterate through a HashMap's keys or values, you cannot expect a certain order. The syntax for the same is as given below −
Identifier= new HashMap()
import 'dart:collection';
main() {
var accounts = new HashMap();
accounts['dept']='HR';
accounts['name']='Tom';
accounts['email']='tom@xyz.com';
print('Map after adding entries :${accounts}');
}
It should produce the following output −
Map after adding entries :{email: tom@xyz.com, dept: HR, name: Tom}
***Adding Multiple Values to a HashMap***
The HashMap class inherits the `addAll()` function from the Map class. This function enables adding multiple values all at once.
```dart
import 'dart:collection';
main() {
var accounts = new HashMap();
accounts.addAll({'dept':'HR','email':'tom@xyz.com'});
print('Map after adding entries :${accounts}');
}
It should produce the following output −
Map after adding entries :{email: tom@xyz.com, dept: HR}
Removing Values from a HashMap
The remove()
and the clear()
functions are used to remove entries from the HashMap. The remove()
function is passed a key that represents the entry to be removed. The clear()
function is used to remove all the entries from the Map.
Example
import 'dart:collection';
main() {
var accounts = new HashMap();
accounts['dept'] = 'HR';
accounts['name'] = 'Tom';
accounts['email'] = 'tom@xyz.com';
print('Map after adding entries :${accounts}');
accounts.remove('dept');
print('Map after removing entry :${accounts}');
accounts.clear();
print('Map after clearing entries :${accounts}');
}
It should produce the following output −
Map after adding entries :{email: tom@xyz.com, dept: HR, name: Tom}
Map after removing entry :{email: tom@xyz.com, name: Tom}
Map after clearing entries :{}
4 A Queue is a collection that can be manipulated at both ends. Queues are useful when you want to build a first-in, first-out collection. Simply put, a queue inserts data from one end and deletes from another end. The values are removed / read in the order of their insertion.
import 'dart:collection';
void main() {
Queue queue = new Queue();
print("Default implementation ${queue.runtimeType}");
queue.add(10);
queue.add(20);
queue.add(30);
queue.add(40);
for(var no in queue){
print(no);
}
}
It should produce the following output −
Default implementation ListQueue
10
20
30
40
Adding Multiple Values to a Queue The addAll() function enables adding multiple values to a queue, all at once. This function takes an iterable list of values. Example
import 'dart:collection';
void main() {
Queue queue = new Queue();
print("Default implementation ${queue.runtimeType}");
queue.addAll([10,12,13,14]);
for(var no in queue){
print(no);
}
}
It should produce the following output −
Default implementation ListQueue
10
12
13
14
Adding Value at the Beginning and End of a Queue
The addFirst()
method adds the specified value to the beginning of the queue. This function is passed an object that represents the value to be added. The addLast()
function adds the specified object to the end of the queue.
Example: addFirst()
The following example shows how you can add a value at the beginning of a Queue using the addFirst()
method −
import 'dart:collection';
void main() {
Queue numQ = new Queue();
numQ.addAll([100,200,300]);
print("Printing Q.. ${numQ}");
numQ.addFirst(400);
print("Printing Q.. ${numQ}");
}
It should produce the following output −
Printing Q.. {100, 200, 300}
Printing Q.. {400, 100, 200, 300}
Example : addLast()
The following example shows how you can add a value at the beginning of a Queue using the addLast()
method −
import 'dart:collection';
void main() {
Queue numQ = new Queue();
numQ.addAll([100,200,300]);
print("Printing Q.. ${numQ}");
numQ.addLast(400);
print("Printing Q.. ${numQ}");
}
It should produce the following output −
Printing Q.. {100, 200, 300}
Printing Q.. {100, 200, 300, 400}
The Iterator class from the ``dart:corelibrary enables easy collection traversal. Every collection has an
iterator` property. This property returns an iterator that points to the objects in the collection.
The following example illustrates traversing a collection using an iterator object.
import 'dart:collection';
void main() {
Queue numQ = new Queue();
numQ.addAll(\[100,200,300\]);
Iterator i= numQ.iterator;
while(i.moveNext()) {
print(i.current);
}
}
The moveNext()
function returns a Boolean value indicating whether there is a subsequent entry. The current
property of the iterator object returns the value of the object that the iterator currently points to.
This program should produce the following output
−
100
200
300
Dart is an optionally typed language
. Collections in Dart are heterogeneous by default. In other words, a single Dart collection can host values of various types. However, a Dart collection can be made to hold homogenous values. The concept of Generics can be used to achieve the same.
The use of Generics enforces a restriction on the data type of the values that can be contained by the collection. Such collections are termed as type-safe
collections. Type safety is a programming feature which ensures that a memory block can only contain data of a specific data type.
All Dart collections support type-safety
implementation via generics. A pair of angular brackets containing the data type is used to declare a type-safe collection. The syntax for declaring a type-safe collection is as given below.
Collection_name <data_type> identifier= new Collection_name<data_type>
The type-safe implementations of List, Map, Set and Queue is given below. This feature is also supported by all implementations of the above-mentioned collection types.
void main() {
List <String> logTypes = new List <String>();
logTypes.add("WARNING");
logTypes.add("ERROR");
logTypes.add("INFO");
// iterating across list
for (String type in logTypes) {
print(type);
}
}
It should produce the following output
−
WARNING
ERROR
INFO
An attempt to insert a value other than the specified type will result in a compilation error. The following example illustrates this.
void main() {
List <String> logTypes = new List <String>();
logTypes.add(1);
logTypes.add("ERROR");
logTypes.add("INFO");
//iterating across list
for (String type in logTypes) {
print(type);
}
}
It should produce the following output
−
1
ERROR
INFO
void main() {
Set <int>numberSet = new Set<int>();
numberSet.add(100);
numberSet.add(20);
numberSet.add(5);
numberSet.add(60);
numberSet.add(70);
// numberSet.add("Tom");
compilation error;
print("Default implementation :${numberSet.runtimeType}");
for(var no in numberSet) {
print(no);
}
}
It should produce the following output
−
Default implementation :_CompactLinkedHashSet<int>
100
20
5
60
70
import 'dart:collection';
void main() {
Queue<int> queue = new Queue<int>();
print("Default implementation ${queue.runtimeType}");
queue.addLast(10);
queue.addLast(20);
queue.addLast(30);
queue.addLast(40);
queue.removeFirst();
for(int no in queue){
print(no);
}
}
It should produce the following output
−
Default implementation ListQueue<int>
20
30
40
A type-safe map declaration specifies the data types of −
- The key
- The value
Map <Key_type, value_type>
void main() {
Map <String,String>m={'name':'Tom','Id':'E1001'};
print('Map :${m}');
}
It should produce the following output
−
Map :{name: Tom, Id: E1001}
A package is a mechanism to encapsulate a group of programming units. Applications might at times need integration of some third-party libraries or plugins. Every language has a mechanism for managing external packages like Maven or Gradle for Java, Nuget for .NET, npm for Node.js, etc.
The package manager for Dart is pub
.
Pub helps to install packages in the repository. The repository of packages hosted can be found at https://pub.dartlang.org/.
The package metadata
is defined in a file, pubsec.yaml
. YAML is the acronym for Yet Another Markup Language
. The pub
tool can be used to download all various libraries that an application requires.
Every Dart application has a pubspec.yaml
file which contains the application dependencies to other libraries and metadata of applications like application name, author, version, and description.
The contents of a pubspec.yaml
file should look something like this −
name: 'vector_victor'
version: 0.0.1
description: An absolute bare-bones web app.
...
dependencies: browser: '>=0.10.0 <0.11.0'
The important pub commands
are as follows −
Command & Description
1 ‘pub get’
- Helps to get all packages your application is depending on.
2 ‘pub upgrade’
- Upgrades all your dependencies to a newer version.
3 ‘pub build’
- This s used for building your web application and it will create a build folder , with all related scripts in it.
4 ‘pub help’
- This will give you help for all different pub commands.
If you are using an IDE like WebStorm, then you can right-click on the pubspec.yaml to get all the commands directly.
Consider an example where an application needs to parse xml. Dart XML is a lightweight library that is open source and stable for parsing, traversing, querying and building XML documents.
The steps for achieving the said task is as follows −
Step 1
− Add the following to the pubsec.yaml file.
name: TestApp
version: 0.0.1
description: A simple console application.
#dependencies:
# foo_bar: '>=1.0.0 <2.0.0'
dependencies: https://mail.google.com/mail/u/0/images/cleardot.gif
xml:
Right-click on the pubsec.yaml
and get dependencies. This will internally fire the pub get command
as shown below.
Resolving dependencies...
+ petitparser 1.5.1 (1.5.4 available)
+ xml 2.4.3 (2.4.4 available)
Downloading xml 2.4.3...
Downloading petitparser 1.5.3...
Changed 2 dependencies!
Process finished with exit code 0
The downloaded packages and its dependent packages can be verified under the packages folder.
Since installation is completed now, we need to refer the dart
xml` in the project. The syntax is as follows −
import 'package:xml/xml.dart' as xml;
To read XML string and verify the input, Dart XML uses a parse()
method. The syntax is as follows −
xml.parse(String input):
The following example shows how to parse XML string input −
import 'package:xml/xml.dart' as xml;
void main(){
print("xml");
var bookshelfXml = '''<?xml version = "1.0"?>
<bookshelf>
<book>
<title lang = "english">Growing a Language</title>
<price>29.99</price>
</book>
<book>
<title lang = "english">Learning XML</title>
<price>39.95</price>
</book>
<price>132.00</price>
</bookshelf>''';
var document = xml.parse(bookshelfXml);
print(document.toString());
}
It should produce the following output
−
xml
<?xml version = "1.0"?><bookshelf>
<book>
<title lang = "english">Growing a Language</title>
<price>29.99</price>
</book>
<book>
<title lang = "english">Learning XML</title>
<price>39.95</price>
</book>
<price>132.00</price>
</bookshelf>
An exception (or exceptional event) is a problem that arises during the execution of a program. When an Exception occurs the normal flow of the program is disrupted and the program/Application terminates abnormally.
Built-in Dart exceptions include −
Exceptions & Description
1 DeferredLoadException
- Thrown when a deferred library fails to load.
2 FormatException
- Exception thrown when a string or some other data does not have an expected format and cannot be parsed or processed.
3 IntegerDivisionByZeroException
- Thrown when a number is divided by zero.
4 IOException
- Base class for all Inupt-Output related exceptions.
5 IsolateSpawnException
- Thrown when an isolate cannot be created.
6 Timeout
- Thrown when a scheduled timeout happens while waiting for an async result.
Every exception in Dart is a subtype of the pre-defined class Exception
. Exceptions must be handled to prevent the application from terminating abruptly.
The try
block embeds code that might possibly result in an exception. The on block is used when the exception type needs to be specified. The catch
block is used when the handler needs the exception object.
The try
block must be followed by either exactly one on / catch
block or one finally
block (or one of both). When an exception occurs in the try block, the control is transferred to the catch
.
The syntax
for handling an exception is as given below −
try {
// code that might throw an exception
}
on Exception1 {
// code for handling exception
}
catch Exception2 {
// code for handling exception
}
Following are some points to remember −
-
A code snippet can have more than one on / catch blocks to handle multiple exceptions.
-
The on block and the catch block are mutually inclusive, i.e. a try block can be associated with both- the on block and the catch block.
The following code illustrates exception handling in Dart −
The following program divides two numbers represented by the variables x
and y
respectively. The code throws an exception since it attempts division by zero. The on block
contains the code to handle this exception.
main() {
int x = 12;
int y = 0;
int res;
try {
res = x ~/ y;
}
on IntegerDivisionByZeroException {
print('Cannot divide by zero');
}
}
It should produce the following output
−
Cannot divide by zero
In the following example, we have used the same code as above. The only difference is that the catch block
(instead of the ON block) here contains the code to handle the exception. The parameter of catch
contains the exception object thrown at runtime.
main() {
int x = 12;
int y = 0;
int res;
try {
res = x ~/ y;
}
catch(e) {
print(e);
}
}
It should produce the following output
−
IntegerDivisionByZeroException
The following example shows how to use the on...catch
block.
main() {
int x = 12;
int y = 0;
int res;
try {
res = x ~/ y;
}
on IntegerDivisionByZeroException catch(e) {
print(e);
}
}
It should produce the following output
−
IntegerDivisionByZeroException
The finally
block includes code that should be executed irrespective of an exception’s occurrence. The optional finally
block executes unconditionally after try/on/catch
.
The syntax for using the finally
block is as follows −
try {
// code that might throw an exception
}
on Exception1 {
// exception handling code
}
catch Exception2 {
// exception handling
}
finally {
// code that should always execute; irrespective of the exception
}
The following example illustrates the use of finally
block.
main() {
int x = 12;
int y = 0;
int res;
try {
res = x ~/ y;
}
on IntegerDivisionByZeroException {
print('Cannot divide by zero');
}
finally {
print('Finally block executed');
}
}
It should produce the following output
−
Cannot divide by zero
Finally block executed
The throw
keyword is used to explicitly raise an exception. A raised exception should be handled to prevent the program from exiting abruptly.
The syntax
for raising an exception explicitly is −
throw new Exception_name()
The following example shows how to use the throw
keyword to throw an exception −
main() {
try {
test_age(-2);
}
catch(e) {
print('Age cannot be negative');
}
}
void test_age(int age) {
if(age<0) {
throw new FormatException();
}
}
It should produce the following output
−
Age cannot be negative
As specified above, every exception type in Dart is a subtype of the built-in class Exception
. Dart enables creating custom exceptions by extending the existing ones. The syntax for defining a custom exception is as given below −
class Custom_exception_Name implements Exception {
// can contain constructors, variables and methods
}
Custom Exceptions should be raised explicitly and the same should be handled in the code.
The following example shows how to define and handle a custom exception.
class AmtException implements Exception {
String errMsg() => 'Amount should be greater than zero';
}
void main() {
try {
withdraw_amt(-1);
}
catch(e) {
print(e.errMsg());
}
finally {
print('Ending requested operation.....');
}
}
void withdraw_amt(int amt) {
if (amt <= 0) {
throw new AmtException();
}
}
In the above code, we are defining a custom exception, AmtException
. The code raises the exception if the amount passed is not within the excepted range. The main
function encloses the function invocation in the try...catch
block.
The code should produce the following output
−
Amount should be greater than zero
Ending requested operation....
Every now and then, developers commit mistakes while coding. A mistake in a program is referred to as a bug. The process of finding and fixing bugs is called debugging and is a normal part of the development process. This section covers tools and techniques that can help you with debugging tasks.
The WebStorm editor enables breakpoints and step-by-step debugging. The program will break at the point where the breakpoint is attached. This functionality is like what you might expect from Java or C# application development. You can watch variables, browse the stack, step over and step into method and function calls, all from the WebStorm Editor.
Consider the following code snippet. (TestString.dart)
void main() {
int a = 10, b = 20, c = 5;
c = c * c * c;
print("$a + $b = ${a+b}");
print("$a%$b = ${a%b}"); // Add a break point here
print("$a*$b = ${a*b}");
print("$a/$b = ${a/b}");
print(c);
}
To add a breakpoint
, click on the left margin to.
Run the program in debug mode
. In the project explorer right click on the dart
program in our case TestString.dart.
Once the program runs in debug mode, you will get the Debugger window as shown in the following screenshot. The variables tab shows the values of variables in the current context. You can add watchers for specific variables and listen to that values changes using watches window.
Step Into
(F7) arrow icon on debug menu helps to Executes code one statement at a time. If main methods call a subroutine, then this will go into the subroutine code also.
Step over
(F8): It is similar to Step Into
. The difference in use occurs when the current statement contains a call to a subroutine. If the main method calls a subroutine, step over will not drill into the subroutine. it will skip the subroutine.
Step Out
(Shift+F8): Executes the remaining lines of a function in which the current execution point lies. The next statement displayed is the statement following the subroutine call.
After running in debug mode, the program gives the following output
−
10 + 20 = 30
10 % 20 = 10
10 * 20 = 200
10 / 20 = 0.5
125
A typedef
, or a function-type alias, helps to define pointers to executable code within memory. Simply put, a typedef
can be used as a pointer that references a function.
Given below are the steps to implement typedefs
in a Dart program.
Step 1: Defining a typedef
A typedef
can be used to specify a function signature that we want specific functions to match. A function signature is defined by a function’s parameters (including their types). The return type is not a part of the function signature. Its syntax is as follows.
typedef function_name(parameters)
Step 2: Assigning a Function to a typedef Variable
A variable of typedef
can point to any function having the same signature as typedef
. You can use the following signature to assign a function to a typedef
variable.
type_def var_name = function_name
Step 3: Invoking a Function
The typedef
variable can be used to invoke functions. Here is how you can invoke a function −
var_name(parameters)
Let’s now take an example to understand more on typedef
in Dart.
At first, let us define a typedef
. Here we are defining a function signature. The function will take two input parameters of the type integer
. Return type is not a part of the function signature.
typedef ManyOperation(int firstNo , int secondNo); //function signature
Next, let us define the functions. Define some functions with the same function signature as that of the ManyOperation typedef
.
Add(int firstNo,int second){
print("Add result is ${firstNo+second}");
}
Subtract(int firstNo,int second){
print("Subtract result is ${firstNo-second}");
}
Divide(int firstNo,int second){
print("Add result is ${firstNo/second}");
}
Finally, we will invoke the function via typedef
. Declare a variable of the ManyOperations type. Assign the function name to the declared variable.
ManyOperation oper ;
//can point to any method of same signature
oper = Add;
oper(10,20);
oper = Subtract;
oper(30,20);
oper = Divide;
oper(50,5);
The oper
variable can point to any method which takes two integer parameters. The Add
function's reference is assigned to the variable. Typedefs can switch function references at runtime
Let us now put all the parts together and see the complete program.
typedef ManyOperation(int firstNo , int secondNo);
//function signature
Add(int firstNo,int second){
print("Add result is ${firstNo+second}");
}
Subtract(int firstNo,int second){
print("Subtract result is ${firstNo-second}");
}
Divide(int firstNo,int second){
print("Divide result is ${firstNo/second}");
}
Calculator(int a, int b, ManyOperation oper){
print("Inside calculator");
oper(a,b);
}
void main(){
ManyOperation oper = Add;
oper(10,20);
oper = Subtract;
oper(30,20);
oper = Divide;
oper(50,5);
}
The program should produce the following output
−
Add result is 30
Subtract result is 10
Divide result is 10.0
Note
− The above code will result in an error if the typedef
variable tries to point to a function with a different function signature.
Typedefs
can also be passed as a parameter to a function. Consider the following example −
typedef ManyOperation(int firstNo , int secondNo); //function signature
Add(int firstNo,int second){
print("Add result is ${firstNo+second}");
}
Subtract(int firstNo,int second){
print("Subtract result is ${firstNo-second}");
}
Divide(int firstNo,int second){
print("Divide result is ${firstNo/second}");
}
Calculator(int a,int b ,ManyOperation oper){
print("Inside calculator");
oper(a,b);
}
main(){
Calculator(5,5,Add);
Calculator(5,5,Subtract);
Calculator(5,5,Divide);
}
It will produce the following output
−
Inside calculator
Add result is 10
Inside calculator
Subtract result is 0
Inside calculator
Divide result is 1.0
A library in a programming language represents a collection of routines (set of programming instructions). Dart has a set of built-in libraries that are useful to store routines that are frequently used. A Dart library comprises of a set of classes, constants, functions, typedefs, properties, and exceptions.
Importing makes the components in a library available to the caller code. The import keyword is used to achieve the same. A dart
file can have multiple import statements.
Built in Dart library URIs use the dart: scheme
to refer to a library. Other libraries can use a file system path or the package: scheme
to specify its URI. Libraries provided by a package manager such as the pub tool uses the package: scheme.
The syntax for importing a library in Dart is given below −
import 'URI'
Consider the following code snippet −
import 'dart:io'
import 'package:lib1/libfile.dart'
If you want to use only part of a library, you can selectively import the library. The syntax for the same is given below −
import 'package: lib1/lib1.dart' show foo, bar;
// Import only foo and bar.
import 'package: mylib/mylib.dart' hide foo;
// Import all names except foo
Some commonly used libraries are given below −
1 dart:io
File, socket, HTTP, and other I/O support for server applications. This library does not work in browser-based applications. This library is imported by default.
2 dart:core
Built-in types, collections, and other core functionality for every Dart program. This library is automatically imported.
3 dart: math
Mathematical constants and functions, plus a random number generator.
4 dart: convert
Encoders and decoders for converting between different data representations, including JSON and UTF-8.
5 dart: typed_data
Lists that efficiently handle fixed sized data (for example, unsigned 8 byte integers).
The following example imports the built-in library ``dart: math. The snippet calls the
sqrt()` function from the `math` library. This function returns the square root of a number passed to it.
import 'dart:math'; void main() { print("Square root of 36 is: ${sqrt(36)}"); }
Output
Square root of 36 is: 6.0
Dart scripts can prefix identifiers with an underscore ( _ ) to mark its components private. Simply put, Dart libraries can restrict access to its content by external scripts. This is termed as encapsulation
. The syntax for the same is given below −
_identifier
At first, define a library with a private function.
library loggerlib;
void _log(msg) {
print("Log method called in loggerlib msg:$msg");
}
Next, import the library
import 'test.dart' as web;
void main() {
web._log("hello from webloggerlib");
}
The above code will result in an error.
common-syntax.dart:53:3: Error: Method not found: 'log'.
web.log("hello from webloggerlib");
Dart also allows you to use your own code as a library. Creating a custom library involves the following steps −
Step 1: Declaring a Library
To explicitly declare a library, use the library statement
. The syntax for declaring a library is as given below −
library library_name
// library contents go here
Step 2: Associating a Library
You can associate a library in two ways −
- Within the same directory
import 'library_name'
- From a different directory
import 'dir/library_name'
First, let us define a custom library, calculator.dart
.
library calculator_lib;
import 'dart:math';
//import statement after the libaray statement
int add(int firstNumber,int secondNumber){
print("inside add method of Calculator Library ") ;
return firstNumber+secondNumber;
}
int modulus(int firstNumber,int secondNumber){
print("inside modulus method of Calculator Library ") ;
return firstNumber%secondNumber;
}
int random(int no){
return new Random().nextInt(no);
}
Next, we will import the library −
import 'calculator.dart';
void main() {
var num1 = 10;
var num2 = 20;
var sum = add(num1,num2);
var mod = modulus(num1,num2);
var r = random(10);
print("$num1 + $num2 = $sum");
print("$num1 % $num2= $mod");
print("random no $r");
}
The program should produce the following output
−
inside add method of Calculator Library
inside modulus method of Calculator Library
10 + 20 = 30
10 % 20= 10
random no 0
If you import two libraries with conflicting identifiers, then you can specify a prefix for one or both libraries. Use the 'as' keyword for specifying the prefix. The syntax for the same is given below −
import 'library_uri' as prefix
First, let us define a library: loggerlib.dart
.
library loggerlib;
void log(msg){
print("Log method called in loggerlib msg:$msg");
}
Next, we will define another library: webloggerlib.dart
.
library webloggerlib;
void log(msg){
print("Log method called in webloggerlib msg:$msg");
}
Finally, we will import the library with a prefix.
import 'loggerlib.dart';
import 'webloggerlib.dart' as web;
// prefix avoids function name clashes
void main(){
log("hello from loggerlib");
web.log("hello from webloggerlib");
}
It will produce the following output
−
Log method called in loggerlib msg:hello from loggerlib
Log method called in webloggerlib msg:hello from webloggerlib
An asynchronous operation
executes in a thread, separate from the main
application thread. When an application calls a method to perform an operation asynchronously, the application can continue executing while the asynchronous method performs its task.
Let’s take an example to understand this concept. Here, the program accepts user input using the IO library
.
import 'dart:io';
void main() {
print("Enter your name :");
// prompt for user input
String name = stdin.readLineSync();
// this is a synchronous method that reads user input
print("Hello Mr. ${name}");
print("End of main");
}
The readLineSync()
is a synchronous method. This means that the execution of all instructions that follow the readLineSync()
function call will be blocked till the readLineSync()
method finishes execution.
The stdin.readLineSync
waits for input. It stops in its tracks and does not execute any further until it receives the user’s input.
The above example will result in the following output
−
Enter your name :
Adarsh
// reads user input
Hello Mr. Adarsh
End of main
In computing, we say something is synchronous
when it waits for an event to happen before continuing. A disadvantage in this approach is that if a part of the code takes too long to execute, the subsequent blocks, though unrelated, will be blocked from executing. Consider a webserver that must respond to multiple requests for a resource.
A synchronous execution model will block every other user’s request till it finishes processing the current request. In such a case, like that of a web server, every request must be independent of the others. This means, the webserver should not wait for the current request to finish executing before it responds to request from other users.
Simply put, it should accept requests from new users before necessarily completing the requests of previous users. This is termed as asynchronous. Asynchronous programming basically means no waiting or non-blocking programming model. The dart:async
package facilitates implementing asynchronous programming blocks in a Dart script.
The following example better illustrates the functioning of an asynchronous block.
Step 1
− Create a contact.txt
file as given below and
save it in the data folder in the current project.
1, Tom 2, John 3, Tim 4, Jane
Step 2
− Write a program which will read the file without blocking other parts of the application.
import "`dart:async";
import "`dart:io";
void main(){
File file = new File( Directory.current.path+"\\\\data\\\\contact.txt");
Future<String> f = file.readAsString();
// returns a futrue, this is Async method
f.then((data)=>print(data));
// once file is read , call back method is invoked
print("End of main");
// this get printed first, showing fileReading is non blocking or async
}
The output
of this program will be as follows −
End of main 1, Tom 2, John 3, Tim 4, Jan
The "end of main" executes first while the script continues reading the file. The Future
class, part of ``dart:async, is used for getting the result of a computation after an asynchronous task has completed. This
Future` value is then used to do something after the computation finishes.
Once the read operation is completed, the execution control is transferred within "then()"
. This is because the reading operation can take more time and so it doesn’t want to block other part of program.
The Dart community defines a Future
as "a means for getting a value sometime in the future." Simply put, Future objects
are a mechanism to represent values returned by an expression whose execution will complete at a later point in time. Several of Dart’s built-in classes return a Future
when an asynchronous method is called.
Dart is a single-threaded programming language. If any code blocks the thread of execution (for example, by waiting for a time-consuming operation or blocking on I/O), the program effectively freezes.
Asynchronous operations let your program run without getting blocked. Dart uses Future objects
to represent asynchronous operations.
Concurrency
is the execution of several instruction sequences at the same time. It involves performing more than one task simultaneously.
Dart uses Isolates
as a tool for doing works in parallel. The ``dart:isolate` package is Dart’s solution to taking single-threaded Dart code and allowing the application to make greater use of the hard-ware available.
Isolates
, as the name suggests, are isolated units of running code. The only way to send data between them is by passing messages, like the way you pass messages between the client and the server. An isolate
helps the program to take advantage of multicore microprocessors out of the box.
Let’s take an example to understand this concept better.
import 'dart:isolate';
void foo(var message){
print('execution from foo ... the message is :${message}');
}
void main(){
Isolate.spawn(foo,'Hello!!');
Isolate.spawn(foo,'Greetings!!');
Isolate.spawn(foo,'Welcome!!');
print('execution from main1');
print('execution from main2');
print('execution from main3');
}
Here, the spawn
method of the Isolate
class facilitates running a function, foo
, in parallel with the rest of our code. The spawn
function takes two parameters −
- the function to be spawned, and
- an object that will be passed to the spawned function.
In case there is no object to pass to the spawned function, it can be passed a NULL value.
The two functions (foo and main)
might not necessarily run in the same order each time. There is no guarantee as to when foo
will be executing and when main()
will be executing. The output will be different each time you run.
execution from main1
execution from main2
execution from main3
execution from foo ... the message is :Hello!!
execution from main1
execution from main2
execution from main3
execution from foo ... the message is :Welcome!!
execution from foo ... the message is :Hello!!
execution from foo ... the message is :Greetings!!
From the outputs, we can conclude that the Dart code can spawn a new isolate
from running code like the way Java or C# code can start a new thread.
Isolates
differ from threads in that an isolate
has its own memory. There’s no way to share a variable between isolates
—the only way to communicate between isolates
is via message passing.
Note
− The above output will be different for different hardware and operating system configurations.
Doing complex computational work asynchronously is important to ensure responsiveness of applications. Dart Future
is a mechanism for retrieving the value of an asynchronous task after it has completed, while Dart Isolates
are a tool for abstracting parallelism and implementing it on a practical high-level basis.
Unit Testing involves testing every individual unit of an application. It helps the developer to test small functionalities without running the entire complex application.
The Dart external library
named "test" provides a standard way of writing and running unit tests.
Dart unit testing involves the following steps −
Step 1: Installing the "test" package
To installing third-party packages in the current project, you will require the pubspec.yaml
file. To install test packages
, first make the following entry in the pubspec.yaml
file −
name: dart_getting_started
description: A simple tutorial for beginners
dependencies:
test:^1.5.1+1
Adarsh:dart-getting-started adarshmaurya$ pub get
Resolving dependencies... (15.7s)
+ analyzer 0.34.0
+ args 1.5.1
+ async 2.0.8
+ boolean_selector 1.0.4
+ charcode 1.1.2
+ collection 1.14.11
+ convert 2.0.2
+ crypto 2.0.6
+ csslib 0.14.6
+ front_end 0.1.7
+ glob 1.1.7
+ html 0.13.3+3
+ http 0.12.0
+ http_multi_server 2.0.5
+ http_parser 3.1.3
+ io 0.3.3
+ js 0.6.1+1
+ json_rpc_2 2.0.9
+ kernel 0.3.7
+ logging 0.11.3+2
+ matcher 0.12.3+1 (0.12.4 available)
+ meta 1.1.6
+ mime 0.9.6+2
+ multi_server_socket 1.0.2
+ node_preamble 1.4.4
+ package_config 1.0.5
+ package_resolver 1.0.6
+ path 1.6.2
+ plugin 0.2.0+3
+ pool 1.3.6
+ pub_semver 1.4.2
+ shelf 0.7.3+3
+ shelf_packages_handler 1.0.4
+ shelf_static 0.2.8
+ shelf_web_socket 0.2.2+4
+ source_map_stack_trace 1.1.5
+ source_maps 0.10.8
+ source_span 1.4.1
+ stack_trace 1.9.3
+ stream_channel 1.6.8
+ string_scanner 1.0.4
+ term_glyph 1.0.1
+ test 1.5.1+1
+ test_api 0.2.1
+ test_core 0.2.0+1
+ typed_data 1.1.6
+ utf 0.9.0+5
+ vm_service_client 0.2.6
+ watcher 0.9.7+10
+ web_socket_channel 1.0.9
+ yaml 2.1.15
Changed 51 dependencies!
Precompiling executables... (9.3s)
Precompiled test:test.
Adarsh:dart-getting-started adarshmaurya$
After making the entry, right-click the pubspec.yaml
file and get dependencies. It will install the "test"
package. Given below is a screenshot for the same in the WebStorm
Editor.
New >
Pub: Get Dependencies
Pub: Upgrade Dependencies
Pub: Build...
Packages can be installed from the command line
too. Type the following in the terminal −
pub get
Step 2: Importing the "test" package
import "package:test/test.dart";
Step 3: Writing Tests
Tests are specified using the top-level function test()
, while test assertions
are made using the expect()
function. For using these methods, they should be installed as a pub
dependency.
test("Description of the test ", () {
expect(actualValue , matchingValue)
});
The group()
function can be used to group tests. Each group's description is added to the beginning of its test's descriptions.
group("some_Group_Name", () {
test("test_name_1", () {
expect(actual, equals(exptected));
});
test("test_name_2", () {
expect(actual, equals(expected));
});
})
The following example defines a method Add()
. This method takes two integer values and returns an integer representing the sum
. To test this add()
method −
Step 1
− Import the test
package as given below.
Step 2
− Define the test using the test()
function. Here, the test()
function uses the expect()
function to enforce an assertion.
import 'package:test/test.dart';
// Import the test package
int Add(int x,int y)
// Function to be tested {
return x+y;
}
void main() {
// Define the test
test("test to check add method",(){
// Arrange
var expected = 30;
// Act
var actual = Add(10,20);
// Asset
expect(actual,expected);
});
}
It should produce the following output
−
00:00 +0: test to check add method
00:00 +1: All tests passed!
The subtract()
method defined below has a logical mistake. The following test
verifies the same.
import 'package:test/test.dart';
int Add(int x,int y){
return x+y;
}
int Sub(int x,int y){
return x-y-1;
}
void main(){
test('test to check sub',(){
var expected = 10;
// Arrange
var actual = Sub(30,20);
// Act
expect(actual,expected);
// Assert
});
test("test to check add method",(){
var expected = 30;
// Arrange
var actual = Add(10,20);
// Act
expect(actual,expected);
// Asset
});
}
Output
− The test case for the function add()
passes but the test for subtract()
fails as shown below.
00:00 +0: test to check sub
00:00 +0 -1: test to check sub
Expected: <10>
Actual: <9>
package:test expect
bin\\Test123.dart 18:5 main.<fn>
00:00 +0 -1: test to check add method
00:00 +1 -1: Some tests failed.
Unhandled exception:
Dummy exception to set exit code.
#0 _rootHandleUncaughtError.<anonymous closure> (`dart:async/zone.dart:938)
#1 _microtaskLoop (`dart:async/schedule_microtask.dart:41)
#2 _startMicrotaskLoop (`dart:async/schedule_microtask.dart:50)
#3 _Timer._runTimers (`dart:isolate-patch/timer_impl.dart:394)
#4 _Timer._handleMessage (`dart:isolate-patch/timer_impl.dart:414)
#5 _RawReceivePortImpl._handleMessage (`dart:isolate-patch/isolate_patch.dart:148)
Grouping Test Cases
-------------------
You can group the `test cases` so that it adds more meaning to you test code. If you have many `test cases` this helps to write much cleaner code.
In the given code, we are writing a test case for the `split()` function and the `trim` function. Hence, we logically group these test cases and call it `String`.
import "package:test/test.dart";
void main() {
group("String", () {
test("test on split() method of string class", () {
var string = "foo,bar,baz";
expect(string.split(","), equals(\["foo", "bar", "baz"\]));
});
test("test on trim() method of string class", () {
var string = " foo ";
expect(string.trim(), equals("foo"));
});
});
}
Output
− The output will append the group name for each test case as given below −
00:00 +0: String test on split() method of string class
00:00 +1: String test on trim() method of string class
00:00 +2: All tests passed
Every webpage resides inside a browser window which can be considered as an object.
A Document object
represents the HTML document that is displayed in that window. The Document object has various properties that refer to other objects which allow access to and modification of document content.
The way a document content is accessed and modified is called the Document Object Model
, or DOM
. The Objects are organized in a hierarchy. This hierarchical structure applies to the organization of objects in a Web document.
Window
|
------------------------------
| | |
History Document Location
|
Form
|
FormControlElements
-
Window
− Top of the hierarchy. It is the outmost element of the object hierarchy. -
Document
− Each HTML document that gets loaded into a window becomes a document object. The document contains the contents of the page. -
Elements
− represent the content on a web page. Examples include the text boxes, page title etc. -
Nodes
− are often elements, but they can also be attributes, text, comments, and other DOM types.
Here is a simple hierarchy of a few important DOM objects −
Dart provides the dart:html
library to manipulate objects and elements in the DOM. Console-based applications cannot use the dart:html
library. To use the HTML library in the web applications, import dart:html
−
import 'dart:html';
Moving on, we will discuss some DOM Operations
in the next section.
The dart:html
library provides the querySelector
function to search for elements in the DOM.
Element querySelector(String selectors);
The querySelector()
function returns the first element that matches the specified group of selectors. "selectors
should be string using CSS selector syntax as given below
var element1 = document.querySelector('.className');
var element2 = document.querySelector('#id');
Follow the steps given below, in the Webstorm IDE −
Step 1
− File NewProject → In the location, provide the project name as DemoWebApp
.
Step 1
− In the section "Generate sample content", select SimpleWebApplication
.
It will create a sample project, DemoWebApp
. There is a pubspec.yaml
file containing the dependencies which need to be downloaded.
name: 'DemoWebApp'
version: 0.0.1
description: An absolute bare-bones web app.
#author: Your Name <email@example.com>
#homepage: https://www.example.com
environment:
sdk: '>=1.0.0 <2.0.0'
dependencies:
browser: '>=0.10.0 <0.11.0' `dart`_to_js_script_rewriter: '^1.0.1'
transformers:
- `dart`_to_js_script_rewriter
If you are connected to Web, then these will be downloaded automatically, else you can right-click on the pubspec.yaml
and get dependencies.
In the web folder, you will find three files: Index.html, main.dart
, and style.css
<!DOCTYPE html>
<html>
<head>
<meta charset = "utf-8">
<meta http-equiv = "X-UA-Compatible" content = "IE = edge">
<meta name = "viewport" content = "width = device-width, initial-scale = 1.0">
<meta name = "scaffolded-by" content = "https://github.com/google/stagehand">
<title>DemoWebApp</title>
<link rel = "stylesheet" href = "styles.css">
<script defer src = "main.dart" type = "application/`dart`"></script>
<script defer src = "packages/browser/`dart`.js"></script>
</head>
<body>
<h1>
<div id = "output"></div>
</h1>
</body>
</html>
import 'dart:html';
void main() {
querySelector('#output').text = 'Your Dart web dom app is running!!!.';
}
Run the index.html
file; you will see the following output on your screen.
The dart:html
library provides the onClick
event for DOM Elements. The syntax shows how an element could handle a stream of click events.
querySelector('#Id').onClick.listen(eventHanlderFunction);
The querySelector()
function returns the element from the given DOM and onClick.listen()
will take an eventHandler
method which will be invoked when a click event is raised. The syntax of eventHandler
is given below −
void eventHanlderFunction (MouseEvent event){ }
Let us now take an example to understand the concept of Event Handling in Dart.
<!DOCTYPE html>
<html>
<head>
<meta charset = "utf-8">
<meta http-equiv = "X-UA-Compatible" content = "IE = edge">
<meta name = "viewport" content = "width = device-width, initial-scale = 1.0">
<meta name = "scaffolded-by" content ="https://github.com/google/stagehand">
<title>DemoWebApp</title>
<link rel = "stylesheet" href = "styles.css">
<script defer src = "TestEvent.dart" type="application/`dart`"></script>
<script defer src = "packages/browser/`dart`.js"></script>
</head>
<body>
<div id = "output"></div>
<h1>
<div>
Enter you name : <input type = "text" id = "txtName">
<input type = "button" id = "btnWish" value="Wish">
</div>
</h1>
<h2 id = "display"></h2>
</body>
</html>
import 'dart:html';
void main() {
querySelector('#btnWish').onClick.listen(wishHandler);
}
void wishHandler(MouseEvent event){
String name = (querySelector('#txtName') as InputElement).value;
querySelector('#display').text = 'Hello Mr.'+ name;
}