Flutter Mobx multiple store not updating value in observer - flutter

I am using Mobx in my flutter application. I have multiple store. When I am trying to update some value in a store it is updating but not reflecting on UI.
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
Provider<LocationStore>(create: (_) => LocationStore()),
Provider<UserStore>(create: (_) => UserStore()),
Provider<RideStore>(create: (_) => RideStore()),
],
child: MaterialApp(
title: 'FLutter APp',
theme: myTheme(),
debugShowCheckedModeBanner: false,
// home: HomePage(
// title: 'Live Location'
// ),
initialRoute: WelcomeScreen.id,
routes: {
HomePage.id: (context) => HomePage(title: 'Home',),
ProfileScreen.id: (context) => ProfileScreen(),
WelcomeScreen.id: (context) => WelcomeScreen(),
RegistrationScreen.id: (context) => RegistrationScreen(),
LoginScreen.id: (context) => LoginScreen(),
FeedScreen.id: (context) => FeedScreen(),
RecordTrackScreen.id: (context) => RecordTrackScreen(),
SaveActivityScreen.id: (context) => SaveActivityScreen(),
UserProfileScreen.id: (context) => UserProfileScreen(),
RideDetailsScreen.id: (context) => RideDetailsScreen(),
StatsScreen.id: (context) => StatsScreen(),
FindInviteScreen.id: (context) => FindInviteScreen(),
CommentsScreen.id: (context) => CommentsScreen(),
},
),
);
}
}
screen
#override
Widget build(BuildContext context) {
final store = Provider.of<LocationStore>(context);
final rideStore = Provider.of<RideStore>(context);
final userStore = Provider.of<UserStore>(context);
return Scaffold(
appBar: AppBar(
title: Text('Record'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.info_outline),
onPressed: () {},
)
],
),
body: Observer(
builder: (_) => SingleChildScrollView(
child: Center(
child: Column(
children: <Widget>[
// Text('isPermitted - ${isPermitted}'),
Container(
child: isPermitted != null && isPermitted ? WeatherMainWidget() : Text(''),
height: (MediaQuery.of(context).size.height - 50.0) * 0.4,
),
Container(
height: (MediaQuery.of(context).size.height - 50.0) * 0.45,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
// Text('rideStore.getIsStarted - ${rideStore.getIsStarted} - $_isRunning'),
// if (rideStore.isStarted)
Divider(
indent: 10.0,
endIndent: 10.0,
thickness: 1.0,
color: color8,
),
Observer(
builder: (_) => Row(
children: [
Text('${rideStore.getSport.name}'),
Expanded(
flex: 1,
child: CustomIconButton(
iconData: rideStore.getSport.icon,
iconColor: Theme.of(context).primaryColor,
bgColor: Colors.transparent,
onPress: () {
SportBottomSheet(context, rideStore);
},
),
),
],
),
),
Container(
child: Visibility(
child: RecordTrackScreen(),
visible: rideStore.isStarted,
),
),
if (!rideStore.isStarted)
Container(
width: 70.0,
height: 70.0,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(50.0),),
color: Theme.of(context).primaryColor,
),
child: Visibility(
visible: !rideStore.isStarted,
child: CustomIconButton(
iconData: Icons.play_arrow,
iconColor: Colors.white,
iconSize: 35.0,
onPress: () {
print('dsdsadsadsad');
startTrip(store, rideStore, userStore);
},
),
),
),
],
),
),
// ListenLocationWidget()
],
),
),
),
),
);
}
Widget SportBottomSheet(BuildContext context, RideStore rideStore) {
showModalBottomSheet(context: context, builder: (BuildContext bc) {
return Container(
color: Colors.white,
child: ListView.builder(itemCount: 2, itemBuilder: (ctx, index) {
final sport = rideStore.sports[index];
return GestureDetector(
child: ListTile(
leading: Icon(sport.icon, size: 30.0, color: Colors.black,),
title: Text(sport.name, style: TextStyle(
fontSize: 20.0,
color: Colors.black
),),
onTap: () {
print('SportBottomSheet $sport');
rideStore.selectedSport = new Sport(
sport.icon,
sport.name
);
rideStore.recentSport = rideStore.recentSport;
Navigator.pop(context);
print('sport ${rideStore.getSport.name}');
},
),
);
}),
);
});
}
ride store
class Sport{
final IconData icon;
final String name;
Sport(this.icon, this.name);
}
List<Sport> sports = [
new Sport(Icons.directions_walk_outlined, 'Walk'),
new Sport(Icons.motorcycle, 'Ride'),
];
#observable
Sport recentSport;
#observable
Sport selectedSport;
#computed
Sport get getSport {
if (selectedSport != null)
return selectedSport;
if (recentSport != null)
return recentSport;
return sports[0];
}
When I am checking getSport value in onTap method it is showing selected value but it is not updating on UI.
Thanks

