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

Какие типы Keys существуют во Flutter и когда их использовать?

1.8 Middle🔥 222 комментариев
#Flutter виджеты

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

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

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

Keys в Flutter: типы и применение

Key — это уникальный идентификатор для элемента в дереве виджетов. Это мощный инструмент для сохранения состояния и управления идентичностью элементов при перестроении.

Почему нужны Keys

Проблема без Keys:

class MyList extends StatefulWidget {
  @override
  State<MyList> createState() => _MyListState();
}

class _MyListState extends State<MyList> {
  List<Color> colors = [Colors.red, Colors.green, Colors.blue];
  
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // Виджет 1
        ColorBox(color: colors[0]),
        // Виджет 2
        ColorBox(color: colors[1]),
        // Виджет 3
        ColorBox(color: colors[2]),
        ElevatedButton(
          onPressed: () {
            setState(() {
              colors.removeAt(0); // Удаляем первый элемент
              // БЕЗ Keys: Flutter может спутать элементы!
              // Состояние ColorBox может остаться с неправильным цветом
            });
          },
          child: Text('Remove first'),
        ),
      ],
    );
  }
}

Типы Keys

1. ValueKey — самый распространённый

Использует значение (обычно ID) для идентификации:

class MyList extends StatefulWidget {
  @override
  State<MyList> createState() => _MyListState();
}

class _MyListState extends State<MyList> {
  List<Item> items = [
    Item(id: '1', name: 'Item 1'),
    Item(id: '2', name: 'Item 2'),
    Item(id: '3', name: 'Item 3'),
  ];
  
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ...items.map((item) => ItemWidget(
          key: ValueKey(item.id), // Уникальный ID
          item: item,
        )),
        ElevatedButton(
          onPressed: () {
            setState(() => items.removeAt(0));
            // С ValueKey: Flutter правильно определит, какой элемент удалён
          },
          child: Text('Remove first'),
        ),
      ],
    );
  }
}

2. ObjectKey — ключ на основе объекта

Использует сам объект как ключ:

class _MyListState extends State<MyList> {
  List<Item> items = [];
  
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: items.length,
      itemBuilder: (context, index) {
        return ItemWidget(
          key: ObjectKey(items[index]), // Весь объект как ключ
          item: items[index],
        );
      },
    );
  }
}

3. UniqueKey — уникальный ключ

Создаёт уникальный ключ каждый раз:

class MyWidget extends StatefulWidget {
  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  List<Widget> items = [
    CustomForm(key: UniqueKey()), // Новый ключ при каждом создании
  ];
  
  void addForm() {
    setState(() {
      items.add(CustomForm(key: UniqueKey()));
    });
  }
  
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [...items],
    );
  }
}

4. GlobalKey — глобальный ключ для всего дерева

Позволяет обращаться к State из других мест:

class MyForm extends StatefulWidget {
  const MyForm({Key? key}) : super(key: key);
  
  @override
  State<MyForm> createState() => _MyFormState();
}

class _MyFormState extends State<MyForm> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  
  void submitForm() {
    // Валидируем форму из другого места
    if (_formKey.currentState!.validate()) {
      _formKey.currentState!.save();
    }
  }
  
  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          TextFormField(
            validator: (value) {
              if (value!.isEmpty) return 'Required';
              return null;
            },
          ),
          ElevatedButton(
            onPressed: submitForm,
            child: Text('Submit'),
          ),
        ],
      ),
    );
  }
}

5. PageStorageKey — сохранение состояния при навигации

class MyList extends StatefulWidget {
  @override
  State<MyList> createState() => _MyListState();
}

class _MyListState extends State<MyList> {
  late ScrollController _scrollController;
  
  @override
  void initState() {
    super.initState();
    _scrollController = ScrollController();
  }
  
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      key: PageStorageKey<String>('my-list'), // Сохраняет позицию скролла
      controller: _scrollController,
      itemCount: 100,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text('Item $index'),
        );
      },
    );
  }
}

Сравнение Keys

Key типИспользованиеПроизводительностьПопулярность
ValueKeyID, простые значенияХорошоОчень высокая
ObjectKeyЦелые объектыХорошоСредняя
UniqueKeyКаждый раз новый ключПлохо (много пересчётов)Низкая
GlobalKeyДоступ к State из других местПлохо (дорого)Средняя
PageStorageKeyСохранение состояния при навигацииХорошоСредняя

Практические примеры

Пример 1: Список с удалением

class TodoList extends StatefulWidget {
  @override
  State<TodoList> createState() => _TodoListState();
}

class _TodoListState extends State<TodoList> {
  List<Todo> todos = [
    Todo(id: 1, title: 'Buy milk'),
    Todo(id: 2, title: 'Walk dog'),
    Todo(id: 3, title: 'Read book'),
  ];
  
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: todos.length,
      itemBuilder: (context, index) {
        final todo = todos[index];
        return TodoItem(
          key: ValueKey(todo.id), // Сохраняет состояние элемента
          todo: todo,
          onDelete: () {
            setState(() => todos.removeAt(index));
          },
        );
      },
    );
  }
}

Пример 2: Form с валидацией

class RegistrationForm extends StatefulWidget {
  @override
  State<RegistrationForm> createState() => _RegistrationFormState();
}

class _RegistrationFormState extends State<RegistrationForm> {
  final _formKey = GlobalKey<FormState>();
  String email = '';
  String password = '';
  
  void _submit() {
    if (_formKey.currentState!.validate()) {
      _formKey.currentState!.save();
      // Отправляем данные
      print('Email: $email, Password: $password');
    }
  }
  
  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          TextFormField(
            validator: (value) => value!.isEmpty ? 'Email required' : null,
            onSaved: (value) => email = value!,
          ),
          TextFormField(
            validator: (value) => value!.length < 6 ? 'Min 6 chars' : null,
            onSaved: (value) => password = value!,
          ),
          ElevatedButton(
            onPressed: _submit,
            child: Text('Register'),
          ),
        ],
      ),
    );
  }
}

Правила использования Keys

Используй ValueKey если:

  • Есть уникальный ID для каждого элемента
  • Элементы перестраиваются часто

Используй GlobalKey если:

  • Нужен доступ к State из других мест
  • Работаешь с Form, TextField

Избегай UniqueKey:

  • Может вызвать излишние перестроения
  • Нарушает оптимизацию Flutter

Keys — это критическая часть Flutter для правильного управления состоянием при динамических списках.

Какие типы Keys существуют во Flutter и когда их использовать? | PrepBro