Could not find correct provider above widget - flutter

I am relatively new to Flutter and am building an app that fetches data from an API and display it throughout the app on different widgets. I am using a Provider to perform the API calls and store the deserialized json in a property I then access in the initState function. Below is the widget tree of my main.dart, I want to then access the provider found here in the Dashboard page as this is where the value is required. I know what I am doing is wrong, but how much have I gotten it wrong?
runApp(MaterialApp(
home: LoginPage(),
routes: routes,
));
}
class SampleApp extends StatefulWidget {
#override
SampleAppState createState() => SampleAppState();
}
class SampleAppState extends State<SampleApp> {
int currentIndex = 0;
final List<Widget> childPages = [
Page1(),
Page2(),
Page3(),
Page4()
];
void onTapped(int index) {
setState(() {
currentIndex = index;
});
}
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => APIProvider())
],
child: Scaffold(
body: childPages[currentIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: onTapped,
currentIndex: currentIndex,
fixedColor: Colors.lightBlue,
backgroundColor: Colors.white,
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
icon: new Icon(Icons.dashboard),
title: new Text('Page 1'),
activeIcon: new Icon(
Icons.dashboard,
color: Colors.lightBlue,
)),
BottomNavigationBarItem(
icon: new Icon(Icons.list),
title: new Text('Page 2'),
activeIcon: new Icon(
Icons.list,
color: Colors.lightBlue,
)),
BottomNavigationBarItem(
icon: new Icon(Icons.collections_bookmark),
title: new Text('Page 3'),
activeIcon: new Icon(
Icons.collections_bookmark,
color: Colors.lightBlue,
)),
BottomNavigationBarItem(
icon: new Icon(Icons.person),
title: new Text('Page 4'),
activeIcon: new Icon(
Icons.person,
color: Colors.lightBlue,
)),
],
),
),
);
}
}
I then attempt to access the provider value in Page1 like so:
#override
void initState() {
valueFromProvider =
Provider.of<APIProvider>(context).providerValue;
super.initState();
}
Perhaps this is happening because the provider value I attempt to access in the Page1 widget is null, would pre-loading my provider solve this?

You aren't providing the Provider at the right time. In your case you add the MultiProvider at page1, but your context is already initialized so you can't access it. In order to properly access it, you must move the MultiProvider to where you initialize the app like the following:
runApp(
MaterialApp(
home: MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => APIProvider())
],
child:LoginPage()
),
routes: routes,
));
This should fix your issue.

Related

How to assing routes to an array of widget screens?

i have the following routes in my main
routes: {
'/sign-in': (context) => BlocProvider(
lazy: false,
create: (_) => AuthCubit(),
child: const LandingPage(),
),
'/home': (context) => const HomeLandingPage(),
'/sign-up': (context) => const SignUpLandingPage(),
'/language-selection': (context) => const SelectionLanguageScreen(),
'/camera-page': (context) => CameraPage(),
'/web-add-page': (context) => const WebAddPage()
},
i'm having issues implementing Routes to my WebAddPage() because WebAddPage() is a widget that is threated as a screen.
final screens = [ //screens is a List<Widget>
const WebAddPage(),
const WebUpdateProducts(),
const WebUpdateCategories(),
const WebUpdateStores(),
const WebUpdateUsers()
];
i'm not using Navigator.push because i'm not changing to a new screen i'm just changing widgets. is there a way the implement the Routes system to this array. i also need the url path to matches the route.
Navigator.push apparently didn't work, also adding the key word of the route change the type of the List<Widget> to a List<object> but because im implementing the screens into a child i can't use the List<object> because the type 'Object' can't be assigned to the parameter type 'Widget?'
You can look at the package flutter autoroute: https://pub.dev/packages/auto_route that allows you to easily setup your project around routes, nested routes architecture and paths.
However if you still want to continue with your architecture, then you cannot switch your routes while using a one page changing Widget dynamically from an index.
So my advice is to use autoroute and if you need redirections, look also at Route Guards.
I think it can help you
final List<Widget> pages = const [
HomePage(),
SearchPage(),
NotificationsPage(),
SettingPage() ];
#override Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
_title,
style: Theme.of(context).textTheme.headline2,
)),
body: pages[_currentIndex],
bottomNavigationBar: Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(color: ColorManager.lightGray, spreadRadius: 0.5)
],
),
child: BottomNavigationBar(
selectedItemColor: ColorManager.primary,
unselectedItemColor: ColorManager.gray,
currentIndex: _currentIndex,
onTap: onTap,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home), label: AppStrings.home),
BottomNavigationBarItem(
icon: Icon(Icons.search), label: AppStrings.search),
BottomNavigationBarItem(
icon: Icon(Icons.notifications),
label: AppStrings.notification),
BottomNavigationBarItem(
icon: Icon(Icons.settings), label: AppStrings.setting),
],
),
),
); }

