В общих словах про ЯП Dart

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Привет, Хабр!

История Darth Vader началась в 2011 году. Целью создания Dart было предложить альтернативу JavaScript, которая позволила бы создавать более сложные, высокопроизводительные веб-приложения, облегчая при этом процесс разработки.

Со временем Dart претерпел множество апдейтов, но его основной рывок произошел с запуском Flutter — фреймворка для создания нативных интерфейсов для мобильных, веб- и настольных приложений из единой кодовой базы.

Язык поддерживает ООП с классами и множественным наследованием, а также функциональные возможности, такие как высшие порядки функций, замыкания и асинхронность и т.п.

Система типов в Dart поддерживает как статическую типизацию, так и типизацию во время выполнения.

Основные возможности

Переменные и типы данных

var используется для объявления переменной без явного указания её типа. Dart автоматически определяет тип переменной на основе присвоенного ей значения:

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

Что касательно неизменяемых данных, Dart предлагает имеет две возможности: final и const.

  • final используется для переменных, значение которых известно только во время выполнения программы и не может быть изменено после первого присвоения.

  • const применяется к переменным, значение которых известно во время компиляции и остаётся константным на протяжении всего времени выполнения.

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

Dart поддерживает статическую типизацию:

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 также предлагаетList, Set, и Map:

  • List: упорядоченная коллекция объектов одного типа.

  • Set: неупорядоченная коллекция уникальных объектов.

  • Map: коллекция пар ключ-значение, где каждый ключ встречается не более одного раза.

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',
};

Функции

Именованные параметры объявляются в фигурных скобках {}:

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

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

Dart поддерживает необязательные позиционные параметры с помощью []:

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);
}

Функции высшего порядка принимают другие функции в качестве аргументов или возвращают их:

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
}

Классы и ООП

В Dart классы представляют собой средство для инкапсуляции и объединения данных и функционала:

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();
}

Наследование позволяет классу наследовать свойства и методы другого класса, юзается слово extends для наследования:

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();
}

Абстрактные классы в Dart используются как базовые классы, которые не предполагается инстанцировать напрямую:

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();
}

В Dart нет специального синтаксиса для объявления интерфейсов. Любой класс может действовать как интерфейс, и другой класс может его реализовать с 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();
}

Управляющие конструкции

Здесь все интуитивно.

Условные операторы if-else позволяют выполнить определенный блок кода в зависимости от того, истинно ли условие:

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

Циклы позволяют повторять выполнение блока кода, пока выполняется заданное условие:

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

while выполняет блок кода, пока условие истинно:

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

do-while: похож на while, но гарантирует, что тело цикла выполнится хотя бы один раз:

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

switch-case позволяет выполнить различные блоки кода в зависимости от значения переменной:

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');
}

Можно генерировать исключения с помощью throw и перехватывать их с помощью 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

В Dart с включённым null safety переменные по умолчанию не могут быть null:

int a = null; // err

Чтобы объявить переменную, которая может быть null, нужно использовать знак вопроса после типа переменной:

int? a = null; // ОК

Когда переменная может быть null, Dart требует от учитывать эту возможность перед тем, как использовать её значение:

int? a;

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

Оператор ! говорит компилятору: "Я уверен, что эта переменная не null". Его нужно юзать с большой осторожностью:

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

Операторы ??= и ?? позволяют работать с переменными, которые могут быть null, присваивая им значения по умолчанию.

  • ??= присваивает значение переменной, если она null.

  • ?? возвращает значение справа от себя, если значение слева от него — null.

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

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

Dart с Flutter

В Flutter есть два основных типа виджетов: StatefulWidget и StatelessWidget. StatelessWidget используется для виджетов, которые не изменяют свое состояние во время выполнения, а StatefulWidget используется для виджетов, которые могут изменять свое состояние в ответ на взаимодействия пользователя или другие факторы

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 облегчает работу с асинхронными операциями через использование 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,
              );
            },
          ),
        ),
      ),
    );
  }
}

Передача данных между экранами обычно осуществляется через конструкторы и 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),
      ),
    );
  }
}

Напомню, что в рамках онлайн-курсов OTUS вы можете изучить самые популярные ЯП, а также зарегистрироваться на ряд бесплатных мероприятий.

Источник: https://habr.com/ru/companies/otus/articles/796585/


Интересные статьи

Интересные статьи

16 апреля зарелизился ClojureDart, а это значит, что для любителей Clojure открылась возможность писать мобильные, веб- и десктоп-приложения на Flutter. Зачем использовать для этого Clojure, как бы оч...
Почему Flutter использует язык Dart? Основные преимущества языка Dart.Сложность: Новичок. Многие лингвисты считают, что естественный язык, на котором говорит человек, влияет на то, как он думает...
gRPC + Dart, Сервис + Клиент, напишем? Часть 2.Первая часть находится здесьПродолжим развивать сервис на основе технологии gRPC, основы которого мы заложили в первой част...
Недавно столкнулся с необходимостью написать REST API сервер на Dart. Оставим за рамками этой статьи почему и зачем это было надо, но первое с чем я столкнулся - выбор би...
При переходе на новую технологию, мы лишаемся привычных инструментов для разработки. В каких-то случая мы вынуждены смириться с их отсутствием из-за каких-то технических ограничений, но при возмо...