Flutter routing after v2 upgrade null check operator - flutter

I have recently updated my flutter project to the V2 sdk I am having an issue with the null check operator for my routing when I try and click on a route the following error comes up Null check operator used on a null value I understand what it means but for some reason my navigatorkey.currentState keeps coming back as null even though it is set in the MaterialApp
Below is my navigation file
import 'package:flutter/material.dart';
class Navigation {
static Navigation instance = Navigation();
GlobalKey<NavigatorState>? navigatorKey = new GlobalKey<NavigatorState>();
Future<dynamic> navigateToReplacement(String newRoute) {
return navigatorKey!.currentState!.pushReplacementNamed(newRoute);
}
Future<dynamic> navigateTo(String newRoute) {
return navigatorKey!.currentState!.pushNamed(newRoute);
}
Future<dynamic> navigateToPage(MaterialPageRoute newRoute) {
return navigatorKey!.currentState!.push(newRoute);
}
goBack() {
return navigatorKey!.currentState!.pop();
}
}
And this is my main.dart file before the upgrade I was not having this issue
import 'package:eventapp/screens/auth/forgotPassword.dart';
import 'package:eventapp/screens/events/add_event.dart';
import 'package:eventapp/services/messaging.dart';
import 'package:flutter/material.dart';
import 'package:eventapp/services/navigation.dart';
import 'package:eventapp/screens/auth/sign_in.dart';
import 'package:eventapp/screens/auth/register.dart';
import 'package:eventapp/screens/home/home.dart';
import 'package:overlay_support/overlay_support.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:get_it/get_it.dart';
GetIt getIt = GetIt.instance;
void main() async {
getIt.registerLazySingleton(() => Navigation());
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final PushNotificationsManager _pushnotifications =
PushNotificationsManager();
final Navigation _navigation = Navigation();
#override
Widget build(BuildContext context) {
_pushnotifications.init();
return OverlaySupport(
child: MaterialApp(
navigatorKey: _navigation.navigatorKey,
theme: ThemeData(
brightness: Brightness.dark,
primaryColor: Color.fromRGBO(112, 17, 15, 1),
backgroundColor: Color.fromRGBO(14, 12, 10, 5),
),
onGenerateRoute: (routeSettings) {
switch (routeSettings.name) {
case 'login':
return MaterialPageRoute(builder: (context) => SignIn());
case 'register':
return MaterialPageRoute(builder: (context) => Registration());
case 'forgotPassword':
return MaterialPageRoute(builder: (context) => ForgotPassword());
case 'addEvent':
return MaterialPageRoute(builder: (context) => AddEvent());
default:
return MaterialPageRoute(builder: (context) => EventList());
}
},
initialRoute: 'login',
));
}
}
Could someone tell me if there is something wrong here?

Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SignforgotPassword()));

Did you set navigatorKey in your MaterialApp?
Should be something like navigatorKey: NavigationService.navigatorKey

Related

Keeps showing Undefined name 'Stripe' while trying to add publishable key

I'm adding Stripe payment to an app I'm building. I've added the Stripe package, imported it and made sure all the requirements are met. But when I try to add publishable key using Stripe.publishableKey, it's showing: Undefined name 'Stripe'. This is the the code in my main.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
...
import 'package:flutter_stripe/flutter_stripe.dart'; //.....stripe package import....
void main() async {
WidgetsFlutterBinding.ensureInitialized();
Stripe.publishableKey = publishableKey; //.....Undefined name 'Stripe'.......
await Firebase.initializeApp();
}
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => ThemeProvider()),
ChangeNotifierProvider(create: (_) => ApplicationState()),
],
child: Consumer<ThemeProvider>(
builder: (context, _themeProvider, snapshot) {
return MaterialApp(
title: 'Title',
debugShowCheckedModeBanner: false,
theme: themeLight(context),
darkTheme: themeDark(context),
themeMode: (_themeProvider.isDarkTheme == true)
? ThemeMode.dark
: ThemeMode.light,
initialRoute: '/',
routes: {
'/': (context) => const SplashScreen(),
'/on-boarding': (context) => const OnBoardingScreen(),
'/all-login-options': (context) =>
const SocialNetworkSignInScreen(),
...
'/home': (context) => const Home(),
},
);
},
),
);
}
}
Has anyone ever faced a similar issue before? Do you have any solutions for it? Any help will be greatly appreciated.
you can give the import a name and use it to access the Stripe class. this is the solution I came up with when facing the same issue
import 'package:flutter_stripe/flutter_stripe.dart' as stripe;
and then in main
stripe.Stripe.publishableKey = publishableKey;

Flutter get value from provider

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

Flutter Error: Could not find the correct Provider<User> above this Wrapper Widget