Flutter Drawer Overlaps the bottom Navigation bar

----------
Here is the 1st-screenshot of my problem
This is the 2nd-second screenshot when after implementing single child scrolled view
I have created a navigation drawer and a Bottom navigation widget, i have face the following problems/
While opening drawer it says the drawer exceeds XX pixels so i wrapped it up in "Single child scroll view and now the drawer opens up like a whole page.
Also, when drawer is pressed the Bottom navigation overlaps it.
I have added images which you can see through my clicking above.
here is my piece of code.
class Mydrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
UserAccountsDrawerHeader(
accountName: Text('Name'),
accountEmail: Text('Username'),
currentAccountPicture: CircleAvatar(
backgroundColor: Colors.white,
child: Text('Hi'),
),
),
ListTile(
leading: Icon(Icons.home),
title: Text(
'Home Page',
),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).pushNamed(MyHomepage.route);
}),
ListTile(
leading: Icon(Icons.person),
title: Text(
'My Account',
),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).pushNamed(Account.route);
},
),
ListTile(
leading: Icon(Icons.assignment),
title: Text(
'My Lists',
),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).pushNamed(Mylist.route);
},
),
ListTile(
leading: Icon(Icons.bookmark),
title: Text(
'Wishlist',
),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).pushNamed(Wishlist.route);
},
),
Divider(),
ListTile(
leading: Icon(Icons.mail),
title: Text(
'Contact us',
),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).pushNamed(Contactus.route);
},
),
ListTile(
leading: Icon(Icons.info),
title: Text(
'Info & FAQ',
),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).pushNamed(Infofaq.route);
},
),
Divider(),
ListTile(
leading: Icon(Icons.lock_open),
title: Text(
'Logout',
),
onTap: () {
Navigator.pop(context);
},
),
],
),
),
),
);
}
}
Bottom Navigation Code
class Nav extends StatefulWidget {
#override
_NavState createState() => _NavState();
}
class _NavState extends State<Nav> {
int _selectedIndex = 0;
final List<Widget> _widgetOptions = [
NavHome(),
NavInspiration(),
NavNotification(),
NavMessages(),
];
void _onitemtap(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _widgetOptions[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
showSelectedLabels: false,
showUnselectedLabels: false,
type: BottomNavigationBarType.fixed,
onTap: _onitemtap,
currentIndex: _selectedIndex,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.filter_none),
title: Text('Inspiration'),
),
BottomNavigationBarItem(
icon: Icon(Icons.notifications_none),
title: Text('Notifications'),
),
BottomNavigationBarItem(
icon: Icon(Icons.mail_outline),
title: Text('Messages'),
),
],
),
);
}
}
Main Dart
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter demo',
home: Nav(),
theme: ThemeData(
primarySwatch: Colors.blue,
),
//home: Homepage(),
//initialRoute: '/',
routes: {
MyHomepage.route: (_) => MyHomepage(),
Account.route: (_) => Account(),
Mylist.route: (_) => Mylist(),
Wishlist.route: (_) => Wishlist(),
Contactus.route: (_) => Contactus(),
Infofaq.route: (_) => Infofaq(),
},
);
}
}
----------
Check this!
It's work for me
use drawer part of scaffold().
The best thing that can be done here is, just like you have your Drawer seperate, create a seperate navbar class with no scaffold or body, just the navbar. Now, call both of these, the drawer and the navbar, in a third widget, which contains your scaffold. For changing the index, you can pass the function as a parameter to your navbar widget.
i have changed how u relay your codes, try implementing this in your code. and i have decomposed Nav(), so take in mind that im not using the Nav() as awhole
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: MainEntry(),
theme: ThemeData(
primarySwatch: Colors.blue,
),
//home: Homepage(),
//initialRoute: '/',
routes: {
MyHomepage.route: (_) => MyHomepage(),
Account.route: (_) => Account(),
Mylist.route: (_) => Mylist(),
Wishlist.route: (_) => Wishlist(),
Contactus.route: (_) => Contactus(),
Infofaq.route: (_) => Infofaq(),
},
);
}
}
class MainEntry extends StatefulWidget {
#override
_MainEntryState createState() => _MainEntryState();
}
class _MainEntryState extends State<MainEntry> {
#override
Widget build(BuildContext context) {
return MaterialApp(debugShowCheckedBanner: false,
home: Scaffold(
drawer: MyDrawer(),
body: _widgetOptions[_selectedIndex] //your body
bottomNavigationBar: BottomNavigationBar(
showSelectedLabels: false,
showUnselectedLabels: false,
type: BottomNavigationBarType.fixed,
onTap: _onitemtap,
currentIndex: _selectedIndex,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.filter_none),
title: Text('Inspiration'),
),
BottomNavigationBarItem(
icon: Icon(Icons.notifications_none),
title: Text('Notifications'),
),
BottomNavigationBarItem(
icon: Icon(Icons.mail_outline),
title: Text('Messages'),
),
],
)
)
);
}
}

