prefer_immutable_state
This rule flags a class whose name marks it as state and which is missing the @immutable annotation. Which names count is decided by name_pattern — State$ by default — and the check is then widened to every subclass and implementor.
This rule is in the opinionated preset.
Why use this rule
Section titled “Why use this rule”State that can be mutated in place changes without anything observing the change. A Riverpod notifier that assigns to a field of its own state, rather than replacing the state object, updates nothing: the reference is the same, so no listener rebuilds and no widget redraws.
@immutable moves that failure to analysis time. The analyzer reports a non-final field where it is declared, instead of leaving the bug to surface as a screen that will not refresh.
This rule is deliberately state-management-agnostic: it covers Riverpod, a hand-rolled store, and any plain ...State value object equally.
See also: @immutable
A notifier’s state object with a mutable field. Assigning to it changes the value in place, so the reference never changes and nothing rebuilds:
class LoginFormState { LoginFormState({this.email = '', this.isSubmitting = false});
String email; bool isSubmitting;}@immutable makes the analyzer report the non-final field where it is
declared, instead of leaving the bug to surface as a screen that will not
refresh:
import 'package:meta/meta.dart';
@immutableclass LoginFormState { const LoginFormState({this.email = '', this.isSubmitting = false});
final String email; final bool isSubmitting;
LoginFormState copyWith({String? email, bool? isSubmitting}) => LoginFormState( email: email ?? this.email, isSubmitting: isSubmitting ?? this.isSubmitting, );}Naming other suffixes
Section titled “Naming other suffixes”name_pattern decides which names count. A project whose value objects end in
Status as well:
many_lints: rules: prefer_immutable_state: name_pattern: '(State|Status)$'rules: prefer_immutable_state: name_pattern: '(State|Status)$'class UploadStatus { // reported under the pattern above UploadStatus({this.progress = 0});
double progress;}Known limitations
Section titled “Known limitations”Flutter State subclasses are never reported. Every State<T> is named
...State and every one of them is meant to hold mutable fields —
controllers, setState values, subscriptions. They are excluded by type, not
by name, and which base classes count follows the shared
state_base_classes option.
Classes that already inherit @immutable are never reported. Flutter
annotates Widget @immutable, so widget names ending in State —
EmptyState, ErrorState, LoadingState — already carry what this rule asks
for. The check is on supertypes, so it is not Flutter-specific: any base class
annotated @immutable passes it down.
// Not reported: StatelessWidget inherits @immutable from Widget.class PersonPickerEmptyState extends StatelessWidget { const PersonPickerEmptyState({super.key});
@override Widget build(BuildContext context) => const SizedBox.shrink();}A name matched in its entirety is not reported, so a class named exactly
State — the bare affix — is never flagged.
For Bloc and Cubit,
prefer_immutable_bloc_state
recognises the state class through the Bloc<E, S> type argument instead,
which is exact rather than a name match.
Turning this rule off
Section titled “Turning this rule off”To disable this rule:
rules: prefer_immutable_state: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
name_pattern |
regex | State$ |
Pattern identifying state classes by name. A name matched in its entirety is not reported, so the bare affix itself is never flagged |
Related rules
Section titled “Related rules”avoid_state_constructors— Avoid constructors with logic in State classes.prefer_immutable_bloc_state— Ensure Bloc and Cubit state classes are annotated with @immutable.avoid_empty_setstate— Don’t call setState with an empty callback.avoid_inherited_widget_in_initstate— Don’t look up inherited widgets inside initState.