Flutter get value from provider - flutter

I am using provider as state management. To use value between multiple files i create a provider like this
class globalProvider with ChangeNotifier, DiagnosticableTreeMixin {
String uuID = "";
String get _uuID => uuID;
void changeuuID(id) {
uuID = id;
notifyListeners();
}
}
I am updating value like this
final uuidUpdate = Provider.of<globalProvider>(context, listen: false);
uuidUpdate.changeuuID(user.uid);
Now on other page I need to print uuID value. I try to do like this
print(Provider.of<globalProvider>(context).uuID);
But its showing error Tried to listen to a value exposed with provider, from outside of the widget tree.
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => globalProvider()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: LoginPage(),
),
),
);
}

you need to add your state up in your widgets tree like:
ChangeNotifierProvider(
create: (context) => globalProvider (),
builder: (context, _) {
return theRestOfTheTreeWidgets();}
)

in your main.dart add ChangeNotifierProvider.value(value: GlobalProvider) in MultiProvider widget,
Make sure that your class starts with capital letter and let me know if arrow still exists.

You need to print it out with listen: false like this,
print(Provider.of<globalProvider>(context, listen: false).uuID);

You can use this package
get_it: ^7.2.0
class GlobalProvider with ChangeNotifier, DiagnosticableTreeMixin {
String _uuID = "";
String get uuID => _uuID;
void changeuuID(id) {
_uuID = id;
notifyListeners();
}
}
in your main.dart .please must use Upper cast
and paste the below code in main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await di.init();
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => di.sl<GlobalProvider >()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
home: LoginPage(),
),
),
);
}
make a new dart file and paste this code
final sl = GetIt.instance;
Future<void> init() async {
sl.registerFactory(() => GlobalProvider ());
}
and I use this init() function in main file already ..just import library

Related

ChangeNotifierProvider isn't listening

I am attempting to share a ChangeNotifierProvider to my main.dart, however the value never gets updated.
How it works
main.dart uses ChangeNotifierProvider to get an instance of the class Location()
main.dart routes to the location_login.dart page where a string in Location() class is set.
The instance of Location() should update in main.dart but it DOES NOT.
Here is the main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
ChangeNotifierProvider<Location>.value( <------ CREATE CHANGENOTIFIERPROVIDER
value: Location(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
Location location = Provider.of<Location>(context, listen: false); <----- LISTEN TO PROVIDER
return MultiProvider(
providers: [
FutureProvider<List<Report>>(
create: (context) =>
Collection<Report>(path: '${location.getLocation}/data/reports') <----- USE PROVIDER STRING IN PATH
.getUsers(),
initialData: [],
),
],
child: MaterialApp(
routes: {
'/': (context) => LocationLogin(),
'/login': (context) => LoginScreen(),
'/home': (context) => HomeScreen(),
},
// Theme
theme: ThemeData(
fontFamily: 'Nunito',
bottomAppBarTheme: BottomAppBarTheme(
color: Colors.black87,
),
// your customizations here
brightness: Brightness.dark,
buttonTheme: ButtonThemeData(),
),
),
);
}
}
Here is the location_login.dart
#override
Widget build(BuildContext context) {
Location location = Provider.of<Location>(context, listen: true);
return Scaffold(
body: TextButton(
child: Text("Submit",
style: GoogleFonts.poppins(
fontSize: 15.sp, color: Colors.white)),
onPressed: () {
location.setLocation('London'); <------- SETTING LOCATION
}),
);
}
}
Here is the location.dart
class Location with ChangeNotifier {
String place = 'none';
String get getLocation => place;
setLocation(String location) {
place = location;
notifyListeners();
}
}
To reiterate, the issue is that when I click the button in the location_login.dart page to set the location to "London"; it does not update the ChangeNotifierProvider with a new instance of the Location() class containing "London". Therefore, I can not update the path in my FurtureProvider. Any ideas of what is going wrong here? I tried to make this as clear as possible but if you don't understand please ask. Thank you
I think you have not consume the ChangeNotifierProvider.
For me below simple implementation work perfectly.
my main.dart file code is as below...
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'providers/app_provider.dart';
import 'providers/favorites_provider.dart';
import 'providers/comments_provider.dart';
import 'providers/home_provider.dart';
import 'providers/details_provider.dart';
import 'providers/gallery_provider.dart';
import 'providers/chat_provider.dart';
import 'ui/splash.dart';
import 'helper/constants.dart';
import 'ui_user/login.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => AppProvider()),
ChangeNotifierProvider(create: (_) => GalleryProvider()),
ChangeNotifierProvider(create: (_) => CommentsProvider()),
ChangeNotifierProvider(create: (_) => ChatProvider()),
ChangeNotifierProvider(create: (_) => HomeProvider()),
ChangeNotifierProvider(create: (_) => DetailsProvider()),
ChangeNotifierProvider(create: (_) => FavoritesProvider()),
],
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<AppProvider>(
builder: (BuildContext context, AppProvider appProvider, Widget child) {
return MaterialApp(
key: appProvider.key,
debugShowCheckedModeBanner: false,
navigatorKey: appProvider.navigatorKey,
title: Constants.appName,
theme: appProvider.theme,
home: appProvider.isLogin == "0" ? LoginPage() : Splash(),
);
},
);
}
}
And my app_provider.dart as below...
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../helper/constants.dart';
class AppProvider extends ChangeNotifier {
AppProvider() {
checkTheme();
}
String isLogin = "0";
ThemeData theme = Constants.lightTheme;
Key key = UniqueKey();
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
void setKey(value) {
key = value;
notifyListeners();
}
void setNavigatorKey(value) {
navigatorKey = value;
notifyListeners();
}
void setTheme(value, c) {
theme = value;
SharedPreferences.getInstance().then((prefs) {
prefs.setString("theme", c).then((val) {
SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor:
c == "dark" ? Constants.darkPrimary : Constants.lightPrimary,
statusBarIconBrightness:
c == "dark" ? Brightness.light : Brightness.dark,
));
});
});
notifyListeners();
}
ThemeData getTheme(value) {
return theme;
}
Future<ThemeData> checkTheme() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
ThemeData t;
String r =
prefs.getString("theme") == null ? "light" : prefs.getString("theme");
isLogin = prefs.getString("isLogin") == null? "0" : prefs.getString("isLogin");
if (r == "light") {
t = Constants.lightTheme;
setTheme(Constants.lightTheme, "light");
} else {
t = Constants.darkTheme;
setTheme(Constants.darkTheme, "dark");
}
return t;
}
}
This solution is working very well for me. Hope this will help you too...