Flutter: "Multiple GlobalKeys in the Widget tree" while Navigation

I'm facing an issue of "Multiple GlobalKeys in the Widget tree" while Navigation.
I have a BottomNavigationBar & a Drawer defined in the Scaffold of a Base Screen and in the body parameter of Scaffold I have multiple screens which I'm accessing with BottomNavigationBar. The thing is, I'm accessing the Drawer of the Base Screen from one of the multiple screens by using a GlobalKey, and everything's working fine but when I Navigate to the Base Screen from Another Screen then I get the above-mentioned error.
I have tried a solution of not using a static keyword while defining the key and it solves the error of navigation but then I can't access the Drawer because then I get another error of "method 'openDrawer' was called on null".
This is a separate class where I have defined the Key:
class AppKeys {
final GlobalKey<ScaffoldState> homeKey = GlobalKey<ScaffoldState>();
}
This is the Base Screen:
class Base extends StatefulWidget {
#override
_BaseState createState() => _BaseState();
}
class _BaseState extends State<Base> {
int selectedScreen = 0;
final screens = List<Widget>.unmodifiable([Home(), Cart(), Orders(), Help()]);
AppKeys appKeys = AppKeys();
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: MyDrawer(),
key: appKeys.homeKey,
body: screens[selectedScreen],
bottomNavigationBar: SizedBox(
height: 80,
child: BottomNavigationBar(
onTap: (val) {
setState(() {
selectedScreen = val;
});
},
currentIndex: selectedScreen,
selectedItemColor: AppColor.primary,
elevation: 20.0,
unselectedItemColor: Colors.grey,
showUnselectedLabels: true,
type: BottomNavigationBarType.fixed,
iconSize: 25,
selectedFontSize: 15,
unselectedFontSize: 15,
items: [
BottomNavigationBarItem(
icon: Icon(AnanasIcons.home), title: Text("Home")),
BottomNavigationBarItem(
icon: Icon(AnanasIcons.cart), title: Text("Cart")),
BottomNavigationBarItem(
icon: Icon(AnanasIcons.orders), title: Text("My Orders")),
BottomNavigationBarItem(
icon: Icon(AnanasIcons.help), title: Text("Help")),
],
),
),
);
}
}
This is the Home Screen from where I'm accessing the Drawer:
class Home extends StatelessWidget {
final AppKeys appKeys = AppKeys();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(
AnanasIcons.menu,
color: Colors.black,
),
onPressed: () {
appKeys.homeKey.currentState.openDrawer();
}),
backgroundColor: Theme.of(context).canvasColor,
title: Text("Hi"),
actions: [
IconButton(
icon: Icon(
Icons.person,
color: Colors.black,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Profile(),
));
})
],
),
body: Container(),
);
}
}
I've found the answer! Instead of pushing the route with the key, we need to remove all the routes from the stack till the screen where we have to go.
Here's the code for that:
Navigator.of(context).popUntil(ModalRoute.withName(Base.routeName));

How to make Bottom Navigation Bar visible on every page of my app in flutter?

