Getting error when trying to navigate to different sceens using reusable widget with different functions passed to it (set to an InkWell's onTap)
Error
The following assertion was thrown building ProfileSettings(dirty, dependencies: [MediaQuery, _InheritedProviderScope<FirebaseService?>]): setState() or markNeedsBuild() called during build. This Overlay widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase. The widget on which setState() or markNeedsBuild() was called was: Overlay-[LabeledGlobalKey#dfb28] The widget which was currently being built when the offending call was made was: ProfileSettings
The relevant error-causing widget was: ProfileSettings ProfileSettings:file:///C:/Thrills/Thrills/lib/screens/profile/profile.dart:242:31
Code
Reusable widget (in Settings page):
buildButton(IconData icon, String label, void fcn) {
return Container(
child: Material(
color: Colors.transparent,
clipBehavior: Clip.hardEdge,
child: InkWell(
onTap: () => fcn,
... [ some code ] ...
Calling widget (in Settings page):
... [ some code ] ...
Container(
padding:
EdgeInsets.only(left: 15.0, top: 20.0, bottom: 5.0),
alignment: Alignment.topLeft,
child: Text(
"Account",
style: TextStyle(
color: Colors.black,
fontSize: 18,
),
),
),
buildButton(Icons.email, "Email",
goToPersonalInfoScreen(context, "Email")),
buildButton(Icons.date_range, "Birthday", {}),
... [ some code ] ...
Function calling Settings page:
void goToSettings(BuildContext context) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProfileSettings(
currentUserId: widget.currentUserId,
),
),
);
}
Screen to navigate to:
void goToPersonalInfoScreen(BuildContext context, String title) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PersonalInfo(
currentUserId: currentUserId,
title: title,
),
),
);
}
Last screen in flow (showing what is called as soon as it is added to widget tree):
class PersonalInfo extends StatefulWidget {
const PersonalInfo({
Key? key,
required this.currentUserId,
required this.title,
}) : super(key: key);
final String currentUserId;
final String title;
@override
_PersonalInfoState createState() => _PersonalInfoState();
}
class _PersonalInfoState extends State<PersonalInfo> {
TextEditingController textController = TextEditingController();
late UserModel currentUser;
late final FirebaseService fbs;
@override
void initState() {
super.initState();
}
@override
void didChangeDependencies() async {
super.didChangeDependencies();
fbs = Provider.of<FirebaseService>(context);
await fbs.getCurrentUserData().then((value) => {
currentUser = value,
setField(),
});
}
setField() {
setState(() {
if (widget.title == "Email")
textController.text = currentUser.email;
else if (widget.title == "Radius")
textController.text = currentUser.radius.toString();
/* displayNameController.selection = TextSelection.fromPosition(
TextPosition(offset: displayNameController.text.length)); */
});
}
The initState() and didChangeDependencies() runs before the build runs, and setState(() {}) triggers the build function, that's why this error is caused.
You should remove the setState(() {}) from setField() {} method.
setField() {
if (widget.title == "Email")
textController.text = currentUser.email;
else if (widget.title == "Radius")
textController.text = currentUser.radius.toString();
/* displayNameController.selection = TextSelection.fromPosition(
TextPosition(offset: displayNameController.text.length)); */
}