Based on your comment
... generate state from outside build or State and then access it if I can only route that way. Ideally, I can route from within main().....
Yes it's possible. That's with reference to routing without BuildContext. The following is the mechanism behind Navigation in Stacked Architecture.
In summary, you provide MaterialApp with your own navigatorKey and use that key to navigate anywhere in the Flutter code. A clean way to do this is to use dependency injection. Have a service provided by GetIt package that will make the navigatorKey accessible to the entire Flutter app.
In details, we can use the following 4 steps:
1. NavigationService and key
Have a navigation.service.dart
file with a navigatorKey. This key will have the type and value of GlobalKey<NavigatorState>
import 'package:flutter/material.dart';
class NavigationService {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
}
This NavigationService class can do other things but let's leave it like this for now.
2. GetIt package
The package provides some mechanism to make services available app-wide. You actually initialise it before the runApp()
call just as you do with Firebase for example.
Install the package. Run the following command.
flutter pub add get_it
Then, have a file, let's say app.locator.dart
with the following.
import 'package:get_it/get_it.dart';
import 'navigation.service.dart';
final locator = GetIt.instance;
void setupLocator {
locator.registerLazySingleton(() => NavigationService());
}
Then in main.dart
, call setupLocator()
before runApp()
. Based on the main
method you provided:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FFAppState(); // Initialize FFAppState
GetSocial.addOnInitializedListener(() => {
// GetSocial SDK is ready to use
});
setupLocator(); //add this line
runApp(MyApp());
registerListeners();
}
3. Key and MaterialApp
Attach the navigatorKey from the NavigationService (obtained from locator) to the navigatorKey on the topmost MaterialApp.
So in whatever file where you defined MaterialApp
, have something like this
// ...
return MaterialApp(
navigatorKey: locator<NavigationService>().navigatorKey,
// ...
);
4. Routing without BuildContext
In the registerListeners()
function (called from main and not having access to BuildContext), you navigate with the navigatorKey. You can also use the context attached to the navigatorKey to showAlert too. Change your registerListeners()
function to the following
void registerListeners() {
Invites.setOnReferralDataReceivedListener((received) {
globalReferralData = received;
print(globalReferralData);
print(globalReferralData.linkParams);
print(globalReferralData.linkParams['referralID']);
// instead of navigating with Navigator,
// Navigator.pushNamed(context, '/landingPage');
// navigate with the navigatorKey
locator<NavigationService>().navigatorKey.currentState!.pushNamed('/landingPage');
// You can as well use the key's context to showAlert.
// so instead of the following
// showAlert(buildContextList.last, 'Referral Data Received', '$received');
// You can have
showAlert(
locator<NavigationService>().navigatorKey.currentContext!,
'Referral Data Received',
'$received',
);
});
}
This way you have access to routing without the BuildContext and can use it in any other Dart file in the same project.
And hence also you've ideally routed from within main.
The above doesn't invalidate any routing with Navigator.of(context).navigate...
in other parts of the app as it'll still be the same NavigationState
that is been used by Flutter.
Update
I've updated the snippet of step 4 above.
From your comment
I'm unclear how from within main() which is where I put the listener class to capture the globalReferralData how I can pass that based on the example above to then route....
If you carry step 4 well above (and all the other steps) the problem should be solved. It should be solved because your main method calls this registerListeners() function, so that should do the trick.
Do you now understand?
It's also good that you called registerListeners after runApp, that way you're sure the navigatorKey must have been attached to MaterialApp.
...
That should solve one part of the problem, that's navigation. The above is just minimalist setup to serve the purpose you want. You can go further to use the entire StackedServices and or StackedApp if you wish.
The other issue about setState not working in main, it's really not possible. It's just natural. main is an entry point for compiled code and not a StatefulWidget. I'm supposing that if the above routing works for you, you might not have the issue of setState again.
But if you still need something, I propose you notifyListeners. Other state management architectures, not only Stacked, use notifyListeners (a method from ChangeNotifier) to update UI from outside StatefulWidget.
I guess you would have your way around this. And you could still go ahead with the View and ViewModel classes of Stacked and call notifyListeners in a viewModel (maybe that of a splash screen) when the globalReferredData is received.