In general, about the Yap Dart

In general, about the Yap Dart

Hello, Habre!

History Darth Vader started in 2011. The purpose of creating Dart was to offer an alternative to JavaScriptwhich would allow the creation of more complex, high-performance web applications, while simplifying the development process.

Over time, the Dart has undergone many updates, but its main thrust came with the launch Flutter — a framework for creating native interfaces for mobile, web and desktop applications from a single codebase.

Language supports OOP with classes and multiple inheritance, as well as functionality such as higher order functions, closures and async, etc.

Dart’s type system supports both static typing, so and so typing at runtime.

Basic capabilities

Variables and data types

var a variable declaration is used without an explicit indication of its type. Dart automatically determines the type of a variable based on the value assigned to it:

var name="Dart"; // dart понимает, что переменная name имеет тип string
var version = 2.12; // dart определяет, что переменная version` имеет тип double

What about unchanged dataDart offers two options: final and const.

  • final used for variables, values which are known only during program execution and may be changed after initial assignment.

  • const applies to variables whose values is known at compile time and remains constant throughout the execution time.

final userName="otus";
const double PI = 3.14;

Dart supports static typing:

String language="Dart";
int year = 2023;
double version = 2.10;
List<String> developers = ['artem', 'ivan', 'igor'];
Map<String, dynamic> info = {
  'name': 'Dart',
  'created': 2008,
};

Dart also offersList, Setand Map:

  • Letter: an ordered collection of objects of the same type

  • Set: an unordered collection of unique objects

  • Map: a collection of key-value pairs where each key occurs at most once

List<int> primeNumbers = [2, 3, 5, 7, 11];
Set<String> languages = {'Dart', 'Flutter', 'Java'};
Map<String, String> headers = {
  'Content-Type': 'application/json',
  'Authorization': 'Bearer your_token',
};

Functions

Named parameters are declared in curly braces {}:

void printUserInfo({String name, int age}) {
  print('Name: $name, Age: $age');
}

void main() {
  // вызов
  printUserInfo(name: 'ivan', age: 30);
}

Dart supports optional positional parameters by using []:

void printMessage(String message, [String author="Anonymous"]) {
  print('$message — $author');
}

void printUserInfoOptional({String name="Unknown", int age}) {
  print('Name: $name, Age: $age');
}

void main() {
  // функция с необязательным позиционным параметром
  printMessage('Hello, Dart!');
  // функция с необязательным именованным параметром
  printUserInfoOptional(age: 25);
}

Higher order functions accept other functions as arguments or return them:

void operateOnNumbers(int a, int b, Function(int, int) operation) {
  var result = operation(a, b);
  print('Result: $result');
}

int sum(int x, int y) => x + y;
int product(int x, int y) => x * y;

void main() {
  // функции высшего порядка
  operateOnNumbers(3, 4, sum); // Result: 7
  operateOnNumbers(3, 4, product); // Result: 12

  // use анонимной функции
  operateOnNumbers(3, 4, (x, y) => x - y); // Result: -1
}

Classes and OVP

In Dart, classes are a means of encapsulating and combining data and functionality:

class Car {
  String make;
  String model;
  int year;

  // конструктор
  Car(this.make, this.model, this.year);

  // именованный конструктор
  Car.named({required this.make, required this.model, required this.year});

  // метод для вывода данных автомобиля
  void displayInfo() {
    print('Make: $make, Model: $model, Year: $year');
  }
}

void main() {
  var myCar = Car('Toyota', 'Corolla', 2020);
  myCar.displayInfo();

  var mySecondCar = Car.named(make: 'Ford', model: 'Mustang', year: 1969);
  mySecondCar.displayInfo();
}

inheritance allows a class to inherit the properties and methods of another class, the word uses extends to follow:

class ElectricCar extends Car {
  double batteryCapacity;

  ElectricCar(String make, String model, int year, this.batteryCapacity)
      : super(make, model, year);

  @override
  void displayInfo() {
    super.displayInfo();
    print('Battery Capacity: $batteryCapacity kWh');
  }
}

void main() {
  var myElectricCar = ElectricCar('Tesla', 'Model Y', 2023, 85.0);
  myElectricCar.displayInfo();
}

Abstract classes in Dart are used as base classes that are not supposed to be instantiated directly:

abstract class Shape {
  void draw(); // абстрактный метод
}

class Circle extends Shape {
  @override
  void draw() {
    print('Drawing circle...');
  }
}

class Square extends Shape {
  @override
  void draw() {
    print('Drawing square...');
  }
}

void main() {
  var circle = Circle();
  circle.draw();

  var square = Square();
  square.draw();
}

In Dart there is no special syntax for announcement interfaces. Any class can act as an interface, and another class can implement it with implements:

class Vehicle {
  void start() => print("Vehicle started");
  void stop() => print("Vehicle stopped");
}

class Bike implements Vehicle {
  @override
  void start() => print("Bike started");

  @override
  void stop() => print("Bike stopped");
}

