← Назад к вопросам

Как используются Immutable объекты в Dart?

1.3 Junior🔥 171 комментариев
#Dart

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Immutable объекты в Dart

Immutable (неизменяемые) объекты — это объекты, состояние которых не может быть изменено после создания. В Dart это критически важно для производительности и управления состоянием в Flutter.

Что такое Immutable?

Неизменяемый объект

// Immutable класс
class User {
  final String name;
  final int age;
  final String email;
  
  User({
    required this.name,
    required this.age,
    required this.email,
  });
  
  // Нельзя менять свойства!
  // user.name = "John"; // Ошибка!
}

// Использование
final user = User(name: "Alice", age: 30, email: "alice@example.com");
// user неизменяем

Аннотация @immutable

Указывает, что класс должен быть immutable:

import 'package:flutter/foundation.dart';

@immutable
class Point {
  final double x;
  final double y;
  
  const Point({required this.x, required this.y});
  
  // Если забыть final — ошибка анализатора!
}

Const конструкторы

Для максимальной оптимизации используются const конструкторы:

class Color {
  final int red;
  final int green;
  final int blue;
  
  // Const конструктор — все параметры final
  const Color({
    required this.red,
    required this.green,
    required this.blue,
  });
}

// Const объекты создаются один раз и переиспользуются
const color1 = Color(red: 255, green: 0, blue: 0);
const color2 = Color(red: 255, green: 0, blue: 0);

print(identical(color1, color2)); // true! Один и тот же объект

Immutable модели данных

Простой пример

@immutable
class Product {
  final String id;
  final String name;
  final double price;
  final bool inStock;
  
  const Product({
    required this.id,
    required this.name,
    required this.price,
    required this.inStock,
  });
  
  // Методы для создания модифицированной копии
  Product copyWith({
    String? id,
    String? name,
    double? price,
    bool? inStock,
  }) {
    return Product(
      id: id ?? this.id,
      name: name ?? this.name,
      price: price ?? this.price,
      inStock: inStock ?? this.inStock,
    );
  }
}

copyWith() для изменений

Вместо изменения объекта создаем новый:

final product = Product(
  id: "1",
  name: "Laptop",
  price: 999.99,
  inStock: true,
);

// Не меняем — создаем новый объект!
final updated = product.copyWith(
  price: 899.99,
  inStock: false,
);

print(product.price); // 999.99 (не изменился)
print(updated.price); // 899.99 (новый объект)

Immutable в Flutter State Management

Без Immutable (плохо)

class UserState {
  User? user;
  bool isLoading = false;
  String? error;
  
  // Мутируем состояние — флаттер может не заметить изменений!
  void setLoading(bool loading) {
    isLoading = loading; // Плохо!
  }
}

С Immutable (хорошо)

@immutable
class UserState {
  final User? user;
  final bool isLoading;
  final String? error;
  
  const UserState({
    this.user,
    this.isLoading = false,
    this.error,
  });
  
  // Создаем новое состояние
  UserState copyWith({
    User? user,
    bool? isLoading,
    String? error,
  }) {
    return UserState(
      user: user ?? this.user,
      isLoading: isLoading ?? this.isLoading,
      error: error ?? this.error,
    );
  }
}

// Использование
final state = UserState(isLoading: true);
final newState = state.copyWith(isLoading: false, user: userData);

Immutable в BLoC и Provider

Provider с Immutable моделью

@immutable
class AppState {
  final List<Todo> todos;
  final String? selectedId;
  
  const AppState({
    required this.todos,
    this.selectedId,
  });
}

class AppNotifier extends StateNotifier<AppState> {
  AppNotifier() : super(AppState(todos: []));
  
  void addTodo(Todo todo) {
    // Создаем новое состояние
    state = AppState(
      todos: [...state.todos, todo],
      selectedId: state.selectedId,
    );
  }
  
  void toggleTodo(String id) {
    state = state.copyWith(
      todos: state.todos.map((t) {
        return t.id == id ? t.copyWith(done: !t.done) : t;
      }).toList(),
    );
  }
}

Comparison и Equality

Для immutable объектов важно сравнение:

@immutable
class User {
  final String id;
  final String name;
  
  const User({required this.id, required this.name});
  
  // Переопределяем equals и hashCode
  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is User &&
          runtimeType == other.runtimeType &&
          id == other.id &&
          name == other.name;
  
  @override
  int get hashCode => id.hashCode ^ name.hashCode;
}

// Теперь можно сравнивать
final user1 = User(id: "1", name: "Alice");
final user2 = User(id: "1", name: "Alice");

print(user1 == user2); // true! Даже разные объекты

Freezed пакет для автогенерации

import 'package:freezed_annotation/freezed_annotation.dart';

part 'user.freezed.dart';

@freezed
class User with _$User {
  const factory User({
    required String id,
    required String name,
    required String email,
  }) = _User;
}

// Автоматически генерируются:
// - copyWith()
// - operator ==() и hashCode
// - toString()

final user = User(id: "1", name: "Alice", email: "alice@example.com");
final updated = user.copyWith(name: "Bob"); // Автогенерированный метод

Практический пример в Widget

class TaskWidget extends StatelessWidget {
  final Task task; // Immutable!
  
  const TaskWidget({required this.task});
  
  @override
  Widget build(BuildContext context) {
    // Flutter может оптимизировать, т.к. task не изменяется
    return Card(
      child: ListTile(
        title: Text(task.title),
        subtitle: Text(task.description),
        trailing: Checkbox(
          value: task.isDone,
          onChanged: (value) {
            // Создаем новый task, не мутируем старый
            final updated = task.copyWith(isDone: value);
            context.read<TaskProvider>().updateTask(updated);
          },
        ),
      ),
    );
  }
}

Преимущества Immutable объектов

  • Производительность — Флаттер может кэшировать и оптимизировать
  • Потокобезопасность — нет race conditions
  • Предсказуемость — состояние не меняется неожиданно
  • Отладка — легче отслеживать изменения
  • Тестирование — проще создавать test fixtures

Итого

Immutable объекты в Dart:

  • Используют final для всех полей
  • Используют const конструкторы для оптимизации
  • Имеют методы copyWith() для создания модифицированных копий
  • Критичны для правильного управления состоянием во Flutter
  • Позволяют Флаттеру эффективнее определять изменения и перестраивать UI
Как используются Immutable объекты в Dart? | PrepBro