Please, anyone, tell me how can I make Bottom Navigation Bar visible on every page of my app in flutter? I know there's an option called Custom Navigator (https://pub.dev/packages/custom_navigator), but how to use this for more than 2 subpages? Please help me I am stucked on a big project. Thank you in Advance :)
you just need to change widgets on the same page, not navigating, check this code out!
import 'package:flutter/material.dart';
import './pages/home.dart'; //imported widget 1
import './pages/listed_homes.dart'; //imported widget 2
import './widgets/form_card.dart'; //imported widget 3
class BottomNav extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return BottomNavState();
}
}
class BottomNavState extends State<BottomNav> {
int _currentIndex = 0; //initialize index that alters widgets when increased or decreased`
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (value) {
_currentIndex = value;
setState(() {});
},
items: [
BottomNavigationBarItem(
//<--- item 1 text and icon declared
icon: Icon(Icons.book),
title: Text('Find a home')),
BottomNavigationBarItem(
//<--- item 2 text and icon declared
icon: Icon(Icons.add_a_photo),
title: Text('Enlist a home')),
BottomNavigationBarItem(
//<--- item 3 text and icon declared
icon: Icon(Icons.message),
title: Text('Messages')),
]),
body: Stack(children: [
[
Home(_cent), //widget one
FormCard(widget.model), //widget two
Messages() //widget three
][_currentIndex], //Alter widgets with changing index
Positioned(
top: 30,
left: 15,
child: IconButton(
icon: Icon(Icons.menu),
onPressed: () {},
padding: EdgeInsets.all(0.0),
iconSize: 40.0,
),
)
]),
);
}
}
Check this method to keep a widget on every page:
MaterialApp(
title: 'Flutter Demo',
initialRoute:"/home",
routes: [
...
],
builder: (context, child) {
return Stack(
children: [
child!,
Overlay(
initialEntries: [
OverlayEntry(
builder: (context) {
return YourCustomWidget(); *//This widget now appears on all pages*
},
),
],
),
],
);
},

How can I have the BottomNavigationBar respond to navigation to a new page via AlertDialog?

I have a navigation_bar.dart file that handles changing to new pages within my app. Within it, I am using the bottomNavigationBar to build out four different pages based on what tab is currently selected like so:
class NavigationBar extends StatefulWidget {
#override
_NavigationBarState createState() => _NavigationBarState();
}
class _NavigationBarState extends State<NavigationBar> {
int _selectedIndex = 0;
final List<Widget> _pageOptions = <Widget>[
Page1(),
Page2(),
Page3(),
Page4(),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
String userID =
Provider.of<FirebaseUser>(context, listen: false) != null ? Provider.of<FirebaseUser>(context).uid : 'null';
return MultiProvider(
providers: [StreamProvider<MyUser>.value(value: DatabaseService().streamUser(userID))],
child: Scaffold(
body: IndexedStack(
children: _pageOptions,
index: _selectedIndex,
),
bottomNavigationBar: Theme(
data: Theme.of(context).copyWith(
canvasColor: Color(0xff271037).withOpacity(0.90),
splashColor: Colors.transparent,
),
child: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: _onItemTapped,
unselectedItemColor: Colors.white,
selectedItemColor: Color(0xff3ADEA7),
type: BottomNavigationBarType.fixed,
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Container(),
title: Icon(FontAwesomeIcons.fire, color: Colors.white),
),
BottomNavigationBarItem(
icon: Container(),
title: Icon(Icons.fastfood, color: Colors.white),
),
BottomNavigationBarItem(
icon: Container(),
title: Icon(Icons.directions_bike, color: Colors.white),
),
BottomNavigationBarItem(
icon: Container(),
title: Icon(Icons.person, color: Colors.white),
)
],
),
),
),
);
}
}
Now, in a different file which is Page3.dart, on that page there is an alert dialog that pops up and when clicked, I want it to navigate to Page4().
Future<void> _showMissingDataDialog(String data) async {
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('You have not set your $data yet.'),
actions: <Widget>[
FlatButton(
splashColor: Colors.transparent,
highlightColor: Colors.grey[200],
textColor: Colors.black,
child: const Text('Cancel'),
onPressed: () => Navigator.of(context).pop(),
),
FlatButton(
splashColor: Colors.transparent,
highlightColor: Colors.grey[200],
textColor: Colors.black,
child: Text('Set $data', style: TextStyle(fontWeight: FontWeight.bold)),
onPressed: () {
Navigator.of(context).pop();
// TODO: Redirect to page4() here as if it was tapped on the BottomNavigationBar
})
],
);
},
);
}
How can I have it so that clicking the "Set $data" button would route to Page4()? I want to make it so that the bottomNavigationBar reacts to this as if you tapped on the actual fourth BottomNavigationBarItem item.
Give your Nav Bar a Global Key. I declared this outside of all widgets on my main Dart file.
GlobalKey navBarGlobalKey = GlobalKey(debugLabel: 'bottomAppBar');
bottomNavigationBar: BottomNavigationBar(
key: navBarGlobalKey,
onTap: _onItemTapped,
currentIndex: _selectedIndex,
type: BottomNavigationBarType.fixed,
items: [ ... ]
Then use the global key to call the onTap Method in the onPressed method of your button. You will have to import the other dart file into this page before the global key is available.
final BottomNavigationBar navigationBar = navBarGlobalKey.currentWidget;
initialIndex = 0;
navigationBar.onTap(3); //Starts at index 0, so passing in 3 should do the trick.