Try changing your #computed to #action
#action
Sport get getSport {
if (selectedSport != null)
return selectedSport;
if (recentSport != null)
return recentSport;
return sports[0];
}
or maybe use ObservableList or ObservableFuture

Use ObservableList like this
#observable
ObservableList<Sport> sports =[Sport(Icons.directions_walk_outlined, 'Walk'), new Sport(Icons.motorcycle, 'Ride'),];

Related

Different button will take me through different pages in Flutter

I'm working on a flutter project. I have a multiple button (Container wrap in Inkwell) using only one button in code and in List. I have tried to handle multiple buttons work in a List. But there is an error showing on the onTap function.
import 'package:flutter/material.dart';
import '../Ambulance/AmbulanceHome.dart';
import '../Blood Bank/BloodHome.dart';
import '../CreateCase/CaseHome.dart';
import '../Doctor Appoinment/HomeScreen.dart';
class BottomHomePage extends StatelessWidget {
List<FeaturesList> featuresList = [
FeaturesList('assets/BloodBank.jpg', 'Blood Bank', (context) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) {
return HomeScreen();
},
),
);
}),
FeaturesList('assets/BloodBank.jpg', 'Doctor Appoinment', (context) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) {
return HomeScreen();
},
),
);
}),
FeaturesList('assets/BloodBank.jpg', 'Create a Case', (context) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) {
return HomeScreen();
},
),
);
}),
FeaturesList('assets/BloodBank.jpg', 'Ambulance', (context) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) {
return HomeScreen();
},
),
);
}),
];
// void selectFeatures(BuildContext context) {
// Navigator.of(context).push(
// MaterialPageRoute(
// builder: (_) {
// return HomeScreen();
// },
// ),
// );
// }
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFD9E4EE),
body: Padding(
padding: const EdgeInsets.all(10),
child: Column(
children: [
Container(
width: double.infinity,
height: 150,
decoration: BoxDecoration(
color: Theme.of(context).primaryColor.withOpacity(0.8),
borderRadius: BorderRadius.circular(20),
),
child: const Text(
'Fade-in logo of our project added here',
textAlign: TextAlign.center,
),
),
const SizedBox(height: 20),
Container(
alignment: AlignmentDirectional.topStart,
child: Text(
"Features",
style: Theme.of(context).textTheme.headline6,
),
),
const SizedBox(height: 20),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(
featuresList.length,
(index) {
return Column(
children: [
InkWell(
onTap: () => featuresList.callback?.call(context),
child: Container(
width: 150,
height: 150,
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(50),
// color: Theme.of(context)
// .colorScheme
// .primaryContainer
// .withOpacity(0.4),
),
child: Image.asset(featuresList[index].icon),
),
),
Text(featuresList[index].name),
],
);
},
),
),
),
],
),
),
);
}
}
class FeaturesList {
final String icon;
final String name;
final Function(BuildContext) callback;
FeaturesList(
this.icon,
this.name,
this.callback,
);
}
I have tried to use the navigation route in the List, but when I try to call the function called callback (which is defined in Features List class).
The error is on the line:
onTap: () => featuresList.callback?.call(context),
redline on the callback
Error message:
The getter 'callback' isn't defined for the type 'List'.
Try importing the library that defines 'callback', correcting the name to the name of an existing getter, or defining a getter or field named 'callback'.
featuresList is a List, so it does not have callback property. You should use
onTap: () => featuresList[index].callback?.call(context)

Provider cannot get value

