В общих словах про ЯП 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/articles/796585/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *