
Привет, Хабр!
История 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 вы можете изучить самые популярные ЯП, а также зарегистрироваться на ряд бесплатных мероприятий.