i am building mobile app using flutter. i try to use provider but it doesn't get the value.
this is the code for class change notifier
class StoreProvider with ChangeNotifier{
UserServices _userServices = UserServices();
StoreServices _storeServices = StoreServices();
User user = FirebaseAuth.instance.currentUser;
String userLocation = "";
String selectedStore;
String selectedStoreId;
getSelectedStore(storeName, storeId){
this.selectedStore = storeName;
this.selectedStoreId = storeId;
notifyListeners();
}
}
this is the code where i call this function
ListView(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
children: snapshot.data.docs.map((DocumentSnapshot document) {
return InkWell(
onTap: (){
_storeData.getSelectedStore(document['shopName'], document['uid']);
pushNewScreenWithRouteSettings(
context,
settings: RouteSettings(name: VendorHomeScreen.id),
screen: VendorHomeScreen(),
withNavBar: true,
pageTransitionAnimation: PageTransitionAnimation.cupertino,
);
},
child: Padding(
padding: const EdgeInsets.only(top: 4, right: 4, left: 4),
child: Container(
width: 120,
child: Column(
children: [
SizedBox(
width: 120,
height: 100,
child: Card(
child: ClipRRect(
borderRadius: BorderRadius.circular(4),
child: Image.network(document['imageUrl'],))),
),
Container(
height: 35,
child: Text(document['shopName'], style: TextStyle(
fontSize: 14, fontWeight: FontWeight.bold,
), maxLines: 2, overflow: TextOverflow.ellipsis,),
),
],
),
),
),
);
}).toList(),
),
this is the code where i want to get the value that i put to the provider
var _store = Provider.of<StoreProvider>(context);
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return [
SliverAppBar(
iconTheme: IconThemeData(
color: Colors.white,
),
actions: [
IconButton(
onPressed: () {},
icon: Icon(CupertinoIcons.search),
)
],
title: Text(
**//this is the error**
**//this is the error**
**//this is the error**
**//this is the error**
**//this is the error**
**_store.selectedStoreId,**
style:
TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
),
)
];
},
body: Center(
child: Text('vendor screen'),
),
));
and this is the main file
void main() async {
Provider.debugCheckInvalidValueType = null;
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => AuthProvider()),
ChangeNotifierProvider(create: (_) => StoreProvider()),
],
child: MyApp(),
));
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(primaryColor: Colors.lightBlueAccent, fontFamily: 'Lato'),
initialRoute: SplashScreen.id,
routes: {
SplashScreen.id: (context) => SplashScreen(),
LoginPage.id: (context) => LoginPage(),
SignupPage.id: (context) => SignupPage(),
HomePage.id: (context) => HomePage(),
ResetPassword.id: (context) => ResetPassword(),
MainScreen.id: (context)=> MainScreen(),
VendorHomeScreen.id: (context) => VendorHomeScreen(),
},
);
}
}
i always get 'a non null A non-null String must be provided to a Text widget.'
i think the problem is when i put the value in the change notifier class and i try to get the value in other class, the value is null.
does anyone know what is the mistake??
thank you

Flutter/Dart - Text value not showing correctly