void main() {
  var bike = Bike();
  bike.start();
  bike.stop();
}

Control structures

Everything here is intuitive.

Conditional operators if-else allow you to execute a specific block of code depending on whether a condition is true:

int number = 10;
if (number % 2 == 0) {
  print('$number is even');
} else {
  print('$number is odd');
}

Cycles allow you to repeat the execution of a block of code until a given condition is met:

for (int i = 0; i < 5; i++) {
  print('i = $i');
}

while executes a block of code while the condition is true:

int num = 5;
while (num > 0) {
  print('num = $num');
  num--;
}

do-while: similar to whilebut ensures that the loop body is executed at least once:

int count = 0;
do {
  print('count = $count');
  count++;
} while (count < 5);

switch-case allows you to execute different blocks of code depending on the value of the variable:

String grade="B";
switch (grade) {
  case 'A':
    print('Excellent');
    break;
  case 'B':
    print('Good');
    break;
  case 'C':
    print('Fair');
    break;
  case 'D':
  case 'F':
    print('Poor');
    break;
  default:
    print('Invalid grade');
}

Exceptions can be generated using throw and intercept them with the help of try-catch:

void checkAge(int age) {
  if (age < 18) {
    throw Exception('Not eligible for voting');
  } else {
    print('Eligible for voting');
  }
}

try {
  checkAge(16);
} catch (e) {
  print('Caught an exception: $e');
} finally {
  print('This is always executed');
}

null safety

In Dart with null safety enabled, variables cannot be null by default:

int a = null; // err

To declare a variable that can be null, you need to use a question mark after the variable type:

int? a = null; // ОК

When a variable can be null, Dart requires that this possibility be considered before using its value:

int? a;

if (a != null) {
  print(a + 2); // ОК, потому что мы проверили, что a не null
}

Operator ! tells the compiler, “I’m sure this variable is not null.” It should be used with great care:

int? a = null;
print(a! + 2); // исключение во время выполнения

Operators ??= and ?? allow you to work with variables that can be null by assigning them a default value.

  • ??= returns the value of the variable if it is null.

  • ?? returns the value to its right if the value to its left is null.

int? a;
a ??= 10; // a будет равно 10, потому что до присваивания a было null
print(a); //  10

int? b = null;
print(b ?? 20); // 20, потому что b равно null

Dart with Flutter

There are two main types of widgets in Flutter: StatefulWidget and StatelessWidget. StatelessWidget is used for widgets that do not change their state at runtime, a StatefulWidget used for widgets that can change their state in response to user interaction or other factors

StatelessWidget:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Пример StatelessWidget'),
        ),
        body: Center(
          child: Text('Привет, Хабр!'),
        ),
      ),
    );
  }
}

StatefulWidget:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Пример StatefulWidget'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text('Вы нажали на кнопку столько раз:'),
              Text('$_counter', style: Theme.of(context).textTheme.headline4),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: _incrementCounter,
          tooltip: 'Increment',
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

Flutter makes it easy to work with asynchronous operations by using FutureBuilder:

import 'package:flutter/material.dart';
import 'dart:async';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  final Future<String> _calculation = Future<String>.delayed(
    Duration(seconds: 2),
    () => 'Данные загружены',
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Пример FutureBuilder'),
        ),
        body: Center(
          child: FutureBuilder<String>(
            future: _calculation,
            builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
              List<Widget> children;

              if (snapshot.connectionState == ConnectionState.done) {
                if (snapshot.hasError) {
                  children = <Widget>[
                    Icon(
                      Icons.error_outline,
                      color: Colors.red,
                      size: 60,
                    ),
                    Padding(
                      padding: const EdgeInsets.only(top: 16),
                      child: Text('Error: ${snapshot.error}'),
                    ),
                  ];
                } else {
                  children = <Widget>[
                    Icon(
                      Icons.check_circle_outline,
                      color: Colors.green,
                      size: 60,
                    ),
                    Padding(
                      padding: const EdgeInsets.only(top: 16),
                      child: Text('Result: ${snapshot.data}'),
                    ),
                  ];
                }
              } else {
                children = <Widget>[
                  SizedBox(
                    child: CircularProgressIndicator(),
                    width: 60,
                    height: 60,
                  ),
                  const Padding(
                    padding: EdgeInsets.only(top: 16),
                    child: Text('Awaiting result...'),
                  )
                ];
              }

              return Column(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: children,
              );
            },
          ),
        ),
      ),
    );
  }
}

Transferring data between screens is usually done through constructors and the Navigator API:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Передача данных между экранами',
      home: FirstScreen(),
    );
  }
}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Первый экран'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Перейти ко второму экрану'),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => SecondScreen(data: 'Привет от первого экрана'),
              ),
            );
          },
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  final String data;

  SecondScreen({Key key, @required this.data}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Второй экран'),
      ),
      body: Center(
        child: Text(data),
      ),
    );
  }
}

Let me remind you that in In OTUS online courses, you can study the most popular UTsand also sign up for a number of free events.

Related posts