← Назад к вопросам
Чем отличается Stateful widget от Stateless widget?
1.2 Junior🔥 282 комментариев
#Flutter виджеты#State Management
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Stateless Widget vs Stateful Widget: Когда что использовать
Это один из самых фундаментальных вопросов во Flutter. Понимание разницы между stateless и stateful виджетами критично для написания правильного и эффективного кода.
Основная разница
Stateless Widget:
- Не имеет внутреннего состояния (state)
- Неизменяемый (immutable)
- Строит UI один раз и никогда не меняется
- Быстрый и лёгкий
Stateful Widget:
- Имеет изменяемое состояние (State)
- Может перестраиваться (rebuild) при изменении состояния
- Может иметь lifecycle (initState, dispose)
- Более сложный
Stateless Widget: Простой и чистый
Пример:
class WelcomeScreen extends StatelessWidget {
final String userName;
const WelcomeScreen({required this.userName});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Welcome')),
body: Center(
child: Text('Hello, $userName!'),
),
);
}
}
// Использование
WelcomeScreen(userName: 'Alice')
Когда использовать Stateless:
- Данные приходят через конструктор (props)
- UI не меняется внутри компонента
- Презентационные компоненты (кнопки, карточки, иконки)
- Компоненты, зависящие от parent
Stateful Widget: Управление состоянием
Структура:
class CounterApp extends StatefulWidget {
@override
State<CounterApp> createState() => _CounterAppState();
}
class _CounterAppState extends State<CounterApp> {
int counter = 0;
@override
void initState() {
super.initState();
// Инициализация
print('Виджет создан');
}
@override
void dispose() {
super.dispose();
// Очистка ресурсов
print('Виджет удалён');
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('Counter: $counter'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
counter++; // Перестройка UI
});
},
child: Icon(Icons.add),
),
);
}
}
Lifecycle методы:
class _MyStateState extends State<MyState> {
@override
void initState() {
super.initState();
// Вызывается один раз при создании
// Инициализация переменных, подписка на потоки
}
@override
void didUpdateWidget(MyState oldWidget) {
super.didUpdateWidget(oldWidget);
// Вызывается когда parent перестроился
// Обновить state если нужно
}
@override
void deactivate() {
super.deactivate();
// Вызывается перед dispose
}
@override
void dispose() {
super.dispose();
// Вызывается один раз перед удалением
// ОБЯЗАТЕЛЬНО закрыть потоки, отписаться
}
@override
Widget build(BuildContext context) {
// Вызывается при каждом setState()
return Container();
}
}
Когда использовать Stateful
Типичные сценарии:
// 1. Управление пользовательским вводом
class LoginForm extends StatefulWidget {
@override
State<LoginForm> createState() => _LoginFormState();
}
class _LoginFormState extends State<LoginForm> {
late TextEditingController emailController;
late TextEditingController passwordController;
@override
void initState() {
super.initState();
emailController = TextEditingController();
passwordController = TextEditingController();
}
@override
void dispose() {
emailController.dispose();
passwordController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(controller: emailController),
TextField(controller: passwordController),
ElevatedButton(
onPressed: login,
child: Text('Login'),
),
],
);
}
void login() {
print('Email: ${emailController.text}');
}
}
// 2. Взаимодействие с внешними ресурсами
class UserProfile extends StatefulWidget {
final int userId;
const UserProfile({required this.userId});
@override
State<UserProfile> createState() => _UserProfileState();
}
class _UserProfileState extends State<UserProfile> {
late StreamSubscription userSubscription;
User? user;
@override
void initState() {
super.initState();
// Подписаться на обновления
userSubscription = userService.getUser(widget.userId).listen((newUser) {
setState(() => user = newUser);
});
}
@override
void dispose() {
userSubscription.cancel(); // ВАЖНО!
super.dispose();
}
@override
Widget build(BuildContext context) {
return user == null
? LoadingWidget()
: UserCard(user: user!);
}
}
// 3. Анимации
class FadingWidget extends StatefulWidget {
@override
State<FadingWidget> createState() => _FadingWidgetState();
}
class _FadingWidgetState extends State<FadingWidget>
with SingleTickerProviderStateMixin {
late AnimationController controller;
late Animation<double> opacity;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
);
opacity = Tween(begin: 0.0, end: 1.0).animate(controller);
controller.forward();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return FadeTransition(
opacity: opacity,
child: Text('Fading text'),
);
}
}
Сравнительная таблица
Аспект Stateless Stateful
────────────────────────────────────────────────────
Сложность Простой Сложнее
Производ. Быстрее Медленнее
Изменяемость Immutable Mutable
Пересройка Нет (данные из props) Да (setState)
Лифцикл Нет Да (init/dispose)
Используемость ~70% виджетов ~30% виджетов
Ошибки Меньше Больше
────────────────────────────────────────────────────
Современный подход: Избегайте Stateful
Современный Flutter рекомендует:
- Использовать StateManagement (BLoC, Provider, Riverpod)
- Минимизировать local state в Stateful
- Предпочитать Stateless + Provider/BLoC
Пример с Provider (лучше):
// Stateless + управление через Provider
class CounterScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Consumer<CounterProvider>(
builder: (context, counter, _) {
return Column(
children: [
Text('Count: ${counter.value}'),
ElevatedButton(
onPressed: counter.increment,
child: Text('Increment'),
),
],
);
},
),
),
);
}
}
class CounterProvider extends ChangeNotifier {
int _value = 0;
int get value => _value;
void increment() {
_value++;
notifyListeners();
}
}
Типичные ошибки
Ошибка 1: Использование Stateful для простого UI
// Плохо
class SimpleButton extends StatefulWidget {
final String label;
final VoidCallback onPressed;
@override
State<SimpleButton> createState() => _SimpleButtonState();
}
// Хорошо
class SimpleButton extends StatelessWidget {
final String label;
final VoidCallback onPressed;
const SimpleButton({required this.label, required this.onPressed});
@override
Widget build(BuildContext context) {
return ElevatedButton(onPressed: onPressed, child: Text(label));
}
}
Ошибка 2: Забыть dispose
// Плохо — утечка памяти
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late StreamSubscription subscription;
@override
void initState() {
super.initState();
subscription = stream.listen((_) {});
// НЕ закрыто!
}
}
// Хорошо
@override
void dispose() {
subscription.cancel(); // Очистка!
super.dispose();
}
Ошибка 3: Использовать Stateful когда нужен Provider
// Плохо
class UserProfile extends StatefulWidget { /* много кода */ }
// Хорошо — используй BLoC/Provider
class UserProfile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<UserBloc, UserState>(
builder: (context, state) => buildUI(state),
);
}
}
Правило большого пальца
Используй Stateless по умолчанию.
Перевдитесь на Stateful только если:
- Виджет имеет внутреннее состояние (counter, form fields)
- Нужны lifecycle методы (initState, dispose)
- Используете TextEditingController или AnimationController
- Нельзя использовать StateManagement
Для большей части UI используйте Stateless + StateManagement (Provider/BLoC).
Этот подход делает код чище, тестируемее и проще поддерживать.