Issue
I am developing a flutter website with a persistent navigation bar on top that displays the current page the user is on, by adding a border around the title of the active page as shown below.
I am using url navigation on the website, so the user may be able to access different pages using the specific url:
- webpage.com/#/about : Opens the 'About Me' page
- webpage.com/#/skills : Opens the 'Skills' page and so on
Since the users can access a website page directly, I need to set the state of the navigation bar upon loading the page based on the url. But I am unable to call setState during build and I get the following error:
Another exception was thrown: setState() or markNeedsBuild() called during build.
I am using Navigators and Routes to take of the url navigation as follows:
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: Strings.appName,
theme: ThemeData(
primarySwatch: Colors.cyan,
),
builder: (context, child) => Dashboard(child: child!),
navigatorKey: locator<NavigationService>().navigatorKey,
onGenerateRoute: generateRoute,
initialRoute: Routes.aboutRoute,
);
}
}
I am using Provider to control the state of the navigation bar as follows:
class NavigationBarProvider extends ChangeNotifier {
List<bool> isSelected = [false, false, false, false];
List<bool> isHovering = [false, false, false, false];
void setSelected(int _index) {
isSelected = List.filled(4, false);
isSelected[_index] = true;
notifyListeners();
}
void setHovering(int _index, bool _val) {
isHovering[_index] = _val;
notifyListeners();
}
bool isSelHov(int _index) {
return isSelected[_index] || isHovering[_index];
}
}
This is my widget for the Navigation Bar buttons:
return InkWell(
onTap: () {
locator<NavigationService>().navigateTo(routeName);
provider.setSelected(index);
},
onHover: (value) {
provider.setHovering(index, value);
},
child: SizedBox(
width: 100.0,
height: 50.0,
child: Stack(
children: [
Center(
child: AnimatedContainer(
duration: const Duration(milliseconds: 500),
curve: Curves.easeInCirc,
width: provider.isSelHov(index) ? 100.0 : 0.0,
height: provider.isSelHov(index) ? 50.0 : 0.0,
decoration: BoxDecoration(
border: Border.all(
color: Colors.white,
width: provider.isSelHov(index) ? 0.0 : 10.0,
),
borderRadius: BorderRadius.circular(provider.isSelHov(index) ? 10.0 : 50.0),
),
),
),
Center(
child: Text(
name,
style: const TextStyle(
fontSize: 16.0,
color: Colors.white,
),
),
),
],
),
),
);
Please help me understand how I may be able to go about this to set the state, and show the active Navigation Button when the website is launched using a specific url.
I appreciate any little help. Thank you very much.
EDIT
This is my code for the Navigation Bar, with the indexes:
ChangeNotifierProvider<NavigationBarProvider>(
create: (context) => NavigationBarProvider(),
child: Consumer<NavigationBarProvider>(
builder: (context, provider, child) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
NavBarButton(
index: 0,
name: 'About Me',
routeName: Routes.aboutRoute,
provider: provider,
),
NavBarButton(
index: 1,
name: 'Projects',
routeName: Routes.projectsRoute,
provider: provider,
),
NavBarButton(
index: 2,
name: 'Skills',
routeName: Routes.skillsRoute,
provider: provider,
),
NavBarButton(
index: 3,
name: 'Contact Me',
routeName: Routes.contactsRoute,
provider: provider,
),
],
);
},
),
)
EDIT 2
I managed to find a solution myself, and I have provided it below as one of the answers. Thank you.
Solution
I have managed to find a solution. I implemented Provider across the whole app, and changed the selected values when generating Routes as well. The following is my working code:
My runApp:
runApp(
ChangeNotifierProvider<NavBarProvider>(
create: (context) => NavBarProvider(),
child: const MyApp(),
),
);
I am using a custom RouteRouteBuilder as follows:
_FadeRoute(
{required this.index, required this.child, required this.routeName})
: super(
settings: RouteSettings(name: routeName),
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
Future.delayed(Duration.zero, () async {
Provider.of<NavBarProvider>(context, listen: false).setSelected(index);
});
return child;
},
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return FadeTransition(
opacity: animation,
child: child,
);
},
);
The rest of the code is the same, except I removed ChangeNotifierProvider as the parent in the NavBar Widget, and just left Consumer as follows:
Consumer<NavigationBarProvider>(
builder: (context, provider, child) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
NavBarButton(
index: 0,
name: 'About Me',
routeName: Routes.aboutRoute,
provider: provider,
),
NavBarButton(
index: 1,
name: 'Projects',
routeName: Routes.projectsRoute,
provider: provider,
),
NavBarButton(
index: 2,
name: 'Skills',
routeName: Routes.skillsRoute,
provider: provider,
),
NavBarButton(
index: 3,
name: 'Contact Me',
routeName: Routes.contactsRoute,
provider: provider,
),
],
);
},
)
Answered By - Abinandhan Selvaraj
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.