How to avoid using GlobalKey when using MaterialApp builder method - flutter

I've setup my project with a Scaffold wrapper so all the routes have the AppBar and SideDrawer widgets.
I'm quite annoyed having to pass around my _navigatorKey all the time and running into this error: "Navigator operation requested with a context that does not include a Navigator".
Also when trying to do very specific things like open a dialog I have to pass navigatorKey.currentContext which throws this error: "The argument type 'BuildContext?' can't be assigned to the parameter type 'BuildContext'".
Also when using any widget requiring an overlay like TextFormField or DropdownButton I'm running into this error: "No Overlay widget found.".
I've been able to fix all these issues with roundabout methods that I don't like, but more and more issues keep coming up because of this GlobalKey usage.
Is there a better way to wrap my routes and avoid using GlobalKey?
import 'package:flutter/material.dart';
import 'package:get_it_mixin/get_it_mixin.dart';
import 'package:provider/provider.dart';
import 'pages/blog_page.dart';
import 'pages/landing_page/landing_page.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget with GetItMixin {
MyApp({Key? key}) : super(key: key);
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<MenuProvider>(
create: (_) => MenuProvider(),
),
ChangeNotifierProvider<MarkdownProvider>(
create: (_) => MarkdownProvider(),
),
],
builder: (context, child) {
return MaterialApp(
title: "test",
navigatorKey: _navigatorKey,
debugShowCheckedModeBanner: false,
builder: (context, child) {
return Overlay(
initialEntries: [
OverlayEntry(
builder: (context) =>
MyScaffold(child: child!, navigatorKey: _navigatorKey),
),
],
);
},
home: const LandingPage(),
routes: {
LandingPage.routeName: (context) => const LandingPage(),
BlogPage.routeName: (context) => const BlogPage(),
MarkdownPage.routeName: (context) => const MarkdownPage(),
},
);
},
);
}
}

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 error A GlobalKey was used multiple times inside one widget's child list

I'm using Fluro package to handle my project navigations however after reload one of my pages I get this error :
Another exception was thrown: A GlobalKey was used multiple times inside one widget's child list.
and here is my Material App code snippet:
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
_MyAppState() {
final router = FluroRouter.appRouter;
AppRouter.setupRouter(router);
AppRouter.router = router;
}
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (ctx) => Auth(),
),
ListenableProxyProvider<Auth, Orders>(
update: (_, authObj, prevOrders) =>
Orders(authObj.usrName, authObj.objId)
],
child: MaterialApp(
initialRoute: root,
onGenerateRoute: AppRouter.router.generator,
debugShowCheckedModeBanner: false,
home: authData.isAuth
? TabsScreen()
: FutureBuilder(
initialData: authData.tryLogin(),
future: authData.tryLogin(),
builder: (_, snapshot) => snapshot.connectionState ==
ConnectionState.waiting
? SplashScreen()
: AuthScreen()),
);

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(

Flutter Error : Could not find the correct Provider<StateStreamable<Object?>> above this BlocBuilder<StateStreamable<Object?>

I'm trying to implements a Cubit that is used to make the navigation bewteen the pages.
My first try was to implement it on my background screens, but I got this error when trying to do it :
"Error: Could not find the correct Provider<StateStreamable<Object?>> above this BlocBuilder<StateStreamable<Object?>, Object?> Widget"
I don't understand why it doesn't find the correct context because my BlocBuilder is above the BlocProvider in the widget tree ...
Here's the code :
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flexmes_mobile_app/buisness_logic/cubit/internet_cubit.dart';
import 'package:flexmes_mobile_app/buisness_logic/utility/app_bloc_observer.dart';
import 'package:flexmes_mobile_app/config/themes.dart';
import 'package:flexmes_mobile_app/ui/screens/auth_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:responsive_sizer/responsive_sizer.dart';
import 'buisness_logic/cubit/navigation_cubit.dart';
import 'config/app_router.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
Firebase.initializeApp();
BlocOverrides.runZoned(
() => runApp(MyApp()),
blocObserver: AppBlocObserver(),
);
}
class MyApp extends StatelessWidget {
final AppRouter _appRouter = AppRouter();
MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MediaQuery(
data: const MediaQueryData(),
child: ResponsiveSizer(
builder: (context, orientation, deviceType) {
return MultiBlocProvider(
providers: [
BlocProvider<InternetCubit>(
create: (context) => InternetCubit(connectivity: Connectivity()),
),
BlocProvider<NavigationCubit>(
create: (context) => NavigationCubit(),
),
],
child: MaterialApp(
title: 'Flexmes Mobile App',
//Generate routes for navigation
onGenerateRoute: _appRouter.generateRoute,
//Take the correct theme to apply to the screens
theme: appThemeData[AppTheme.authTheme],
//Build the default widget, the "background" widget
builder: (context, child) {
//Instantiate the Cubits
BlocProvider.of<InternetCubit>(context);
BlocProvider.of<NavigationCubit>(context);
return BlocBuilder(
builder: (BuildContext context, state) {
if (state is AuthenticationInitial){
return AuthScreen(child: child);
}
return Container();
},
);
},
),
);
}
),
);
}
}
Does anyone knows why ? :)
Thanks for your answers !
Chris
what worked for is writing the type of Cubit and State in the BlocBuilder too
so from that :
return BlocBuilder(
builder: (BuildContext context, state) {
if (state is AuthenticationInitial)
{
return AuthScreen(child: child);
}
return Container();
},
it will be :
return BlocBuilder<BlocCubit,BlocState>(
builder: (BuildContext context, state) {
if (state is AuthenticationInitial)
{
return AuthScreen(child: child);
}
return Container();
},
that is what works for me

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(),