I am trying to create a shopping cart using provider and display the number of items currently in the cart on my homepage. When I create my cart icon with a text widget overlaid, the value being shown does not reflect the number of items in the cart.
Here is my code:
class OurShoppingBasketIcon extends StatelessWidget {
const OurShoppingBasketIcon({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.center,
child: InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ShoppingBasketScreen()),
);
},
child: Stack(
children: <Widget>[
new Icon(
Icons.shopping_cart_outlined,
color: Colors.white,
),
new Positioned(
right: 0,
child: new Container(
padding: EdgeInsets.all(1),
decoration: new BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(6),
),
constraints: BoxConstraints(
minWidth: 12,
minHeight: 12,
),
child: Text(
context.read<ShoppingBasket>().items.length.toString(),
style: new TextStyle(
color: Colors.white,
fontSize: 8,
),
textAlign: TextAlign.center,
),
),
)
],
),
),
);
}
}
This is where the icon is implemented:
class OurHomePage extends StatefulWidget {
#override
_OurHomePageState createState() => _OurHomePageState();
}
class _OurHomePageState extends State<OurHomePage> {
#override
Widget build(BuildContext context) {
return Consumer<OurUser>(
builder: (_, user, __) {
return ChangeNotifierProvider<SignInViewModel>(
create: (_) => SignInViewModel(context.read),
builder: (_, child) {
return Scaffold(
appBar: AppBar(
title: Text("My app"),
actions: [
OurShoppingBasketIcon(),
IconButton(
icon: Icon(
Icons.logout,
color: Colors.white,
),
onPressed: () {
context.read<FirebaseAuthService>().signOut();
},
),
],
),
);
},
);
},
);
}
}
There are 2 items in the cart as of writing this:
But the icon on the homepage does not change:
Here is my main.dart:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MultiProvider(
providers: [
Provider(
create: (_) => FirebaseAuthService(),
),
StreamProvider<OurUser>(
create: (context) =>
context.read<FirebaseAuthService>().onAuthStateChanged),
ChangeNotifierProvider.value(
value: ShoppingBasket(),
),
],
child: MaterialApp(theme: OurTheme().buildTheme(), home: OurHomePage()),
),
);
}
perhaps if you watch for the value it will be updated dynamically:
context.watch<ShoppingBasket>().items.length.toString(), //<-- watch instead of read
The OurHomePage needs to be wrapped in the Provider<ShoppingBasket>.
return Provider<ShoppingBasket>(
create: (context) => ShoppingBasket(),
child: Consumer<OurUser>(
builder: (_, user, __) {
return ChangeNotifierProvider<SignInViewModel>(
create: (_) => SignInViewModel(context.read),
builder: (_, child) {
return Scaffold(
appBar: AppBar(
title: Text("My app"),
actions: [
OurShoppingBasketIcon(),
IconButton(
icon: Icon(
Icons.logout,
color: Colors.white,
),
onPressed: () {
context.read<FirebaseAuthService>().signOut();
},
),
],
),
),
);
},
);
I forgot to NotifyListeners() in my Change Notifier class:
class ShoppingBasket extends ChangeNotifier {
Map<String, SingleBasketItem> _items = {};
Map<String, SingleBasketItem> get items {
return {..._items};
}
void addItem(String id) {
_items.putIfAbsent(
id,
() => SingleBasketItem(id),
);
notifyListeners(); //HERE
}

Why isn't Navigator.pop() refreshing data?

Hi guys I'm trying to build an app with flutter, so I have two screens HomeScreen() and RoutineScreen(). The first one is a Scaffold and in the body has a child Widget (a ListView called RoutinesWidget()) with all the routines. And the second one is to create a routine. The thing is, that when I create the routine, I use a button to pop to the HomeScreen() but it doesn't refresh the ListView (I'm guessing that it's because when I use Navigator.pop() it refreshes the Scaffold but not the child Widget maybe?)
HomeScreen() code here:
import 'package:flutter/material.dart';
import 'package:workout_time/constants.dart';
import 'package:workout_time/Widgets/routines_widget.dart';
import 'package:workout_time/Widgets/statistics_widget.dart';
import 'package:workout_time/Screens/settings_screen.dart';
import 'package:workout_time/Screens/routine_screen.dart';
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int _selectedIndex = 0;
List<Widget> _views = [
RoutinesWidget(),
StatisticsWidget(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kThirdColor,
appBar: AppBar(
leading: Icon(Icons.adb),
title: Text("Workout Time"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.settings),
onPressed: () => Navigator.push(context,
MaterialPageRoute(builder: (context) => SettingsScreen()))),
],
),
body: _views[_selectedIndex],
floatingActionButton: (_selectedIndex == 1)
? null
: FloatingActionButton(
onPressed: () async {
await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RoutineScreen(null)));
setState(() {});
},
child: Icon(
Icons.add,
color: kSecondColor,
size: 30.0,
),
elevation: 15.0,
),
bottomNavigationBar: BottomNavigationBar(
items: <BottomNavigationBarItem>[
bottomItems(Icon(Icons.fitness_center_rounded), "Routines"),
bottomItems(Icon(Icons.leaderboard_rounded), "Statistics"),
],
currentIndex: _selectedIndex,
onTap: (int index) => setState(() => _selectedIndex = index),
),
);
}
}
BottomNavigationBarItem bottomItems(Icon icon, String label) {
return BottomNavigationBarItem(
icon: icon,
label: label,
);
}
RoutinesWidget() code here:
import 'package:flutter/material.dart';
import 'package:workout_time/Services/db_crud_service.dart';
import 'package:workout_time/Screens/routine_screen.dart';
import 'package:workout_time/constants.dart';
import 'package:workout_time/Models/routine_model.dart';
class RoutinesWidget extends StatefulWidget {
#override
_RoutinesWidgetState createState() => _RoutinesWidgetState();
}
class _RoutinesWidgetState extends State<RoutinesWidget> {
DBCRUDService helper;
#override
void initState() {
super.initState();
helper = DBCRUDService();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: helper.getRoutines(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
Routine routine = Routine.fromMap(snapshot.data[index]);
return Card(
margin: EdgeInsets.all(1.0),
child: ListTile(
leading: CircleAvatar(
child: Text(
routine.name[0],
style: TextStyle(
color: kThirdOppositeColor,
fontWeight: FontWeight.bold),
),
backgroundColor: kAccentColor,
),
title: Text(routine.name),
subtitle: Text(routine.exercises.join(",")),
trailing: IconButton(
icon: Icon(Icons.delete_rounded),
color: Colors.redAccent,
onPressed: () {
setState(() {
helper.deleteRoutine(routine.id);
});
},
),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RoutineScreen(routine))),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0)),
color: kSecondColor,
);
},
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
);
}
}
RoutineScreen() code here:
import 'package:flutter/material.dart';
import 'package:workout_time/Models/routine_model.dart';
import 'package:workout_time/Widgets/type_card_widget.dart';
import 'package:workout_time/constants.dart';
import 'package:workout_time/Services/db_crud_service.dart';
class RoutineScreen extends StatefulWidget {
final Routine _routine;
RoutineScreen(this._routine);
#override
_RoutineScreenState createState() => _RoutineScreenState();
}
class _RoutineScreenState extends State<RoutineScreen> {
DBCRUDService helper;
final _nameController = TextEditingController();
final _descriptionController = TextEditingController();
bool _type = true;
int _cycles = 1;
int _restBetweenExercises = 15;
int _restBetweenCycles = 60;
#override
void initState() {
super.initState();
helper = DBCRUDService();
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context),
),
title: widget._routine != null
? Text(widget._routine.name)
: Text("Create your routine"),
actions: [
IconButton(
icon: Icon(Icons.done_rounded),
onPressed: createRoutine,
)
],
bottom: TabBar(
tabs: [
Tab(
text: "Configuration",
),
Tab(
text: "Exercises",
),
],
),
),
body: TabBarView(children: [
//_routine == null ? ConfigurationNewRoutine() : Text("WIDGET N° 1"),
ListView(
children: [
Container(
padding: EdgeInsets.all(15.0),
child: Row(
children: [
Text(
"Name:",
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
SizedBox(
width: 40.0,
),
Expanded(
child: TextField(
textAlign: TextAlign.center,
controller: _nameController,
),
),
],
),
),
SizedBox(
height: 20.0,
),
Card(
margin: EdgeInsets.all(15.0),
color: kSecondColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
child: Container(
padding: EdgeInsets.all(15.0),
child: Column(
children: [
Text(
"Type",
style: TextStyle(fontSize: 25.0),
),
Row(
children: [
Expanded(
child: TypeCard(
Icons.double_arrow_rounded,
_type == true ? kFirstColor : kThirdColor,
() => setState(() => _type = true),
"Straight set",
),
),
Expanded(
child: TypeCard(
Icons.replay_rounded,
_type == false ? kFirstColor : kThirdColor,
() => setState(() => _type = false),
"Cycle",
),
),
],
),
],
),
),
),
SizedBox(
height: 20.0,
),
Container(
padding: EdgeInsets.all(15.0),
child: Row(
children: [
Text(
"N° cycles:",
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
SizedBox(
width: 40.0,
),
Expanded(
child: Text("Hello"),
),
],
),
),
SizedBox(
height: 20.0,
),
],
),
Text("WIDGET N° 2"),
]),
),
);
}
void createRoutine() {
List<String> _exercises = ["1", "2"];
List<String> _types = ["t", "r"];
List<String> _quantities = ["30", "20"];
Routine routine = Routine({
'name': _nameController.text,
'description': "_description",
'type': _type.toString(),
'cycles': 1,
'numberExercises': 2,
'restBetweenExercises': 15,
'restBetweenCycles': 60,
'exercises': _exercises,
'types': _types,
'quantities': _quantities,
});
setState(() {
helper.createRoutine(routine);
Navigator.pop(context);
});
}
}
Any idea what can I do to make it work? Thank you
Make it simple
use Navigator.pop() twice
so that the current class and old class in also removed
from the stack
and then use Navigator.push()
When you push a new Route, the old one still stays in the stack. The new route just overlaps the old one and forms like a layer above the old one. Then when you pop the new route, it will just remove the layer(new route) and the old route will be displayed as it was before.
Now you must be aware the Navigator.push() is an asynchronous method and returns a Future. How it works is basically when you perform a Navigator.push(), it will push the new route and will wait for it to be popped out. Then when the new route is popped, it returns a value to the old one and that when the future callback will be executed.
Hence the solution you are looking for is add a future callback like this after your Navigator.push() :
Navigator.push(context,
MaterialPageRoute(builder: (context) => SettingsScreen())
).then((value){setState(() {});}); /// A callback which is executed after the new route will be popped. In that callback, you simply call a setState and refresh the page.

