Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
didUpdateWidget: Отслеживание изменений свойств
didUpdateWidget — это важный lifecycle метод в StatefulWidget, который вызывается когда родительский виджет перестраивается и передаёт новые параметры (properties) в StatefulWidget. Это ключевой метод для реагирования на изменения входных данных.
Когда вызывается didUpdateWidget
class MyStatefulWidget extends StatefulWidget {
final String title; // Входное свойство
const MyStatefulWidget({required this.title});
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
@override
void initState() {
super.initState();
print('initState вызван');
// Вызывается один раз при создании State
}
@override
void didUpdateWidget(MyStatefulWidget oldWidget) {
super.didUpdateWidget(oldWidget);
print('didUpdateWidget вызван');
print('Старый title: ${oldWidget.title}');
print('Новый title: ${widget.title}');
// Вызывается когда родитель передал новые параметры
}
@override
void dispose() {
super.dispose();
print('dispose вызван');
}
@override
Widget build(BuildContext context) {
return Text(widget.title);
}
}
// Использование
class ParentWidget extends StatefulWidget {
@override
State<ParentWidget> createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
String _title = 'Initial';
@override
Widget build(BuildContext context) {
return Column(
children: [
MyStatefulWidget(title: _title),
ElevatedButton(
onPressed: () {
setState(() {
_title = 'Updated'; // Изменяем входное свойство
});
},
child: const Text('Update'),
),
],
);
}
}
// Вывод:
// 1. Нажимаем "Update"
// 2. Parent вызывает setState
// 3. Parent перестраивает MyStatefulWidget с новым title
// 4. didUpdateWidget вызывается в MyStatefulWidget
// 5. print выведет:
// "didUpdateWidget вызван"
// "Старый title: Initial"
// "Новый title: Updated"
Пример: Фильтрация списка при изменении фильтра
class FilteredListWidget extends StatefulWidget {
final List<String> items;
final String filter;
const FilteredListWidget({
required this.items,
required this.filter,
});
@override
State<FilteredListWidget> createState() => _FilteredListWidgetState();
}
class _FilteredListWidgetState extends State<FilteredListWidget> {
late List<String> _filteredItems;
@override
void initState() {
super.initState();
_filteredItems = _filterItems();
}
@override
void didUpdateWidget(FilteredListWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// Если изменился filter или items, пересчитываем
if (oldWidget.filter != widget.filter ||
oldWidget.items != widget.items) {
_filteredItems = _filterItems();
}
}
List<String> _filterItems() {
return widget.items
.where((item) => item.contains(widget.filter))
.toList();
}
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: _filteredItems.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_filteredItems[index]),
);
},
);
}
}
// Использование
class SearchScreen extends StatefulWidget {
@override
State<SearchScreen> createState() => _SearchScreenState();
}
class _SearchScreenState extends State<SearchScreen> {
final List<String> _allItems = [
'Apple',
'Banana',
'Cherry',
'Date',
'Elderberry',
];
String _filter = '';
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
onChanged: (value) {
setState(() {
_filter = value; // Обновляем filter
});
},
decoration: const InputDecoration(hintText: 'Search...'),
),
Expanded(
child: FilteredListWidget(
items: _allItems,
filter: _filter, // Передаём новый filter
),
),
],
);
}
}
Когда пользователь вводит текст в TextField:
setStateвызывается в SearchScreen- SearchScreen перестраивается
- FilteredListWidget получает новый
filter - didUpdateWidget вызывается в FilteredListWidgetState
- Список пересчитывается и UI обновляется
Практический пример: Загрузка данных при изменении ID
class UserDetailWidget extends StatefulWidget {
final String userId;
const UserDetailWidget({required this.userId});
@override
State<UserDetailWidget> createState() => _UserDetailWidgetState();
}
class _UserDetailWidgetState extends State<UserDetailWidget> {
late Future<User> _userFuture;
@override
void initState() {
super.initState();
_userFuture = _loadUser();
}
@override
void didUpdateWidget(UserDetailWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// Если userId изменился, загружаем нового пользователя
if (oldWidget.userId != widget.userId) {
_userFuture = _loadUser();
}
}
Future<User> _loadUser() async {
return await api.getUser(widget.userId);
}
@override
Widget build(BuildContext context) {
return FutureBuilder<User>(
future: _userFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
final user = snapshot.data!;
return Column(
children: [
Text('Name: ${user.name}'),
Text('Email: ${user.email}'),
],
);
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return const CircularProgressIndicator();
}
},
);
}
}
// Использование
class UserListScreen extends StatefulWidget {
@override
State<UserListScreen> createState() => _UserListScreenState();
}
class _UserListScreenState extends State<UserListScreen> {
String? _selectedUserId;
@override
Widget build(BuildContext context) {
return Row(
children: [
// Список пользователей
Expanded(
flex: 1,
child: ListView.builder(
itemCount: 10,
itemBuilder: (context, index) {
final userId = 'user_$index';
return ListTile(
title: Text('User $index'),
onTap: () {
setState(() {
_selectedUserId = userId;
});
},
);
},
),
),
// Детали пользователя
Expanded(
flex: 2,
child: _selectedUserId != null
? UserDetailWidget(userId: _selectedUserId!)
: const Center(child: Text('Select a user')),
),
],
);
}
}
// Когда пользователь нажимает на другого пользователя:
// 1. setState обновляет _selectedUserId
// 2. UserDetailWidget перестраивается с новым userId
// 3. didUpdateWidget вызывается и начинает загрузку нового пользователя
Сравнение lifecycle методов
class MyState extends State<MyWidget> {
@override
void initState() {
super.initState();
// ✅ Вызывается ЛИШ ОДН АЗ при создании State
// Используй для:
// - Инициализации переменных
// - Запуска слушателей
// - Загрузки начальных данных
}
@override
void didUpdateWidget(MyWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// ✅ Вызывается КАЖДЫЙ РАЗ когда входные параметры изменяются
// Используй для:
// - Реагирования на изменения в widget.property
// - Пересчета состояния на основе новых параметров
// - Обновления данных при изменении входных значений
}
@override
void deactivate() {
super.deactivate();
// ✅ Вызывается перед remove из дерева
// Используй для:
// - Чистки внешних ресурсов
}
@override
void dispose() {
super.dispose();
// ✅ Вызывается когда State окончательно удаляется
// Используй для:
// - Закрытия контроллеров
// - Отписки от stream'ов
// - Очистки памяти
}
}
Best Practices
✅ ИСПОЛЬЗУЙ didUpdateWidget для:
@override
void didUpdateWidget(MyWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// 1. Проверь что именно изменилось
if (oldWidget.searchQuery != widget.searchQuery) {
_performSearch();
}
// 2. Перестарт animation'ов
if (oldWidget.isActive != widget.isActive && widget.isActive) {
_animationController.forward();
}
// 3. Обновление кэша или state на основе новых параметров
if (oldWidget.userId != widget.userId) {
_loadUserData();
}
}
❌ НЕ ДЕЛАЙ:
// ❌ Не вызывай setState в didUpdateWidget
@override
void didUpdateWidget(MyWidget oldWidget) {
super.didUpdateWidget(oldWidget);
setState(() { // ❌ ОПАСНО!
_myVariable = widget.newValue;
});
}
// ✅ Правильно
@override
void didUpdateWidget(MyWidget oldWidget) {
super.didUpdateWidget(oldWidget);
_myVariable = widget.newValue; // Просто присвой
}
Пример: API с интервальным обновлением
class AutoRefreshWidget extends StatefulWidget {
final String dataId;
final Duration refreshInterval;
const AutoRefreshWidget({
required this.dataId,
this.refreshInterval = const Duration(seconds: 5),
});
@override
State<AutoRefreshWidget> createState() => _AutoRefreshWidgetState();
}
class _AutoRefreshWidgetState extends State<AutoRefreshWidget> {
late Timer _refreshTimer;
late Future<Data> _dataFuture;
@override
void initState() {
super.initState();
_startRefresh();
}
@override
void didUpdateWidget(AutoRefreshWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// Если dataId или интервал изменились, перезагрузи
if (oldWidget.dataId != widget.dataId ||
oldWidget.refreshInterval != widget.refreshInterval) {
_refreshTimer.cancel();
_startRefresh();
}
}
void _startRefresh() {
_dataFuture = _loadData();
_refreshTimer = Timer.periodic(widget.refreshInterval, (_) {
setState(() {
_dataFuture = _loadData();
});
});
}
Future<Data> _loadData() async {
return await api.getData(widget.dataId);
}
@override
void dispose() {
_refreshTimer.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return FutureBuilder<Data>(
future: _dataFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('Data: ${snapshot.data!.value}');
}
return const CircularProgressIndicator();
},
);
}
}
Итог
didUpdateWidget — это:
- Lifecycle метод вызывается когда входные параметры изменяются
- Нужен для реагирования на изменения в родительском виджете
- Essential для виджетов, которые зависят от входных данных
- Должен быть использован вместо setState для обновления state
- Хороший способ реализовать реактивное поведение
Это один из самых важных методов для создания корректно работающих StatefulWidget'ов в Flutter.