Flutter. Could not find the correct Provider<ThemeChanger> above this Home Widget

I wanted to add theme with provider to my code. I adapted it from this source. https://github.com/lohanidamodar/flutter_theme_provider/blob/master/lib/main.dart .
Even it is same code, I got this error:
"The following ProviderNotFoundException was thrown building Home(dirty, state: _HomeState#c900c):
Error: Could not find the correct Provider above this Home Widget"
This happens because you used a BuildContext that does not include the provider
of your choice.
void main() async {
setPathUrlStrategy();
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MaterialAppWithTheme());
}
class MaterialAppWithTheme extends StatefulWidget {
#override
_MaterialAppWithThemeState createState() => _MaterialAppWithThemeState();
}
class _MaterialAppWithThemeState extends State<MaterialAppWithTheme> {
#override
void initState() {
super.initState();
AppRouter appRouter = AppRouter(
routes: AppRoutes.routes,
notFoundHandler: AppRoutes.routeNotFoundHandler,
);
appRouter.setupRoutes();
}
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => ThemeNotifier(),
child: Consumer<ThemeNotifier>(
builder: (context, ThemeNotifier notifier, child) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: notifier.darkTheme ? dark : light,
onGenerateRoute: AppRouter.router.generator,
);
},
),
);
}
}
Change this:
create: (_) => ThemeNotifier(),
To this:
create: (context) => ThemeNotifier(),

Flutter - Could not find the correct Provider<Provider> above this ChangeLocation Widget

