You create an error notification with Notice.error('Retry'), but its blocking field is false. The constructor name promises one meaning while the actual state says another. The likely cause is that two construction paths duplicate the field-initialization rules.
How does a named constructor differ from the unnamed one?
Notice(message: 'Saved') calls an unnamed constructor. Notice.error('Retry') calls a named constructor that expresses the reason for creating the object. The name does not validate the fields automatically, however. If each constructor initializes message, level, and blocking separately, one copied rule can drift.
How can separate initializers produce inconsistent values?
This runnable example creates the same Retry notice in two ways. Manual intentionally duplicates blocking = false incorrectly; Redirect forwards to another constructor. Save it as main.dart and run dart --enable-asserts main.dart:
class NoticeManual {
final String message;
final String level;
final bool blocking;
NoticeManual({required this.message, this.level = 'info'})
: blocking = level == 'error';
NoticeManual.error(this.message)
: level = 'error',
blocking = false; // Deliberately inconsistent copied rule.
}
class NoticeRedirect {
final String message;
final String level;
final bool blocking;
NoticeRedirect({required this.message, this.level = 'info'})
: blocking = level == 'error';
NoticeRedirect.error(String message)
: this(message: message, level: 'error');
}
void main() {
final manual = NoticeManual.error('Retry');
final redirected = NoticeRedirect.error('Retry');
final ordinary = NoticeRedirect(message: 'Saved');
print('manual: ${manual.level}, blocking=${manual.blocking}');
print('redirect: ${redirected.level}, blocking=${redirected.blocking}');
print('ordinary: ${ordinary.level}, blocking=${ordinary.blocking}');
assert(manual.blocking == false);
assert(redirected.blocking == true);
assert(ordinary.blocking == false);
}manual: error, blocking=false
redirect: error, blocking=true
ordinary: info, blocking=falseThe first object has level == 'error' but blocking == false. The program runs, so this bug can be harder to spot than an exception. The redirected constructor gives the error and ordinary notices the intended values.
What does this(...) centralize?
The diagram shows manual initialization drifting away from the shared rule. A this(...) redirect keeps blocking derived from level in one place.
NoticeRedirect.error is both named and redirecting. “Named” describes how callers select it; “redirecting” describes how it forwards initialization to another constructor. The Dart constructor guide documents both forms.
This does not prevent every invalid state. level is still any string, including a typo such as 'erorr'. If only specific levels are valid, use an enum or validate them in the shared path. If constructors have no shared initialization rule, a redirect may add complexity without benefit.
Key takeaways
A named constructor clarifies intent at the call site. Duplicating field rules across constructors can create inconsistent objects. When the rules should be shared, redirect with this(...) and test the actual field values as well as the constructor name.

