On edit forms, like a profile page, disable the "Save" button until something changes. The user sees there's nothing to save, and when the button turns on after an edit, they know the change counts. It also saves you from network requests that save nothing:
Don't hide the button, a page without it looks not editable. And don't grey it out with opacity, a half-transparent colored button still looks tappable. Use solid grey. Background #F1F5F9 with text #64748B (shadcn's muted colors) are the best values, but style it however fits your app.
Compare the fields with their initial values to know if something changed. So if the user types a letter and deletes it, the button gets disabled again. Here's the simplest way, adapt it to your state management:
late String _initialName = widget.profile.name;late String _initialEmail = widget.profile.email;late final TextEditingController _nameController = TextEditingController( text: _initialName, )..addListener(_onChanged);late final TextEditingController _emailController = TextEditingController( text: _initialEmail, )..addListener(_onChanged);bool get _hasChanges { return _nameController.text.trim() != _initialName || _emailController.text.trim() != _initialEmail;}void _onChanged() { setState(() {});}
The button takes it from there, null means disabled:
The same _hasChanges flag can power a "Discard changes?" prompt when the user leaves the page.
This is for edit forms only. On login and sign up keep the button enabled and show errors on tap, a disabled button there doesn't tell the user what's missing.