I'm using both BlocProvider & ChangeNotifierProvider in my app. The flow of the app goes here:-
first time user opens the app: InstructionPage() -> WelcomePage() -> HomePage() //getting error
second time user opens the app: HomePage() //working fine
I'm using sharedPreference to store the value of isInstructionPageLoaded.
But navigating from WelcomePage() to HomePage() getting error Could not find the correct Provider above this ChangeLocation Widget
here is my code:-
//main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await StorageUtil.getInstance();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: Theme.of(context).copyWith(primaryColor: kBgColorGreen),
home: MultiBlocProvider(
providers: [
BlocProvider(
create: (context) =>
RestaurantBloc()..add(RestaurantPageFetched())),
],
child: MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => LocationServiceProvider()),
],
child: StorageUtil.getBoolValue(
SharedPrefsKeys.isInstructionPageLoaded)
? HomePage()
: InstructionScreen(),
)),
routes: Routes.getRoutes(),
);
}
}
//routes.dart
class Routes {
static const String instruction = '/instruction';
static const String welcome = '/welcome';
static const String home = '/home';
static const String change_location = '/change_location';
static Map<String, WidgetBuilder> getRoutes() {
return {
Routes.instruction: (context) => InstructionScreen(),
Routes.welcome: (context) => WelcomePage(),
Routes.home: (context) => HomePage(),
Routes.change_location: (context) => ChangeLocation(),
};
}
}
//location_service.dart
class LocationServiceProvider extends ChangeNotifier {
void toogleLocation(LocationService location) {
location.isLocationUpdated = !location.isLocationUpdated;
notifyListeners();
}
}
class LocationService {
bool isLocationUpdated = false;
}
//welcome_page.dart -
on button pressed calling below method
void _navigateToHomePage() async {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return BlocProvider(
create: (context) => RestaurantBloc()..add(RestaurantPageFetched()),
child: ChangeNotifierProvider(create: (context) => LocationServiceProvider(),
child: HomePage(),),
);
}));
}
I have added BlocProvider in above method becoz before it was giving me error
blocprovider.of() called with a context that does not contain a bloc navigating from other screen from navigating from WelcomePage() to HomePage().
Thanks in advance!!!
To make sure the blocs are exposed to new routes, you need to follow the documentation and add BlocProvider.value() to provide the value of the bloc to new routes. This will carry the bloc's state and make your life easier.
Check the Official Documentations for a clear step-by-step guide ;).

Flutter main.dart initialRout is not Working

I'm New in this FrameWork and here initial Rout is not Accepting the Loggin Session value Please help me with this. I tried to add Home with the splash screen but that also not working I'm not getting What's wrong in this.
This is my main Page
Future main() async {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
));
runApp(Phoenix(child: AmericanCuisine()));
}
class AmericanCuisine extends StatefulWidget {
#override
_AmericanCuisineState createState() => _AmericanCuisineState();
}
class _AmericanCuisineState extends State<AmericanCuisine> {
bool isLoggedIn;
#override
void initState() {
super.initState();
getData();
}
getData() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences storage = await SharedPreferences.getInstance();
setState(() {
isLoggedIn = storage.getBool("loggedIn");
});
}
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<LanguageCubit>(
create: (context) => LanguageCubit(),
),
BlocProvider<ThemeCubit>(
create: (context) => ThemeCubit(),
),
],
in this page after using BlockBuilder how i To give the Initial Route
child: BlocBuilder<ThemeCubit, ThemeData>(
builder: (_, theme) {
return BlocBuilder<LanguageCubit, Locale>(
builder: (_, locale) {
return MaterialApp(
localizationsDelegates: [
const AppLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
const Locale('en'),
],
locale: locale,
theme: theme,
//This initial rout is not working.
initialRoute: isLoggedIn == false ?'/': '/homeOrderAccount',
routes: {
// When navigating to the "/" route, build the FirstScreen widget.
'/': (context) => OpeningScreen(),
'/homeOrderAccount': (context) => HomeOrderAccount(),
},
);
},
);
},
),
);
}
}
You can't use initialRoute with routes map either delete '/' from the routes map or delete the initialRoute:

TextFormField does not reflect initialValue from a ChangeNotifierProvider

I am trying to set the initialValue of my TextFormField from my provider. This works but not on inital load of my screen. For this to work, I need to go to previous screen, then come back. Here are the relevant source codes.
User class:
class User with ChangeNotifier {
String name;
String email;
User() {
_deserialiseUser();
}
}
void _deserialiseUser() async {
final prefs = await SharedPreferences.getInstance();
final String jsonUser = prefs.getString('user');
if (jsonUser != null) {
var ju = jsonDecode(jsonUser);
name = ju['name'];
email = ju['email'];
notifyListeners();
}
}
Main class:
void main() => runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => User()),
...
],
child: MyApp(),
),
);
The affected screen:
class AccountScreen extends StatefulWidget {
#override
_AccountScreenState createState() => _AccountScreenState();
}
class _AccountScreenState extends State<AccountScreen> {
User user;
#override
Widget build(BuildContext context) {
user = context.watch<User>();
return Scaffold(
appBar: AppBar(
title: Text('Account'),
),
body: TextFormField(
initialValue: user.name,
),
);
}
}
If I replace my TextFormField with Text, this works fine. What am I missing?
EDIT: Although I already have the answer, I will still accept answers that can explain the root cause of the issue.
I solved the issue by not lazily loading my User provider like so:
void main() => runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => User(),
lazy: false,
),
],
child: MyApp(),
),
);