After updating/converting a program to null-safety, i'm presented with the following error:
Error: Could not find the correct Provider above this Wrapper Widget
This happens because you used a BuildContext that does not include the provider
of your choice. There are a few common scenarios:
You added a new provider in your main.dart and performed a hot-reload.
To fix, perform a hot-restart.
The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
You used a BuildContext that is an ancestor of the provider you are trying to read.
Make sure that Wrapper is under your MultiProvider/Provider.
This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because `context` is associated
// to the widget that is the parent of `Provider<Example>`
child: Text(context.watch<Example>()),
),
}
consider using builder like so:
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context) {
// No longer throws
return Text(context.watch<Example>()),
}
),
}
If none of these solutions work, consider asking for help on StackOverflow:
https://stackoverflow.com/questions/tagged/flutter
The error also points to the "relevant error-causing widget" for me it was my call to my Wrapper(), Highlighted below:
The relevant error-causing widget was:
Wrapper Wrapper:file:///home/AlphaUser/AndroidStudioProjects/test/lib/main.dart:78:20
The stack had more lines I didn't think were relevant. (I can add if needed)
Current Code in use:
Main.dart:
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:test/screens/authenticate/test_forgot.dart';
import 'package:test/screens/authenticate/test_login.dart';
import 'package:test/screens/authenticate/test_signup.dart';
import 'package:test/screens/faq_screen.dart';
import 'package:test/screens/review_purchase_screen.dart';
import 'package:test/screens/purchased_products_screen.dart';
import 'package:test/screens/search_products_screen.dart';
import 'package:test/screens/test_postImage.dart';
import 'package:test/wrapper/wrapper.dart';
import "package:provider/provider.dart";
import 'package:test/services/auth.dart';
import 'models/user.dart';
import 'package:test/screens/product_detail_screen.dart';
import 'package:test/providers/products_provider.dart';
import 'package:test/providers/shippments_provider.dart';
import 'package:test/providers/orders.dart';
import 'package:test/screens/user_products_screen.dart';
import 'package:test/screens/edit_product_screen.dart';
import 'package:test/screens/product_checkout_screen.dart';
import 'package:test/screens/test_feed.dart';
import 'package:test/screens/profile_screen.dart';
import 'package:test/home/home.dart';
import 'package:test/home/homeUser.dart';
import 'package:flutter_stripe/flutter_stripe.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
FirebaseApp app = await Firebase.initializeApp();
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
Stripe.publishableKey =
'stripe_publishableKey';
await Stripe.instance.applySettings();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (ctx) => Shippments(),
),
ChangeNotifierProvider(
create: (ctx) => Orders(),
),
ChangeNotifierProvider(
create: (ctx) => Products(),
),
],
child: StreamProvider<User>.value(
value: AuthService().user,
initialData: null,
child: new MaterialApp(
title: 'Test',
theme: ThemeData(
primarySwatch: Colors.grey,
// accentColor: Colors.green,
),
debugShowCheckedModeBanner: false,
//* Login Options **//
home: Wrapper(), //***<-- Error points to this line.***
//home: Home(), //<-- **Works Fine if I use this instead of Wrapper().** //
//* Login Options **//
routes: {
TestLogin.id: (context) => TestLogin(),
TestSignup.id: (context) => TestSignup(),
TestForgot.id: (context) => TestForgot(),
ProductDetailScreen.routeName: (context) => ProductDetailScreen(),
UserProductsScreen.routeName: (context) => UserProductsScreen(),
EditProductScreen.routeName: (context) => EditProductScreen(),
TestPostImageScreen.routeName: (context) => TestPostImageScreen(),
ProductCheckoutScreen.routeName: (context) =>
ProductCheckoutScreen(),
PurchasedProductsScreen.routeName: (context) =>
PurchasedProductsScreen(),
SearchProductsScreen.routeName: (context) => SearchProductsScreen(),
TestFeed.routeName: (context) => TestFeed(),
ReviewPurchaseScreen.routeName: (context) => ReviewPurchaseScreen(),
ProfileScreen.routeName: (context) => ProfileScreen(),
FAQScreen.routeName: (context) => FAQScreen()
},
),
),
);
}
}
wrapper.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:test/home/home.dart';
import 'package:test/home/homeUser.dart';
import 'package:test/models/user.dart' as model;
import '../screens/authenticate/test_login.dart';
import 'package:http/http.dart' as http;
import 'package:flutter_spinkit/flutter_spinkit.dart';
class Wrapper extends StatefulWidget {
#override
_WrapperState createState() => _WrapperState();
}
class _WrapperState extends State<Wrapper> {
get lists => null;
bool currentAdmin;
bool loading = false;
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
return FutureBuilder(
future: _checkUser(user),
builder: (context, snapshot) {
String profile = snapshot.data;
print("profile!!!: $profile");
if (profile == 'true') {
print("Snapshot has data: ${snapshot.data}");
return Home();
} else if (profile == 'false') {
print("Snapshot has no data: ${snapshot.data}");
return HomeUser();
} else if (user == null) {
return TestLogin();
} else {
//return CircularProgressIndicator();
return SpinKitFadingCube(
color: Colors.black,
size: 25.0,
);
}
//return null;
},
);
}
_checkUser(User user) async {
...
} //_checkUser()
}
User.dart
class User {
final String uid;
User({this.uid});
}
Just want to add that if I replace Wrapper() with Home() the program works, but that will bypass my "user login/register/forgot password" functionality.
Any help or guidance will be much appreciated.
you are using the User Provider and didn't create the user provider yet
add the User Provider in the MultiProvider in main
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (ctx) => Shippments(),
),
ChangeNotifierProvider(
create: (ctx) => Orders(),
),
ChangeNotifierProvider(
create: (ctx) => Products(),
),
ChangeNotifierProvider( // add this
create: (ctx) => User(),
),
],
child: StreamProvider<User>.value(

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 main.dart Navigator context does not include a Navigator

I've looked over several other posts with this same error about the Navigator and either their code looks different, it fails in totally different places, or other reasons and I must be missing something important. Where this fails for me is only from resuming from background or sleep. The app lifecycle detects "resume" and I want to navigate to the login page for the user to select a profile or login. The error below shows any way I try to use a Navigator in that function didChangeAppLifecycleState(AppLifecycleState state). Actually if I use Navigator anywhere in main.dart it gives the error. Outside of main.dart Navigator works great.
Navigator operation requested with a context that does not include a Navigator.
The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.
The code that causes the error in main.dart :
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
print("State changed! ${state}");
setState(() {
_notification = state;
});
if(state == AppLifecycleState.resumed){
NavService().navigateTo(context, '/login');
}
}
The main.dart build looks like this:
#override
Widget build(BuildContext context) {
return
MaterialApp(
theme: new ThemeData(
primarySwatch: themeSwatchColor,
brightness: Brightness.light,
primaryColor: themePrimaryColor,
accentColor: themeAccentColor,
),
initialRoute: '/',
navigatorObservers: <NavigatorObserver>[
NavService(), // this will listen all changes
],
onGenerateRoute: (routeSettings) {
switch (routeSettings.name) {
case '/':
return MaterialPageRoute(builder: (_) => LoginPage());
case '/login':
return MaterialPageRoute(builder: (_) => LoginPage());
case '/home':
return MaterialPageRoute(builder: (_) => HomePage());
case '/items':
return MaterialPageRoute(builder: (_) => ItemLookupPage());
case '/settings':
return MaterialPageRoute(builder: (_) => SettingsPage());
case '/oldsettings':
return MaterialPageRoute(builder: (_) => SecondPage());
case '/pickorders':
return MaterialPageRoute(builder: (_) => ReceivedOrdersPage());
case '/orders':
return MaterialPageRoute(builder: (_) => OrdersPage());
case '/receiving':
return MaterialPageRoute(builder: (_) => ReceivingPage());
case '/inventory':
return MaterialPageRoute(builder: (_) => InventoryPage());
default:
return MaterialPageRoute(builder: (_) => LoginPage());
}
},
home: (noAccount == true)
? LoginPage()
: HomePage(),
);
}
NavService.dart:
class NavService extends RouteObserver {
void saveLastRoute(String lastRoute) async {
if(lastRoute != "/login" && lastRoute != "/error"){
final SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('last_route', lastRoute);
}
}
Future<dynamic> navigateTo(BuildContext context, String routeName, {Map data}) async {
saveLastRoute(routeName);
return Navigator.pushNamed(context, routeName, arguments: data);
}
}
I also tried skipping my NavService and used Navigator directly, but the same error shows.
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => LoginPage(),
),
);
I tried using a GlobalKey as other posts have suggested, but the NavService() using the RouteObserver breaks when I do that.
The NavService and page routing works very well anywhere in the app. Its only while navigating in main.dart I'm having the issue. I just noticed if I place the above Navigator.of().push in initState() I get the same error. Maybe my MaterialApp is setup wrong? Or am I using the NavService incorrectly?
Thanks for any help!
The didChangeAppLifecycleState method does not provide any context unlike the build method. You would have to navigate without using context by setting a global key for your navigation:
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
Pass it to MaterialApp:
MaterialApp(
title: 'MyApp',
onGenerateRoute: generateRoute,
navigatorKey: navigatorKey,
);
Push routes:
navigatorKey.currentState.pushNamed('/someRoute');
Credits to this answer