Dart
Экспресс-тур по Dart: переменные, null-безопасность, коллекции, функции, классы, миксины, async/await и дженерики — весь язык на одной странице.
Dart — язык от Google, на котором пишут приложения на Flutter. Строго типизированный, с надёжной null-безопасностью и компиляцией в нативный код и JavaScript. Ниже — весь язык на одной странице: почти вся теория спрятана в комментариях рабочего кода.
1. Структура программы
Точка входа — функция main. Вывод в консоль — print.
// Однострочный комментарий
/* Многострочный
комментарий */
/// Doc-комментарий (документация)
// main — точка входа в программу
void main() {
print('Привет, Dart!'); // вывод: Привет, Dart!
// Точка с запятой обязательна в конце инструкции
print(2 + 2); // 4
}
2. Переменные и типы
var — вывод типа, final — присвоение один раз, const — константа на этапе компиляции.
void main() {
// var — тип выводится автоматически
var name = 'Аня'; // String
var age = 25; // int
// Явные типы
int year = 2026; // целое
double pi = 3.14; // число с плавающей точкой
bool isOk = true; // логический тип
String city = 'Москва'; // строка
// num — общий тип для int и double
num x = 5; // ок
x = 5.5; // тоже ок
// final — присваивается один раз (значение в рантайме)
final now = DateTime.now();
// const — константа времени компиляции (значение известно заранее)
const maxUsers = 100;
// dynamic — тип может меняться (отключает проверки типов)
dynamic anything = 'строка';
anything = 42; // допустимо
print('$name, $age, $year, $pi, $isOk, $city, $x, $maxUsers, $anything');
print(now);
}
3. Строки
Интерполяция через $переменная и ${выражение}.
void main() {
var name = 'Дима';
var age = 30;
// Интерполяция переменной
print('Имя: $name'); // Имя: Дима
// Интерполяция выражения в ${ }
print('Через год: ${age + 1}'); // Через год: 31
// Одинарные и двойные кавычки равнозначны
var s1 = 'одинарные';
var s2 = "двойные";
// Многострочная строка
var multi = '''
строка 1
строка 2''';
// Конкатенация
var full = 'Привет, ' + name; // Привет, Дима
// Соседние литералы склеиваются
var joined = 'abc' 'def'; // abcdef
// Методы строк
print('dart'.toUpperCase()); // DART
print(' обрезка '.trim()); // 'обрезка'
print('a,b,c'.split(',')); // [a, b, c]
print('hello'.length); // 5
print('hello'.contains('ell')); // true
print('hello'.replaceAll('l', 'L')); // heLLo
print('hello'[0]); // h
print('$s1 $s2 $multi $full $joined');
}
3.1. Числа: парсинг и форматирование
void main() {
// Преобразование строки в число
int n = int.parse('42'); // 42
double d = double.parse('3.14'); // 3.14
// Число в строку
String s = 42.toString(); // '42'
print(3.14159.toStringAsFixed(2)); // 3.14 (округление до 2 знаков)
// Деление
print(7 / 2); // 3.5 (всегда double)
print(7 ~/ 2); // 3 (целочисленное деление)
print(7 % 2); // 1 (остаток)
print('$n $d $s');
}
4. Null-безопасность
Главная фишка Dart: по умолчанию переменная не может быть null. Чтобы разрешить null — добавь ? к типу.
void main() {
// int x = null; // ОШИБКА компиляции: int не может быть null
// int? — допускает null
int? maybeAge = null;
maybeAge = 25; // тоже можно
String? name; // по умолчанию null
// ?. — безопасный доступ: если null, вернёт null, без падения
print(name?.length); // null (а не ошибка)
// ?? — значение по умолчанию, если слева null
int len = name?.length ?? 0;
print(len); // 0
// ??= — присвоить, только если переменная сейчас null
name ??= 'Гость';
print(name); // Гость
// ! — оператор «я уверен, что не null» (бросит ошибку, если null)
int? a = 5;
int b = a!; // снимаем nullable, b = 5
print(b);
// late — переменная инициализируется позже, но до первого чтения
late String greeting;
greeting = 'Привет'; // присвоили перед использованием
print(greeting); // Привет
}
5. Операторы и условия
void main() {
var age = 18;
// if / else if / else
if (age < 18) {
print('несовершеннолетний');
} else if (age == 18) {
print('ровно 18'); // сработает это
} else {
print('взрослый');
}
// Тернарный оператор
var status = age >= 18 ? 'можно' : 'нельзя';
print(status); // можно
// Логические операторы: && (и), || (или), ! (не)
print(age > 0 && age < 100); // true
// switch — современный синтаксис (Dart 3)
var day = 3;
switch (day) {
case 1:
print('понедельник');
case 6 || 7: // несколько значений через ||
print('выходной');
default:
print('будний день'); // сработает это
}
// switch-выражение (возвращает значение)
var name = switch (day) {
1 => 'пн',
3 => 'ср',
_ => 'другой', // _ — любой остальной случай
};
print(name); // ср
}
6. Циклы
void main() {
// Классический for
for (int i = 0; i < 3; i++) {
print(i); // 0, 1, 2
}
// for-in — перебор коллекции
var fruits = ['яблоко', 'банан', 'груша'];
for (var fruit in fruits) {
print(fruit); // яблоко, банан, груша
}
// forEach
fruits.forEach((f) => print(f));
// while
var n = 0;
while (n < 3) {
print('while $n');
n++;
}
// do-while — тело выполнится хотя бы раз
var m = 0;
do {
print('do $m');
m++;
} while (m < 2);
// break и continue
for (var i = 0; i < 5; i++) {
if (i == 1) continue; // пропустить итерацию
if (i == 3) break; // выйти из цикла
print('loop $i'); // loop 0, loop 2
}
}
7. Коллекции
List (список), Map (словарь), Set (множество).
void main() {
// List — упорядоченный список
List<int> nums = [1, 2, 3];
nums.add(4); // [1, 2, 3, 4]
print(nums[0]); // 1
print(nums.length); // 4
print(nums.first); // 1
print(nums.last); // 4
print(nums.contains(2)); // true
// Map — пары ключ-значение
Map<String, int> ages = {'Аня': 25, 'Дима': 30};
print(ages['Аня']); // 25
ages['Маша'] = 22; // добавление
print(ages.keys); // (Аня, Дима, Маша)
print(ages.values); // (25, 30, 22)
// Set — уникальные значения
Set<int> unique = {1, 2, 2, 3};
print(unique); // {1, 2, 3} (дубликаты убраны)
// Spread-оператор ... — раскрыть коллекцию
var more = [0, ...nums]; // [0, 1, 2, 3, 4]
print(more);
// ...? — spread с защитой от null
List<int>? maybe;
var safe = [1, ...?maybe]; // [1]
print(safe);
// collection if — условное добавление элемента
bool admin = true;
var menu = ['Главная', if (admin) 'Админка'];
print(menu); // [Главная, Админка]
// collection for — генерация элементов в литерале
var squares = [for (var i = 1; i <= 3; i++) i * i];
print(squares); // [1, 4, 9]
// map / where — преобразование и фильтрация
print(nums.map((n) => n * 2).toList()); // [2, 4, 6, 8]
print(nums.where((n) => n.isEven).toList()); // [2, 4]
}
8. Функции
// Обычная функция с типами
int add(int a, int b) {
return a + b;
}
// Стрелочная функция (=> для одного выражения)
int square(int x) => x * x;
// Именованные параметры в { } — вызываются по имени
// required делает параметр обязательным
String greet({required String name, String greeting = 'Привет'}) {
return '$greeting, $name!';
}
// Позиционные необязательные параметры в [ ] со значением по умолчанию
String tag(String text, [String wrap = '*']) {
return '$wrap$text$wrap';
}
void main() {
print(add(2, 3)); // 5
print(square(4)); // 16
// Именованные аргументы — порядок не важен
print(greet(name: 'Аня')); // Привет, Аня!
print(greet(name: 'Дима', greeting: 'Хай')); // Хай, Дима!
print(tag('текст')); // *текст*
print(tag('текст', '_')); // _текст_
// Функция как значение (первоклассные функции)
Function op = add;
print(op(10, 20)); // 30
// Анонимная функция
var triple = (int n) => n * 3;
print(triple(5)); // 15
// Замыкание — функция помнит окружение
Function counter() {
var count = 0;
return () => ++count; // захватывает count
}
var next = counter();
print(next()); // 1
print(next()); // 2 (состояние сохранилось)
}
9. Классы и ООП
class Person {
// Поля
String name;
int age;
// Конструктор (краткая форма: this.x присваивает поля)
Person(this.name, this.age);
// Именованный конструктор
Person.guest()
: name = 'Гость',
age = 0;
// Метод
void greet() {
print('Привет, я $name, мне $age');
}
// Геттер — вычисляемое свойство (без скобок при вызове)
bool get isAdult => age >= 18;
// Сеттер — присваивание через =
set rename(String newName) => name = newName;
}
void main() {
// new не обязателен
var p = Person('Аня', 25);
p.greet(); // Привет, я Аня, мне 25
print(p.isAdult); // true (геттер без скобок)
p.rename = 'Анна'; // вызов сеттера
print(p.name); // Анна
var g = Person.guest(); // именованный конструктор
print(g.name); // Гость
}
10. Наследование и миксины
extends — наследование, abstract — абстрактный класс, implements — интерфейс, mixin — переиспользуемое поведение.
// Абстрактный класс — нельзя создать напрямую
abstract class Animal {
String get name;
void sound(); // абстрактный метод (без тела)
}
// extends — наследование, @override — переопределение
class Dog extends Animal {
@override
String get name => 'Пёс';
@override
void sound() => print('Гав');
}
// mixin — набор методов для подмешивания
mixin Swimmer {
void swim() => print('Плыву');
}
// with — подмешать миксин
class Duck extends Animal with Swimmer {
@override
String get name => 'Утка';
@override
void sound() => print('Кря');
}
void main() {
var d = Dog();
d.sound(); // Гав
print(d.name); // Пёс
var duck = Duck();
duck.sound(); // Кря
duck.swim(); // Плыву (из миксина)
// super — обращение к родителю
// (в Dog можно вызвать super.method() из переопределённого метода)
}
11. Асинхронность
Future — значение, которое появится позже. async/await — удобная работа с ним. Stream — поток значений во времени.
// Функция возвращает Future<String> — результат «в будущем»
Future<String> fetchUser() async {
// await приостанавливает функцию до готовности Future
await Future.delayed(Duration(seconds: 1)); // имитация задержки
return 'Аня';
}
// Stream — последовательность значений (async*, yield)
Stream<int> countTo(int n) async* {
for (var i = 1; i <= n; i++) {
await Future.delayed(Duration(milliseconds: 100));
yield i; // отдаём значение в поток
}
}
// main тоже может быть async
Future<void> main() async {
print('Загрузка...');
var user = await fetchUser(); // ждём результат
print('Привет, $user!'); // Привет, Аня!
// await for — перебор значений Stream по мере поступления
await for (var i in countTo(3)) {
print('тик $i'); // тик 1, тик 2, тик 3
}
// Альтернатива: .then() вместо await
fetchUser().then((u) => print('then: $u'));
// Обработка ошибок
try {
await Future.error('сбой');
} catch (e) {
print('Поймал: $e'); // Поймал: сбой
}
}
12. Дженерики
Параметризация типом <T> — один код для разных типов с сохранением типобезопасности.
// Обобщённая функция: T — параметр-тип
T firstOf<T>(List<T> items) => items.first;
// Обобщённый класс — простой стек
class Stack<T> {
final List<T> _items = [];
void push(T item) => _items.add(item);
T pop() => _items.removeLast();
bool get isEmpty => _items.isEmpty;
}
// Ограничение типа: T должен быть num (или его наследником)
T maxOf<T extends num>(T a, T b) => a > b ? a : b;
void main() {
print(firstOf<int>([10, 20, 30])); // 10
print(firstOf(['a', 'b'])); // a (тип выведен)
var s = Stack<String>();
s.push('a');
s.push('b');
print(s.pop()); // b
print(s.isEmpty); // false
print(maxOf(3, 7)); // 7
print(maxOf(1.5, 0.5)); // 1.5
}
Готово! Это ядро Dart. Дальше — records и pattern matching (Dart 3), пакеты с pub.dev и, конечно, Flutter для UI.