0% found this document useful (0 votes)
15 views37 pages

Dart Programming Language Basics Guide

The document provides an overview of the Dart programming language, covering topics such as creating a simple console application, variable types, conditional expressions, and collection types like lists, sets, and maps. It also explains advanced features like records, spread operators, control-flow operators, and pattern matching. The content is structured to serve as a guide for understanding Dart syntax and functionality before a midterm examination.

Uploaded by

ZAIN Ul ABIDEEN
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
15 views37 pages

Dart Programming Language Basics Guide

The document provides an overview of the Dart programming language, covering topics such as creating a simple console application, variable types, conditional expressions, and collection types like lists, sets, and maps. It also explains advanced features like records, spread operators, control-flow operators, and pattern matching. The content is structured to serve as a guide for understanding Dart syntax and functionality before a midterm examination.

Uploaded by

ZAIN Ul ABIDEEN
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Dart Programming Language

Before Midterm

Simple Hello World App


Create a Dart console application using Visual Studio Code. The following code will be generated. The
calculate() function is defined in lib/[Link].

import 'package:dartproject/[Link]' as dartproject;

void main(List<String> arguments) {


print('Hello world: ${[Link]()}!');

Passing arguments through console


import 'package:dartproject/[Link]' as dartproject;

void main(List<String> arguments) {


print('Arguments: $arguments');
}

To pass the parameters to the above application using console, run the following command in console:

A:\AAA\flutterprojects\dartproject\bin> dart [Link] one two three

Variables
Variable creation and initialization.
var name = 'Bob';
print(name);

Changing value of variable.


var name = 'Bob';
name = 10; // not allowed to change data type
print(name);
The following code will work.

We use Object data type, we want to change data type of variable.


Object name = 'Bob';
name = 10;
print(name);

Explicit data types


String name = 'Bob';
print(name);

Nullable type
String? name1; // Nullable type. Can be `null` or string.

String name2; // Non-nullable type. Cannot be `null` but can be string.

print(name1); // null
// print(name2); // error

Late variables
In Dart, the late keyword is used to declare variables that will be initialized at a later point in time. This
can be particularly useful in two main scenarios:

1. Non-nullable Variables: When migrating to null safety, you might have variables that cannot be
initialized immediately. By using late, you can declare these variables and ensure they are
initialized before use. For example:

late String title;

void getTitle() {

title = 'Default';

print('Title is $title');

In this example, title is declared as a late variable and is initialized later in the getTitle function 1.
2. Lazy Initialization: This allows for initializing a variable only when it is first used, which can be
beneficial if the initialization is resource-intensive. For instance:

late String result = _getResult();

String _getResult() {

// Expensive computation

return 'Computed Result';

Here, _getResult() is only called when result is accessed for the first time2.

Using late ensures that the variable is not initialized until it is actually needed, which can help optimize
performance and resource usage3.

Final and Const


A final variable can be set only once; a const variable is a compile-time constant. (Const variables are
implicitly final.)

void main() {
final name = 'Bob'; // Without a type annotation
final String nickname = 'Bobby';

print(name);
print(nickname);

// name = 'Ali'; // This is error as value of final cannot be changed


// after it is initialized.
}

Constant
void main() {
const bar = 1000000; // Unit of pressure (dynes/cm2)
const double atm = 1.01325 * bar; // Standard atmosphere
}

Variable examples

void main() {
var name = 'Voyager I';
var year = 1977;
var antennaDiameter = 3.7;
var flybyObjects = ['Jupiter', 'Saturn', 'Uranus', 'Neptune'];
var image = {
'tags': ['saturn'],
'url': '//path/to/[Link]'
};

print(name);
print(year);
print(antennaDiameter);
print(flybyObjects);
print(image);

Conditional expressions:
condition ? expr1 : expr2
If condition is true, evaluates expr1 (and returns its value); otherwise,
evaluates and returns the value of expr2.

void main()
{
var isPublic = 'public';
var visibility = isPublic=='public' ? 'public' : 'private';

print(visibility);

expr1 ?? expr2
If expr1 is non-null, returns its value; otherwise, evaluates and returns
the value of expr2.

void main()
{
var var1 = null; // or simply write var var1; this defaults to null
var var2 = 10;

var ans = var1 ?? var2;


print(ans);
}
Comments:
// single line , /* multi line */, /// for documentation.

Builtin-Types:
Numbers, Strings, Booleans.

Strings:
String concatenation example:
void main() {
var s1 = 'String '
'concatenation'
" works even over line breaks.";

print(s1);
}

Multiline string:
void main() {

var s1 = '''
You can create
multi-line strings like this one.
''';

print(s1);
}

Records
Records are an anonymous, immutable, aggregate type. Like other collection types,
they let you bundle multiple objects into a single object. Unlike other collection
types, records are fixed-sized, heterogeneous, and typed.

Records are real values; you can store them in variables, nest them, pass them to and from functions,
and store them in data structures such as lists, maps, and sets.

Example:

void main() {

// Record type annotation in a variable declaration:


({int a, bool b}) record;

// Initialize it with a record expression:


record = (a: 123, b: true);

print(record);
print(record.a);
print(record.b);

Records expressions are comma-delimited lists of named or positional fields, enclosed in


parentheses:

dart

Example:

Here ‘first’, “hello”, ‘last’ are positional fields, and others are named.

void main() {

var record = ('first', a: 2, "hello", b: true, 'last');

print(record.$1); // Prints 'first'


print(record.a); // Prints 2
print(record.b); // Prints true
print(record.$2); // Prints 'last'
print(record.$3);

}
In a record type annotation, named fields go inside a curly brace-delimited section
of type-and-name pairs, after all positional fields. In a record expression, the names
go before each field value with a colon after:

// Record type annotation in a variable declaration:


({int a, bool b}) record;

// Initialize it with a record expression:


record = (a: 123, b: true);

The names of named fields in a record type are part of the record's type definition,
or its shape. Two records with named fields with different names have different
types:

({int a, int b}) recordAB = (a: 1, b: 2);


({int x, int y}) recordXY = (x: 3, y: 4);

// Compile error! These records don't have the same type.


// recordAB = recordXY;

In a record type annotation, you can also name the positional fields, but these
names are purely for documentation and don't affect the record's type:

(int a, int b) recordAB = (1, 2);


(int x, int y) recordXY = (3, 4);

recordAB = recordXY; // OK.

Example

(int x, int y, int z) point = (1, 2, 3);


(int r, int g, int b) color = (1, 2, 3);

print(point == color); // Prints 'true'.

Example

({int x, int y, int z}) point = (x: 1, y: 2, z: 3);


({int r, int g, int b}) color = (r: 1, g: 2, b: 3);
print(point == color); // Prints 'false'. Lint: Equals on unrelated
types.

Lists
Perhaps the most common collection in nearly every programming language is the array, or ordered
group of objects. In Dart, arrays are List objects, so most people just call them lists.

Dart list literals are denoted by a comma separated list of expressions or values, enclosed in square
brackets ([]). Here's a simple Dart list:

void main() {

var list = [1, 2, 3];

print(list[0]);
}

List of Records:
void main() {

var list = [(id: 1, name:'Ali'), (id: 2, name:'javed')];

for( var item in list) {


print([Link]);
}
}

Sets
#

A set in Dart is an unordered collection of unique items. Dart support for sets is provided by set literals
and the Set type.

Here is a simple Dart set, created using a set literal:

void main() {

var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};


print([Link](0));
print([Link](1));

for (var element in halogens)


{
print(element);
}
}

To create an empty set, use {} preceded by a type argument, or assign {} to a variable of type Set:

void main() {

var names = <String>{};


// Set<String> names = {}; // This works, too.
// var names = {}; // Creates a map, not a set.

Add items to an existing set using the add() or addAll() methods:

void main() {

var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};


var elements = <String>{};
[Link]('fluorine');
[Link](halogens);

Maps
In general, a map is an object that associates keys and values. Both keys and values can be any type of
object. Each key occurs only once, but you can use the same value multiple times. Dart support for maps
is provided by map literals and the Map type.

var gifts = {
// Key: Value
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings'
};
var nobleGases = {
2: 'helium',
10: 'neon',
18: 'argon',
};

Add a new key-value pair to an existing map using the subscript assignment operator ([]=):

Dart

var gifts = {'first': 'partridge'};


gifts['fourth'] = 'calling birds'; // Add a key-value pair

Retrieve a value from a map using the subscript operator ([]):

Dart

void main() {

var gifts = {'first': 'partridge'};


assert(gifts['first'] == 'partridge');

To print all values of map

void main() {

var nobleGases = {
2: 'helium',
10: 'neon',
18: 'argon',
};

for(var key in [Link]) {


print(nobleGases[key]);
}

You can create the same objects using a Map constructor:


Dart

void main() {

var gifts = Map<String, String>();


gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';

var nobleGases = Map<int, String>();


nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';
}

Add a new key-value pair to an existing map using the subscript assignment operator ([]=):

Dart

var gifts = {'first': 'partridge'};


gifts['fourth'] = 'calling birds'; // Add a key-value pair

Map with mixed data types

void main() {
Map<dynamic, dynamic> data = {1:10, "position": "first", "marks":50};
}

List of map objects


var list = [
{'name': 'Kamran', 'color' : 'red'},
{'name': 'Shahid', 'color' : 'blue'},
{'name': 'Zahid', 'color' : 'green'},

];

Spread operators
Dart supports the spread operator (...) and the null-aware spread operator (...?) in list, map, and set
literals. Spread operators provide a concise way to insert multiple values into a collection.

For example, you can use the spread operator (...) to insert all the values of a list into another list:
void main() {

var list = [1, 2, 3];


var list2 = [0, ...list];
assert([Link] == 4);
}

If the expression to the right of the spread operator might be null, you can avoid exceptions by using a
null-aware spread operator (...?):

void main() {

var list2 = [0, ...?list];


assert([Link] == 1);

We can modify the map objects using spread operator:

Map<String, String> myObject1 = {


'name': 'Devin',
'hairColor': 'brown',
};

var myObject2 = {...myObject1, 'name':'Kamran'};


print(myObject2);

Control-flow operators
Dart offers collection if and collection for use in list, map, and set literals. You can use these operators
to build collections using conditionals (if) and repetition (for).

Here's an example of using collection if to create a list with three or four items in it:

void main() {

bool promoActive = false;


var nav = ['Home', 'Furniture', 'Plants', if (promoActive) 'Outlet'];
print(nav);
}

void main() {

var login = "Manager";

var nav = ['Home', 'Furniture', 'Plants', if (login case 'Manager')


'Inventory'];

print(nav);
}

Here's an example of using collection for to manipulate the items of a list before adding them to
another list:

void main() {

var listOfInts = [1, 2, 3];


var listOfStrings = ['#0', for (var i in listOfInts) '#$i'];
//assert(listOfStrings[1] == '#1');
print(listOfStrings);
}

Patterns
Destructuring

When an object and pattern match, the pattern can then access the object's data and extract it in parts.
In other words, the pattern destructures the object:
void main() {

var numList = [1, 2, 3];


// List pattern [a, b, c] destructures the three elements from numList...
var [a, b, c] = numList;
// ...and assigns them to new variables.
print(a + b + c);

Destructuring an map object using patterns

void main() {
var item = {"id": 10, "name": "Ali"};

// Destructuring the map using patterns


var {"id": personId, "name": personName} = item;

print(personId);
print(personName);

Variable assignment
A variable assignment pattern falls on the left side of an assignment. First, it destructures the matched
object. Then it assigns the values to existing variables, instead of binding new ones.

Use a variable assignment pattern to swap the values of two variables without declaring a third
temporary one:

void main() {

var (a, b) = ('left', 'right');


(b, a) = (a, b); // Swap.
print('$a $b'); // Prints "right left".
}

Switch statements and expressions


Every case clause contains a pattern. This applies to switch statements and expressions, as well as if-case
statements. You can use any kind of pattern in a case.

Case patterns are refutable. They allow control flow to either:

 Match and destructure the object being switched on.

 Continue execution if the object doesn't match.

The values that a pattern destructures in a case become local variables. Their scope is only within the
body of that case.

void main() {

int obj = 3;
const first = 2;
const last = 4;

switch (obj) {
// Matches if 1 == obj.
case 1:
print('one');

// Matches if the value of obj is between the


// constant values of 'first' and 'last'.
case >= first && <= last:
print('in range');

// Matches if obj is a record with two fields,


// then assigns the fields to 'a' and 'b'.
case (var a, var b):
print('a = $a, b = $b');

default:
}

}
Guard clauses evaluate an arbitrary conditon as part of a case, without exiting the switch if the condition
is false (like using an if statement in the case body would cause).

void main() {
var pair=(20,10);
switch (pair) {
case (int a, int b):
if (a > b) print('First element greater');
// If false, prints nothing and exits the switch.
case (int a, int b) when a > b:
// If false, prints nothing but proceeds to next case.
print('First element greater');
case (int a, int b):
print('First element not greater');
}
}

For and for-in loops


You can use patterns in for and for-in loops to iterate-over and destructure values in a collection.

This example uses object destructuring in a for-in loop to destructure the MapEntry objects that
a <Map>.entries call returns:

void main() {
Map<String, int> hist = {
'a': 23,
'b': 100,
};

for (var MapEntry(key: key, value: count) in [Link]) {


print('$key occurred $count times');
}

Can also be written as:

for (var MapEntry(:key, value: count) in [Link]) {


print('$key occurred $count times');
}

Functions:
bool isNoble(int atomicNumber) {
return true;
}

Although Effective Dart recommends type annotations for public APIs, the function still works if you omit
the types:

isNoble(int atomicNumber) {
return true;
}

For functions that contain just one expression, you can use a shorthand syntax:

bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

Named parameters
Named parameters are optional unless they're explicitly marked as required.

When defining a function, use {param1, param2, …} to specify named parameters. If you don't provide a
default value or mark a named parameter as required, their types must be nullable as their default value
will be null:

void main() {
print("Hello world");

enableFlags(); // will assign default null to bold and hidden


enableFlags(bold:true, hidden: true);
}

void enableFlags({bool? bold, bool? hidden}) {

print("$bold $hidden");

To define a default value for a named parameter besides null, use = to specify a default value. The
specified value must be a compile-time constant. For example:

void main() {
print("Hello world");
enableFlags(hidden: false);
}

void enableFlags({bool bold=true, bool hidden=false}) {

print("$bold $hidden");

Optional positional parameters


Wrapping a set of function parameters in [] marks them as optional positional parameters. If you don't
provide a default value, their types must be nullable as their default value will be null:

void main() {
print("Hello world");

say("Ali", "hello"); // calling with two arguments


say("Ali", "hello", "laptop"); // calling with optional argument included
}

void say(String from, String msg, [String? device]) {


var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
print(result);
}

To define a default value for an optional positional parameter besides null, use = to specify a default
value. The specified value must be a compile-time constant. For example:

void main() {
say("Ali", "hello"); // calling with two arguments
}

void say(String from, String msg, [String device = 'carrier pigeon']) {


print('$from $msg $device');
}
The main() function
Every app must have a top-level main() function, which serves as the
entrypoint to the app. The main() function returns void and has an
optional List<String> parameter for arguments.

Here's a simple main() function:

The file [Link] is in the bin folder. Run the app like this: dart run
[Link] 1 test

// Run the app like this: dart run [Link] 1 test


void main(List<String> arguments) {
print(arguments);

Functions as first-class objects


You can pass a function as a parameter to another function. For example:

void main() {

var list = [1, 2, 3];

// Pass printElement as a parameter.


[Link](printElement);

void printElement(int element) {


print(element);
}

You can also assign a function to a variable, such as:

void main() {
var loudify = (msg) => '!!! ${[Link]()} !!!';

print(loudify("hello"));
}
Anonymous functions
Most functions are named, such as main() or printElement(). You can also create
a nameless function called an anonymous function, or sometimes
a lambda or closure. You might assign an anonymous function to a variable
so that, for example, you can add or remove it from a collection.

The following example defines an anonymous function with an untyped


parameter, item, and passes it to the map function. The function, invoked for each
item in the list, converts each string to uppercase. Then in the anonymous function
passed to forEach, each converted string is printed out alongside its length.

void main() {

const list = ['apples', 'bananas', 'oranges'];

var result = [Link] (

(item)
{
return [Link]();
}
);

print(result);

[Link]((item) {
print('$item: ${[Link]}');
});

/*
// COMBINED FORM

[Link]((item) {
return [Link]();
}).forEach((item) {
print('$item: ${[Link]}');
});
*/

}
Arrow notation:
Example:

void main() {

const list = ['apples', 'bananas', 'oranges'];

print( [Link]((item) => [Link]()) );

Example:

void main() {

const list = ['apples', 'bananas', 'oranges'];

[Link]((item) => [Link]())


.forEach((item) => print('$item: ${[Link]}'));

typdef functions
// 1. Define a typedef for a function that takes two numbers and returns
their sum
typedef SumFunction = int Function(int a, int b);

void main() {

// 2. Implement a function that matches the SumFunction signature


int add(int a, int b) => a + b;

// 3. Use the SumFunction type to declare a variable and assign the add
function
SumFunction sum = add;

// 4. Call the function using the variable with the SumFunction type
int result = sum(5, 3); // result will be 8

print("Sum of 5 and 3 is: $result");


}

Error handling.

void main() {

misbehave();

void misbehave() {
try {
dynamic foo = true;
print(foo++);
} catch (e) {
print(e);
}
}

Classes
Simple class example
class Point {
double? x; // Declare instance variable x, initially null.
double? y; // Declare y, initially null.
}

void main() {
var point = Point();
point.x = 4; // Use the setter method for x.
print(point.x);

}
No argument constructor
class Car {

Car() {
print("no argument constructor called");
}

/*
//OR
Car();
*/

void main() {
Car c = Car();

One argument generative constructor


class Car {
String model;
Car([Link]);
}

void main() {
Car c = Car("Toyota");
print([Link]);
}

Two argument generative constructor


import 'dart:math';

class Point {
final double x;
final double y;

// Sets the x and y instance variables


// before the constructor body runs.
Point(this.x, this.y);
double distanceTo(Point other) {
var dx = x - other.x;
var dy = y - other.y;
return sqrt(dx * dx + dy * dy);
}
}

void main() {
Point p1 = Point(10, 20);
print("x: ${p1.x}, y: ${p1.y}");
Point p2 = Point(5, 10);
print([Link](p1));
}

Another example of two argument constructor


If we want to reprocess the variables before assignment to class variables, we can define the constructor
as follows.

class Car {

String? name;
int? age;

Car(name, age){
[Link] = '$name' ' Ali';
[Link] = age + 30;
}

void main() {
Car c = Car("Shahid", 45);

print('${[Link]} ${[Link]}');

Calling a constructor from another constructor within same class.


class Point {
int? x;
int? y;

Point(int a, int b) {
x = a;
y = b;
}

[Link]() : this(10, 20); // Calls the main constructor


}

void main() {
var point1 = Point(3, 4);
var point2 = [Link]();
print('${point1.x} ${point1.y}');
print('${point2.x} ${point2.y}');
}

Named constructors
class User {

[Link]();
[Link]([Link]);
[Link]([Link]);
[Link]([Link], [Link]);

// we can also place class variables after constructors


int? id;
String? name;

void main() {

var user1 = [Link]();


var user2 = [Link](10);
var user3 = [Link]("Javed");
var user4 = [Link](10, "Javed");

print('${[Link]} ${[Link]}');
print('${[Link]} ${[Link]}');
print('${[Link]} ${[Link]}');
print('${[Link]} ${[Link]}');
}

Named arguments in a constructor.


class Person {
String? name;
int age;

Person({required [Link], String? name}) { // Name is optional now


[Link] = name ?? "Unknown"; // Default value for name if not
provided
}
}

void main(){
Person person1 = Person(age: 25); // Specify age first, name is unknown
print('${[Link]} ${[Link]}');

Person person2 = Person(name: "Bob", age: 42); // Specify both in any


order
print('${[Link]} ${[Link]}');

Immutable objects
By adding const, the created object is immutable, its content can’t be changed.

class Teacher {
const Teacher([Link]);
final String name;
}

void main() {
var teacher = const Teacher("Osman");

print([Link]);
//[Link] = "Zahid"; //error

Optional arguments to a constructor


class Person {
final String name;
final int? age; // Can be null

// Constructor with optional age parameter


const Person([Link], {[Link]});
}

void main() {
// Create a Person with name and age
var person1 = Person('Alice', age: 30);

// Create a Person with only name (age will be null)


var person2 = Person('Bob');

print('${[Link]} ${[Link]}');

Array of objects
class Student {

String name;
int age;

// Constructor
Student([Link], [Link]);

void main() {

// Create an array (List) of MyObject instances


List<Student> myObjects = [];
// Add objects to the array
[Link](Student('Alice', 25));
[Link](Student('Bob', 30));
[Link](Student('Charlie', 22));

// Access and print elements in the array


for (Student obj in myObjects) {
print('Name: ${[Link]}, Age: ${[Link]}');
}
}

Printing elements in an array


class Student {

String name;
int age;

// Constructor
Student([Link], [Link]);

void main(List<String> arguments) {

// Create an array (List) of MyObject instances


List<Student> myObjects = [];

// Add objects to the array


[Link](Student('Alice', 25));
[Link](Student('Bob', 30));
[Link](Student('Charlie', 22));

// Access and print elements in the array


[Link]((student) =>
[Link]('Alice')).forEach((student)=>print('Name: $
{[Link]}, Age: ${[Link]}'));

}
Looping through array of objects:
class Candidate {
final String name;
final int yearsExperience;

Candidate([Link], [Link]);

void interview() {
print("$name is being interviewed...");
// Simulate the interview process
print("$name: Interview went well!");
}
}

void main() {
// List of candidates
List<Candidate> candidates = [
Candidate("Alice", 6),
Candidate("Bob", 3),
Candidate("Charlie", 8),
Candidate("David", 4),
];

// Filter candidates with 5 or more years of experience and conduct


interviews
candidates
.where((c) => [Link] >= 5)
.forEach((c) => [Link]());
}
Use continue to skip to the next loop iteration:

for (int i = 0; i < [Link]; i++) {


var candidate = candidates[i];
if ([Link] < 5) {
continue;
}
[Link]();
}

Inheritance example.
class ParentClass {
void myfunction() {
print("This is parent class function");
}

class ChildClass extends ParentClass {


@override
void myfunction() {
[Link]();
print("This is child class function");
}

}
void main() {

ChildClass c = ChildClass();
[Link]();
}

Using parent class constructor in child class

class Person {

String? name;
int? age;

Person([Link], [Link]);

class Student extends Person { // Explicitly extend the Person class


String? regno;

Student([Link], {String? name = "Ali", int? age = 34}) :


super(name, age);
}

void main() {
Student student = Student("ABC123");
print([Link]); // Output: Ali
print([Link]); // Output: 34
print([Link]); // Output: ABC123
}
Calling named argument constructor from Child class of Parent class
class Person {
String name;
int age;

Person({required [Link], required [Link]}); // Named arguments


constructor
}

class Student extends Person {


String id;

Student({required [Link], required String name, required int age})


: super(name: name, age: age); // Calling parent constructor with
named arguments
}

void main(){
Student s = Student(id: "334", name:"Jamal", age:34);
print('${[Link]} ${[Link]} ${[Link]}');
}

Flutter widgets
In flutter, widgets act like tags in html containing information/data. Each widget is represented
by class. Widgets call other widgets via class composition principles.

What's the point?

 Widgets are classes used to build UIs.


 Widgets are used for both layout and UI elements.
 Compose simple widgets to build complex widgets.

The core of Flutter’s layout mechanism is widgets. In Flutter, almost


everything is a widget—even layout models are widgets. The images, icons,
and text that you see in a Flutter app are all widgets. But things you don’t
see are also widgets, such as the rows, columns, and grids that arrange,
constrain, and align the visible widgets.
runApp() function
The runApp() function takes the given Widget and makes it the root of the widget tree.

Example:
Here the Text wizard is acting as root widget.
NOTE: When MaterialApp widget is used, we don’t need to specify the text direction.
import 'package:flutter/[Link]';

void main() {
runApp(
const Text(
"hello world",
textDirection: [Link],
style: TextStyle(
fontSize: 60,
color: [Link](255, 31, 136, 248),
),
),
);
}

OUTPUT:
Why use const here?
The const keyword used with Text serves two main purposes:
1. Widget Tree Optimization: When you use const with a widget, it tells Flutter that the
widget and its sub-tree (children) are unlikely to change throughout the lifetime of your
app. This allows Flutter to perform some optimizations, such as caching the widget's
configuration and avoiding unnecessary rebuilds. This is particularly beneficial for
widgets that are expensive to create or render.
2. Immutability: Using const with a widget enforces immutability. This means that once
the widget is created, its state cannot be changed. This aligns well with the concept of
widgets in Flutter, which are supposed to represent a declarative UI state.
Key Points:
 The const keyword is most effective for widgets that are stateless and don't require
dynamic updates.
 For widgets that need to update based on user interaction or data changes,
using const might not be suitable. In such cases, you'd likely use
the StatefulWidget approach.
In summary:
Using const with MaterialApp in this example helps Flutter optimize the widget tree and
enforces immutability, which aligns with the principles of building UIs in Flutter.

Example:
To center align the Text, we use Center widget:
In this example, the widget tree consists of two widgets, the Center widget and its child, the
Text widget. The framework forces the root widget to cover the screen, which means the text
“Hello, world” ends up centered on screen.
import 'package:flutter/[Link]';

void main() {
runApp(
const Center(
child: Text(
'Hello, world!',
textDirection: [Link],
),
),
);
}

Output:
MaterialApp Widget:
Many Material Design widgets need to be inside of a MaterialApp to display properly, in order
to inherit theme data. Therefore, run the application with a MaterialApp.
In Flutter, MaterialApp represents a widget that implements the basic material design visual
layout structure. It's typically used as the root widget of a Flutter application. Here's what it
provides:
1. Material Design Components: MaterialApp provides the basic material design
components such as Scaffold, AppBar, Drawer, BottomNavigationBar, SnackBar, and
more. These components follow the Material Design guidelines for visual appearance
and behavior.
2. Text Style Warning: If your app lacks a Material ancestor for text widgets, MaterialApp
automatically applies an ugly red/yellow text style. This serves as a warning to developers that
they haven’t defined a default text style. Typically, the app’s Scaffold defines the text style for
the entire UI.
3. Routing: MaterialApp provides a navigator that manages a stack of Route objects and a
route table for mapping route names to builder functions. This allows you to navigate
between different screens or "routes" in your app using the Navigator widget.
4. Theme: MaterialApp allows you to define a theme for your entire application using the
theme property. This includes defining colors, typography, shapes, and other visual
properties that are consistent throughout your app.
5. Localization: MaterialApp supports internationalization and localization through the
localizationsDelegates and supportedLocales properties. This allows you to provide
translations for your app's text and adapt its behavior based on the user's locale.
6. Accessibility: MaterialApp includes accessibility features such as support for screen
readers and semantic labels, making your app more accessible to users with disabilities.
Overall, MaterialApp serves as the foundation for building Flutter apps with material design
principles, providing a consistent and intuitive user experience across different devices and
platforms.

Example:
In this example, MaterialApp widget is made the root widget, and its ‘home’ property is
invoked, in which Text wizard is passed.
import 'package:flutter/[Link]';

void main() {
runApp(const MaterialApp(
home: Text("Hello world"),

));
}

Output:

You might also like