Flutter Provider Error: The following ProviderNotFoundException was thrown building HomePage(dirty, state: flutter: _HomePageState#fb424):

My code was actually working and is currently working with the app built from the previous version of flutter. I built it again using the same code but with the Flutter 1.22 upgrade and its causing error now on Provider. Since, the code actually worked, I can't seem to figure out why it is throwing an error.
Here is my code:
The file or the error causing widget according to flutter:
'''
class HomePage extends StatefulWidget {
static final String id = 'home_page';
final String currentUserId;
HomePage({this.currentUserId});
#override
_HomePageState createState() => _HomePageState();
}
CategoryProvider categoryProvider;
ProductProvider productProvider;
class _HomePageState extends State<HomePage> {
// variables
double height, width;
bool homeColor = true;
bool checkoutColor = false;
bool aboutColor = false;
bool contactUsColor = false;
bool profileColor = false;
MediaQueryData mediaQuery;
TextEditingController searchTextEditingController = TextEditingController();
//category each tile change to Service
Widget _buildCategoryProduct({String name, String image, int color}) {
return Container(
child: Column(
children: <Widget>[
Container(
height: 50,
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
boxShadow: shadowList,
color: Colors.white,
borderRadius: BorderRadius.circular(10)),
// maxRadius: height * 0.1 / 2.1,
// backgroundColor: Colors.white,
child: Image(
image:
/// changed
AssetImage('images/category/$image'),
),
),
Text(
name,
style: GoogleFonts.raleway(
fontSize: 12,
fontWeight: FontWeight.bold,
letterSpacing: 1.0,
textStyle: TextStyle(color: Colors.black),
),
),
],
),
);
}
// firebase auth drawer details
/// look into it after
Widget _buildUserAccountsDrawerHeader() {
List<UserModel> userModel = productProvider.userModelList;
return Column(
children: userModel.map((e) {
return UserAccountsDrawerHeader(
accountName: Text(
e.userName,
style: TextStyle(color: Colors.black),
),
currentAccountPicture: CircleAvatar(
backgroundColor: Colors.white,
backgroundImage: e.userImage == null
? AssetImage("images/userImage.png")
: NetworkImage(e.userImage),
),
decoration: BoxDecoration(color: Color(0xfff2f2f2)),
accountEmail: Text(e.userEmail, style: TextStyle(color: Colors.black)),
);
}).toList());
}
//build drawer left side/sidebar
Widget _buildMyDrawer() {
return Drawer(
child: ListView(
children: <Widget>[
_buildUserAccountsDrawerHeader(),
ListTile(
selected: homeColor,
onTap: () {
setState(() {
homeColor = true;
contactUsColor = false;
checkoutColor = false;
aboutColor = false;
profileColor = false;
});
},
leading: Icon(Icons.home),
title: Text("Home"),
),
ListTile(
selected: checkoutColor,
onTap: () {
setState(() {
checkoutColor = true;
contactUsColor = false;
homeColor = false;
profileColor = false;
aboutColor = false;
});
// Navigator.of(context).pushReplacement(
// MaterialPageRoute(builder: (ctx) => CheckOut()));
},
leading: Icon(Icons.shopping_cart),
title: Text("Checkout"),
),
ListTile(
selected: aboutColor,
onTap: () {
setState(() {
aboutColor = true;
contactUsColor = false;
homeColor = false;
profileColor = false;
checkoutColor = false;
});
// Navigator.of(context).pushReplacement(
// MaterialPageRoute(builder: (ctx) => About()));
},
leading: Icon(Icons.info),
title: Text("About"),
),
ListTile(
selected: profileColor,
onTap: () {
setState(() {
aboutColor = false;
contactUsColor = false;
homeColor = false;
profileColor = true;
checkoutColor = false;
});
// Navigator.of(context).pushReplacement(
// MaterialPageRoute(
// builder: (ctx) => ProfileScreen(),
// ),
// );
},
leading: Icon(Icons.info),
title: Text("Profile"),
),
ListTile(
selected: contactUsColor,
onTap: () {
setState(() {
contactUsColor = true;
checkoutColor = false;
profileColor = false;
homeColor = false;
aboutColor = false;
});
// Navigator.of(context).pushReplacement(
// MaterialPageRoute(builder: (ctx) => ContactUs()));
},
leading: Icon(Icons.phone),
title: Text("Contant Us"),
),
ListTile(
onTap: () {
FirebaseAuth.instance.signOut();
},
leading: Icon(Icons.exit_to_app),
title: Text("Logout"),
),
],
),
);
}
/// carousel on top/ change images ^^finalized
Widget _buildImageSlider() {
return Container(
height: 200,
child: Carousel(
borderRadius: true,
radius: Radius.circular(20),
autoplay: true,
autoplayDuration: Duration(seconds: 10),
showIndicator: false,
images: [
// change it up to more approp
AssetImage('images/banner2.jpg'),
AssetImage('images/banner1.jpg'),
AssetImage('images/banner4.jpg'),
],
),
);
}
// build category/services row """"
Widget _buildCategory() {
return Column(
children: <Widget>[
Container(
height: 40,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"SERVICES",
style: GoogleFonts.caveat(
fontSize: 20,
fontWeight: FontWeight.bold,
letterSpacing: 3.0,
textStyle: TextStyle(color: Colors.black),
),
),
],
),
),
Container(
height: 70,
// change new
child: ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
Row(
children: <Widget>[
// each Service icon
_buildHairIcon(),
SizedBox(width: 20),
_buildWaxIcon(),
SizedBox(width: 20),
_buildPedicureIcon(),
SizedBox(width: 20),
_buildManicureIcon(),
SizedBox(width: 20),
_buildFacialIcon(),
],
),
],
),
),
],
);
}
// row of featured and archives view more
Widget _buildNewAchives() {
final Orientation orientation = MediaQuery.of(context).orientation;
return Container(
/// look into it
height: 500,
child: GridView.count(
crossAxisCount: orientation == Orientation.portrait ? 2 : 3,
childAspectRatio: orientation == Orientation.portrait ? 0.8 : 0.9,
children: productProvider.getHomeAchiveList.map((e) {
return GestureDetector(
onTap: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (ctx) => DetailScreen(
userId: widget.currentUserId,
hairdresserId: e.id,
image: e.image,
rating: e.rating,
name: e.name,
surname: e.surname,
description: e.description,
city: e.city,
waxPrice: e.waxPrice,
facialPrice: e.facialPrice,
manicurePrice: e.manicurePrice,
pedicurePrice: e.pedicurePrice,
hairPrice: e.hairPrice,
),
),
);
},
child: SingleProduct(
image: e.image, rating: e.rating, name: e.name, city: e.city),
);
}).toList(),
),
);
}
// row of featured and archives view more
Widget _buildRow() {
List<Product> newAchivesProduct = productProvider.getNewAchiesList;
return Container(
height: height * 0.1 - 30,
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"FEATURED ",
style: GoogleFonts.caveat(
fontSize: 20,
fontWeight: FontWeight.bold,
letterSpacing: 3.0,
textStyle: TextStyle(color: Colors.black),
),
),
GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (ctx) => ListProduct(
userId: widget.currentUserId,
name: "Featured",
isCategory: false,
snapShot: newAchivesProduct,
),
),
);
},
child: Text(
"View all",
style: GoogleFonts.raleway(
fontSize: 15,
fontWeight: FontWeight.bold,
letterSpacing: 1.0,
textStyle: TextStyle(color: Colors.black),
),
),
)
],
),
],
),
);
}
final GlobalKey<ScaffoldState> _key = GlobalKey<ScaffoldState>();
// to get from online firebase database//change names
void getCallAllFunction() {
categoryProvider.getWaxData();
categoryProvider.getHairData();
categoryProvider.getPedicureData();
categoryProvider.getManicureData();
categoryProvider.getFacialData();
categoryProvider.getHairIconData();
productProvider.getNewAchiveData();
productProvider.getFeatureData();
productProvider.getHomeFeatureData();
productProvider.getHomeAchiveData();
categoryProvider.getWaxIcon();
categoryProvider.getPedicureIconData();
categoryProvider.getManicureIconData();
categoryProvider.getFacialIconData();
// productProvider.getUserData();
}
#override
Widget build(BuildContext context) {
//from models product= hairdresser data
categoryProvider = Provider.of<CategoryProvider>(context);
productProvider = Provider.of<ProductProvider>(context);
final String currentUserId = Provider.of<UserData>(context).currentUserId;
getCallAllFunction();
height = MediaQuery.of(context).size.height;
width = MediaQuery.of(context).size.width;
return Scaffold(
key: _key,
drawer: _buildMyDrawer(),
// bottomNavigationBar: BottomNavBar(),
appBar: AppBar(
toolbarOpacity: 0,
shape: RoundedRectangleBorder(),
// search field
title: Text(
'The Mob',
style: TextStyle(
color: Colors.white,
fontFamily: 'Billabong',
fontSize: 35.0,
),
),
elevation: 0.0,
backgroundColor: Colors.blueGrey,
leading: IconButton(
icon: SvgPicture.asset("images/menu.svg"),
onPressed: () {
_key.currentState.openDrawer();
},
),
actions: <Widget>[
NotificationButton(),
],
),
body: Container(
height: double.infinity,
width: double.infinity,
margin: EdgeInsets.symmetric(horizontal: 20),
child: ListView(
scrollDirection: Axis.vertical,
children: <Widget>[
SizedBox(height: 5),
Container(
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_buildImageSlider(),
_buildCategory(),
_buildRow(),
_buildNewAchives(),
],
),
),
],
),
),
);
}
}
'''
I have provided the multi providers in the main while with child as MaterialApp. Hence the main file looks like this:
'''
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => ServicesNotifier(),
),
ChangeNotifierProvider(
create: (context) => HairdresserData(),
),
ChangeNotifierProvider(
create: (context) => ServicesNotifier(),
),
ChangeNotifierProvider<CategoryProvider>(
create: (context) => CategoryProvider(),
),
ChangeNotifierProvider<ProductProvider>(
create: (context) => ProductProvider(),
),
ChangeNotifierProvider(
create: (context) => UserData(),
),
],
child: MyApp(),
),
);
}
'''
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: OnboardingScreen(),
routes: {
// '/a': (_) => Authenticate(),
SignInScreen.id: (context) => SignInScreen(),
RegisterScreen.id: (context) => RegisterScreen(),
LoginScreen.id: (context) => LoginScreen(),
SignupScreen.id: (context) => SignupScreen(),
'/b': (_) => Customer(),
'/c': (_) => Hairdresser(),
'/d': (_) => Choose(),
'/e': (_) => IndicatorScreen(),
},
);
}
}
The error is as follows:
The following ProviderNotFoundException was thrown building HomePage(dirty, state:
flutter: _HomePageState#fb424):
flutter: Error: Could not find the correct Provider<CategoryProvider> above this HomePage Widget
flutter:
flutter: This likely happens because you used a `BuildContext` that does not include the provider
flutter: of your choice. There are a few common scenarios:
flutter:
flutter: - The provider you are trying to read is in a different route.
flutter:
flutter: Providers are "scoped". So if you insert of provider inside a route, then
flutter: other routes will not be able to access that provider.
flutter:
flutter: - You used a `BuildContext` that is an ancestor of the provider you are trying to read.
flutter:
flutter: Make sure that HomePage is under your MultiProvider/Provider<CategoryProvider>.
flutter: This usually happen when you are creating a provider and trying to read it immediately.
flutter:
flutter: For example, instead of:
flutter:
flutter: ```
flutter: Widget build(BuildContext context) {
flutter: return Provider<Example>(
flutter: create: (_) => Example(),
flutter: // Will throw a ProviderNotFoundError, because `context` is associated
flutter: // to the widget that is the parent of `Provider<Example>`
flutter: child: Text(context.watch<Example>()),
flutter: ),
flutter: }
flutter: ```
flutter:
flutter: consider using `builder` like so:
flutter: ```
flutter: Widget build(BuildContext context) {
flutter: return Provider<Example>(
flutter: create: (_) => Example(),
flutter: // we use `builder` to obtain a new `BuildContext` that has access to the provider
flutter: builder: (context) {
flutter: // No longer throws
flutter: return Text(context.watch<Example>()),
flutter: }
flutter: ),
flutter: }
flutter: ```
I simply resolve this issue by move all the files to lib instead of making provider directory to lib because provider did not able to find another file that we want to use. It works for me.