I am using Firebase cloud messaging for notifications, and i want to show a dialog or snackbar once i receive a notification when i am inside the application, my problem is that i am initializing the firebase configuration at the top of my widget tree (Splash screen once the app is starting)
_fireBaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
dynamic data = message['data'];
................ // Adding a snackbar/alertdialog here doesn't work
},
);
obviously if i set a dialog or snackbar it won't show since i need the context of my current page, is there any way to get the current context?
I also tried putting it inside the build widget of my splash screen but still the dialog isn't showing once i am on another page.
#override
Widget build(BuildContext context) {
_fireBaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
dynamic data = message['data'];
if (data['id'] == '1') {
newPro = true;
} else if (data['id'] == '2') {
print("THIS WORKS!!!");
showDialog(
context: context,
builder: (context) => AlertDialog(
content: ListTile(
title: Text("TEST"),
subtitle: Text("TEST"),
),
actions: <Widget>[
FlatButton(
child: Text("OK"),
onPressed: () => Navigator.pop(context),
)
],
));
}
},
);
I had the exact same issue, but I found a brilliant thread on GitHub. Basically, you can create a navigatorKey and pass that in to MaterialApp, and then use that navigatorKey to change route.
See how in this thread: https://github.com/brianegan/flutter_redux/issues/5#issuecomment-361215074
I ended up using Overlay support:
https://pub.dev/packages/overlay_support
It is basically called at the very beginning of my tree just like wrapping providers at the main.dart, it worked like a charm, nothing else worked at all!
Also here is a tutorial that helped me a lot:
https://medium.com/flutter-community/in-app-notifications-in-flutter-9c1e92ea10b3
Because it makes me uncomfortable to have the answer embedded in a link, here is the answer (credit to xqwzts on Github).
Use a GlobalKey you can access from anywhere to navigate:
Create the key:
final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
Pass it to your App:
new MaterialApp(
title: 'MyApp',
onGenerateRoute: generateRoute,
navigatorKey: navigatorKey,
);
Push routes:
navigatorKey.currentState.pushNamed('/someRoute');
An elegant solution to this problem is to use GlobalKey. That'll let you find the current BuildContext and do things with it.
You make a file called eg. global.dart looking like this:
import 'package:flutter/material.dart';
class GlobalVariable {
static final GlobalKey<NavigatorState> navState = GlobalKey<NavigatorState>();
}
You use this in your main() and MaterialApp() like this:
import 'global.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'fcm.dart'; // My Firebase Cloud Messaging code
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'screens/welcome_screen.dart';
void main() {
print('Running main()');
WidgetsFlutterBinding.ensureInitialized();
Firebase.initializeApp();
initializeFcm('', GlobalVariable.navState); // Sending the global key when initializing Firebase Cloud Messaging
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: WelcomeScreen(),
navigatorKey: GlobalVariable.navState, // Putting the global key in the MaterialApp
);
}
}
Then, in the file that handles Firebase Cloud Messaging, which I've named fcm.dart, you'll be able to use the GlobalKey to find the current context and use it, for example like this:
import 'package:blackbox/global.dart';
import 'online_screens/game_hub_screen.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
void initializeFcm(String token, GlobalKey myGlobalKey) async {
print('Initializing Firebase Cloud Messaging...');
await Firebase.initializeApp();
FirebaseMessaging.onMessageOpenedApp.listen((remoteMsg) {
// Using the currentContext found with GlobalKey:
Navigator.push(GlobalVariable.navState.currentContext, MaterialPageRoute(builder: (context) {
return GameHubScreen();
}));
});
}
do the initializing inside a build method of your first widget in the tree ! which normally it called an App widget and it is StateLess StateFull widget and inside the build method you have access to the BuildContext
Related
So most of the tutorials that go over Authorization just use Firebase's Auth, and most of the backend work is taken care of.
I need to create an OAuth Client in Dart/Flutter for Intuit's Quickbooks Online.
My basic understanding is when a user launches my Flutter Web Application, I pop up a screen to initiate the Authorization Code Grant - OAuth.
They sign into Intuit Quickbooks using this pop-up screen, then grant my application permission.
At this point my application should receive an Authorization Code.
I am guessing that I need to store this Authorization Code in my Google Cloud Firestore?
I need to send this Authorization Code back to Intuit & receive 2 things: An Access Token & a Refresh Token.
I think I should also store these in the Cloud Firestore?
But I don't see where cloud functions fit into this picture. Do I use the cloud functions to write/read to the Cloud Firestore?
How do I handle user sessions? I need to address State management as well.
I am starting to understand why many people just use built-in, out-of-the-box functionality of Firebase Auth, because developing a custom OAuth Client in Dart/Flutter is a huge undertaking.
I'm starting to feel confused & lost. I need some suggestions, or organizing because I'm losing sight of what needs to revised, designed or developed.
Main.dart
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:html' as html;
import 'dart:convert';w
import 'package:cloud_firestore/cloud_firestore.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(App());
}
class App extends StatefulWidget {
// Create the initialization Future outside of `build`:
#override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
final Future<FirebaseApp> _initialization = Firebase.initializeApp();
#override
Widget build(BuildContext context) {
return FutureBuilder(
/// Initialize FlutterFire:
future: _initialization,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
/// Once complete, show your application
if (snapshot.connectionState == ConnectionState.done) {
return MyApp();
}
/// Otherwise, show something whilst waiting for initialization to complete
return CircularProgressIndicator();
},
);
}
}
/// Client id provided by Intuit, our production app ClientID
const String clientId = "ABS0R9arxiHjNcAb0rP7OMs8aS1FRiMIINxOkhQimUPewGmQ2H";
const String clientSecret = "";
class MyApp extends StatelessWidget {
/// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Title',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: cPrimaryColor,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late String _token;
late html.WindowBase _popupWin;
Future<String> _validateToken() async {
final response = await http.get(
Uri.parse('https://appcenter.intuit.com/connect/oauth2'),
headers: {'Authorization': 'OAuth $_token'},
);
return (jsonDecode(response.body) as Map<String, dynamic>)['login']
.toString();
}
void _login(String data) {
/// Parse data to extract the token.
final receivedUri = Uri.parse(data);
/// Close the popup window
if (_popupWin != null) {
_popupWin.close();
_popupWin == null; // changed = to ==
}
setState(() => _token = receivedUri.fragment
.split('&')
.firstWhere((e) => e.startsWith('access_token='))
.substring('access_token='.length));
}
#override
void initState() {
super.initState();
/// Listen to message send with `postMessage`.
html.window.onMessage.listen((event) {
/// The event contains the token which means the user is connected.
if (event.data.toString().contains('access_token=')) {
_login(event.data);
}
});
/// You are not connected so open the Intuit authentication page.
WidgetsBinding.instance!.addPostFrameCallback((_) {
final currentUri = Uri.base;
final redirectUri = Uri(
host: currentUri.host,
scheme: currentUri.scheme,
port: currentUri.port,
path: '/static.html',
);
final authUrl = //TODO add state=security_token
'https://appcenter.intuit.com/connect/oauth2?client_id=ABS0R9arxiHjNcAb0rP7OMs8aS1FRiMIINxOkhQimUPewGmQ2H&response_type=code&scope=com.intuit.quickbooks.accounting&redirect_uri=https://google.com/&state=security_token%3D138r5719ru3e1%26url%3Dhttps://qb-payment-app.web.app/';
_popupWin = html.window.open(
authUrl, "Intuit QuickBooks Online Auth", "width=800, height=900, scrollbars=yes");
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('My App Bar'),
)
);
}
}
You asked a lot of questions here. I will address one of the statements:
I am starting to understand why many people just use built-in, out-of-the-box functionality of Firebase Auth, because developing a custom OAuth Client in Dart/Flutter is a huge undertaking.
Actually it's pretty easy to implement custom OAuth client, please see this question where I've implemented my own google sign in service: Flutter web google_sign_in: How to retrieve refreshToken
You can customise this service to work with Quickbooks instead of Google SignIn.
While Firebase.initializeApp() only needs to be called once, are there negative consequences for calling it twice?
Background: I'm troubleshooting a [core/no-app] No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp() error and temporarily fixed it by adding await Firebase.initializeApp(); in void main() async in addition to my pre-existing final Future<FirebaseApp> _fbApp = Firebase.initializeApp();
Everything seems to work okay now. I intend to fix it, but if calling Firebase.initializeApp() twice isn't hurting anything, I can stick with my immediate priorities and move forward.
Here's the relevant block of code:
void main() async {
WidgetsFlutterBinding
.ensureInitialized(); // added per https://stackoverflow.com/questions/57689492/flutter-unhandled-exception-servicesbinding-defaultbinarymessenger-was-accesse
await Firebase
.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final Future<FirebaseApp> _fbApp = Firebase
.initializeApp(); // changed from "async { await Firebase.initializeApp();" per official "Getting started with Firebase on Flutter - Firecasts" video at https://youtu.be/EXp0gq9kGxI?t=920
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return StreamProvider<Userrr>.value(
value: AuthService().user,
// above specifies what stream we want to listen to and what data we expect to get back
child: MaterialApp(
Thanks!
UPDATE: I tried all the good advice below and nothing seemed to work. I think my code, an exact duplication from two tutorials (one for getting started with Firebase and another for Firebase auth 101) had one or more gaps because of package updates or other incompatibilities.
I went back to basics and wrote-out by hand and re-implemented every step for installing and setting-up Firebase Core from the official "FlutterFire Overview."
I re-read all the documentation, as suggested below.
I updated all packages, including firebase_auth: “^0.20.0" to firebase_auth: “^0.20.0+1" (the +1 change is to “FIX: package compatibility,” per the changelog).
And then finally, I created a backup of main.dart as old_main.dart, then copy-pasted the exact "Initializing FlutterFire" FurtureBuilder code, then carefully replaced each part of that generic code with my own. Here is each item I replaced:
// replaced "_initialization" with "_fbApp"
// replaced if (snapshot.hasError) ... "return SomethingWentWrong();" with the response from below
// replaced "return Loading();" with CircularProgressIndicator form below
// changed return MyAwesomeApp(); to return MyApp();
// changed "class App extends StatelessWidget" to "class MyApp extends StatelessWidget
// replaced "MyAwesomeApp();" from "if (snapshot.connectionState == ConnectionState.done) { return MyAwesomeApp();" with all the "StreamProvider<Userrr>.value(..." code EXCEPT changed home to "home: Wrapper(),"
It may seem elementary, but for a novice like myself, it was the only way forward. Thankfully it worked!
Here's the full working code excerpt:
void main() {
WidgetsFlutterBinding
.ensureInitialized(); // added by mgav, per https://stackoverflow.com/questions/57689492/flutter-unhandled-exception-servicesbinding-defaultbinarymessenger-was-accesse
// await Firebase.initializeApp(); // added by mgav to test, KNOWING the Firebase is already initialized as a future below in line 25. Was trying to test (temp?) fix for error: “[core/no-app] No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp() The relevant error-causing widget was: MyApp file:///Users/mgav/AndroidStudioProjects/brand_new_flutter_app/lib/main.dart:21:10”
runApp(MyApp());
}
// BEGIN Firebase FutureBuilder code pasted from docs at https://firebase.flutter.dev/docs/overview/#initializing-flutterfire (replaces next section of commented-out code)
class MyApp extends StatelessWidget {
// Create the initialization Future outside of `build`:
final Future<FirebaseApp> _fbApp = Firebase.initializeApp();
#override
Widget build(BuildContext context) {
return FutureBuilder(
// Initialize FlutterFire:
future: _fbApp,
builder: (context, snapshot) {
// Check for errors
if (snapshot.hasError) {
print('You have an error! ${snapshot.error.toString()}');
return Text('Something went wrong main.dart around line 48');
}
// Once complete, show your application
if (snapshot.connectionState == ConnectionState.done) {
return StreamProvider<Userrr>.value(
value: AuthService().user,
// above specifies what stream we want to listen to and what data we expect to get back
child: MaterialApp(
title: 'Real Revs and Q&A',
theme: ThemeData(
primarySwatch: Colors.blueGrey,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
routes: {
// '/welcome': (context) => WelcomeScreen(),
'/cleanwritereview': (context) => CleanWriteReviewScreen(),
'/registrationscreen': (context) => RegistrationScreen(),
'/loginscreen': (context) => LoginScreen(),
'/viewreviewsscreen': (context) => ViewReviewsScreen(),
'/homescreen': (context) => Home(),
},
home: Wrapper(),
),
);
}
// Otherwise, show something whilst waiting for initialization to complete
return Center(
child: CircularProgressIndicator(),
);
},
);
}
}
You'll get an error message if you call initializeApp() twice for the same FirebaseApp.
In your case, you can get the app that you've already created with:
final FirebaseApp _fbApp = Firebase.app();
Also see the documentation on FlutterFire, specifically initializing and using FirebaseApp.
To initialise firebase you either do:
main(){
await Firebase.initializeApp(); // don't run the app until firebase is initialized
runApp(MyApp());
}
Or use a FutureBuilder which ensure the future is resolved before running the code inside the builder function.
#override
Widget build(BuildContext context) {
final _fbApp = Firebase.initializeApp();
return FutureBuilder(
future: _fbApp,
builder: (context, snapshot) { // waits until _fbApp is resolved to execute.
....
});
}
You get an error because you don't await _fbApp future.
In your code there is no guarantee AuthService().user is executed after initializeApp has finished. To garantee this you have to wait until initializeApp() is resolved by using await, then or a FutureBuilder.
Add a try catch to understand why the first call in initializeApp is not working.
Firebase initialises its core services only once. there is exactly one FirebaseApp instance by name. When you don't specify the name '[DEFAULT]' is used.
Try doing this:
final app = await Firebase.initializeApp();
final app2 = await Firebase.initializeApp();
print(app == app2); // is true
Please provide more details on your setup:
firebase_auth, firebase_core versions,
Execution plateform (Android, ios or web).
In the last version of fire_auth we use:
FirebaseAuth.instance.authStateChanges // stream to subscribe to the user's current authentication state.
I am building a flutter app. When trying to add Firebase database it brings this error and displays a blank white screen
[ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: type 'FirebaseFirestore' is not a subtype of type 'Firestore'
below is my code for main.dart:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:sokoni/src/providers/auth.dart';
import 'package:sokoni/src/screens/Login.dart';
import 'package:sokoni/src/screens/home.dart';
import 'package:sokoni/src/widgets/loading.dart';
import 'package:firebase_core/firebase_core.dart';
void main() async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
//we call our Multiproviders so that our App can recognize the providers we've used.
runApp(
MultiProvider(
providers:[
ChangeNotifierProvider.value(
value: AuthProvider.initialize()
)
],
child:MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Sokoni',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: ScreensController(),
)
));
}
class ScreensController extends StatelessWidget{
#override
Widget build( BuildContext context) {
final user = Provider.of<AuthProvider>(context);
switch(user.status){
case Status.Uninitialized:
return Loading();
case Status.Unauthenticated:
case Status.Authenticating:
return LoginScreen();
case Status.Authenticated:
return HomePage();
default: return LoginScreen();
}
}
}
I encountered similar issue, solved it, so answering in case anyone gets benefited.
The error:
'FirebaseFirestore' is not a subtype of type 'Firestore' means somewhere you have two different name reference to be used as same.
Checkout all the FirebaseFirestore instance creation code or type, do the same with Firestore, replace all with the same or with latest one, which is FirebaseFirestore, and Firestore is deprecated in favour of FirebaseFirestore as of end of 2020.
Please check packages. I cant seen any code about the exception but i think, There is a conflict between different firebase packages.
Maybe you can add You can also add the relevant codes
I am trying to implement Firebase Dynamic links in a flutter. But when I click on the link it calls the functions but does not take me to the specified page.
Code Implementation
main.dart
Main Entry Final for Application
void main() {
Crashlytics.instance.enableInDevMode = true;
FlutterError.onError = Crashlytics.instance.recordFlutterError;
runZoned(() {
runApp(MyApp());
}, onError: Crashlytics.instance.recordError);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
DynamicLinks dynamicLinks = new DynamicLinks();
dynamicLinks.initDynamicLinks(context);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
return LayoutBuilder(builder: (context, constraints) {
return OrientationBuilder(builder: (context, orientation) {
SizeConfig().init(constraints, orientation);
return MaterialApp(
title: 'APP NAME',
theme: ThemeData(
primarySwatch: Colors.orange,
brightness: Brightness.light,
),
debugShowCheckedModeBanner: false,
home:SplashScreenMain(),
);
});
});
}
}
dynamicLinkManager.dart
Another class to handle Dynamic Links.
class DynamicLinks {
void initDynamicLinks(BuildContext context) async{
var data = await FirebaseDynamicLinks.instance.getInitialLink();
FirebaseDynamicLinks.instance.onLink(onSuccess: (dynamicLink) async {
print("Main = ${dynamicLink}");
var deepLink = dynamicLink?.link;
final queryParams = deepLink.queryParameters;
debugPrint('DynamicLinks onLink $deepLink');
print("queryParams $queryParams");
if(DynamicLinksConst.inviteUser == deepLink.path){
print("Step 1.......Code Works");
/* THIS PART CODE IS NOT WORKING */
Login.setActiveContext(context);
Navigator.push(context,
EaseInOutSinePageRoute(
widget: SignupPage()), //MaterialPageRoute
);
}else{
Navigator.push(context,
EaseInOutSinePageRoute(
widget: LoginPage()), //MaterialPageRoute
);
}
}, onError: (e) async {
debugPrint('DynamicLinks onError $e');
});
}
}
Console Output
Here is the output you can see that its returning data captured by dynamic link.
I Don't Think it a problem with firebase dynamic link it feels like more of a Navigator problem but I am unable to identify the problem here as this Navigator is working properly throughout the project expect here.
EaseInOutSinePageRoute just adds animation to navigations.
I/flutter ( 395): Main = Instance of 'PendingDynamicLinkData'
I/flutter ( 395): DynamicLinks onLink https://example.com/abc?para1=dataOne
I/flutter ( 395): queryParams {para1: dataOne}
I/flutter ( 395): Step 1.......Code Works
As mentioned in my comment, the issue here is that the expected BuildContext isn't used in Navigator.push().
Without a minimal repro, it's difficult to provide a concrete solution. Since you've mentioned that you're using an Authenticator class that pushes a new page/screen, it might be safer to manage the screen of the app in a single class. With this, it's easier to manage the BuildContext being used in Navigator.push(). You may check this sample in this blog post and see if it fits your use case.
I am developing Flutter with Redux.
When a user starts an application, I want Redux to automatically dispatch an action. This action will make the Navigator push different routes dependently.
This snippet provided by a Flutter dev member uses the GlobalKey to use the Navigator inside the middleware.
Following this, I organize my code as follows:
main.dart
void main() {
final store = new Store(appStateReducer,
middleware:createRouteMiddleware()
);
runApp(new MyApp(store));
}
class MyApp extends StatelessWidget {
final Store<AppState> store;
MyApp(this.store);
#override
Widget build(BuildContext context) {
return new StoreProvider<AppState>(
store: store,
child: MaterialApp(
routes: {
Routes.REGISTER: (context) {
return RegisterScreenContainer();
},
Routes.SET_PROFILE: (context) {
return SetProfileScreenContainer();
},
//Routes.HOME = "/" so this route will be run first
Routes.HOME: (context) {
return StoreBuilder<AppState>(
//onInit is called to dispatch an action automatically when the application starts. The middleware will catch this and navigate to the appropriate route.
onInit: (store) => store.dispatch(
ChangeRoute(routeStateType: RouteStateType.Register)),
builder: (BuildContext context, Store vm) {
return RegisterScreenContainer();
},
);
},
}));
}
}
middleware.dart
Middleware<AppState> createRouteMiddleware(
{#required GlobalKey<NavigatorState> navigatorKey}) {
final changeRoute = _createChangeRouteMiddleware(navigatorKey: navigatorKey);
return TypedMiddleware<AppState, ChangeRoute>(changeRoute);
}
Middleware<AppState> _createChangeRouteMiddleware(
{#required GlobalKey<NavigatorState> navigatorKey}) {
print(navigatorKey.currentState);
return (Store store, action, NextDispatcher next) async {
switch ((action.routeStateType as RouteStateType)) {
case RouteStateType.Home:
navigatorKey.currentState.pushNamed(Routes.HOME);
break;
case RouteStateType.Register:
//The middleware intercepts and push the appropriate route depending on the action
navigatorKey.currentState.pushNamed(Routes.REGISTER);
break;
default:
break;
}
next(action);
};
}
And this is the error I got
[ERROR:topaz/lib/tonic/logging/dart_error.cc(16)] Unhandled exception:
E/flutter ( 2544): NoSuchMethodError: The method 'pushNamed' was called on null.
E/flutter ( 2544): Receiver: null
E/flutter ( 2544): Tried calling: pushNamed("/register")
This means that the action was successfully dispatched, however, the currentState of the navigatorKey was null.
What am I missing here?
Note that I am aware of this seemingly similar question which does not really apply to my question. Even when I merge the main.dart and middleware.dart into one file, it still doesn't work.
I solved this issue by having the global navigator key in a separate file. Then I used that in my materialApp and in the middleware.
I put the navigator key in my keys.dart file:
import 'package:flutter/widgets.dart';
class NoomiKeys {
static final navKey = new GlobalKey<NavigatorState>();
}
Added the key to MaterialApp widget "navigatorKey: NoomiKeys.navKey," in my main.dart file (alot of code is removed to make it faster to read):
import 'package:noomi_nursing_home_app/keys.dart';
#override
Widget build(BuildContext context) {
return StoreProvider<AppState>(
store: store,
child: MaterialApp(
//Add the key to your materialApp widget
navigatorKey: NoomiKeys.navKey,
localizationsDelegates: [NoomiLocalizationsDelegate()],
onGenerateRoute: (RouteSettings settings) {
switch (settings.name) {
And use it in navigate_middleware.dart;
import 'package:noomi_nursing_home_app/actions/actions.dart';
import 'package:noomi_nursing_home_app/keys.dart';
import 'package:redux/redux.dart';
import 'package:noomi_nursing_home_app/models/models.dart';
List<Middleware<AppState>> navigateMiddleware() {
final navigatorKey = NoomiKeys.navKey;
final navigatePushNamed = _navigatePushNamed(navigatorKey);
return ([
TypedMiddleware<AppState, NavigatePushNamedAction>(navigatePushNamed),
]);
}
Middleware<AppState> _navigatePushNamed(navigatorKey) {
return (Store<AppState> store, action, NextDispatcher next) {
next(action);
navigatorKey.currentState.pushNamed(action.to);
};
}
looks like you forgot to declare & pass navigatorKey to middleware
final navigatorKey = GlobalKey<NavigatorState>();
void main() {
final store = Store(appStateReducer,
middleware:createRouteMiddleware(navigatorKey: navigatorKey)
);
runApp(MyApp(store));
}
and your MaterialApp is missing navigatorKey too
MaterialApp(
navigatorKey: navigatorKey
routes: /* your routes */
)