How to use Flutter Firebase dynamic links with go_router - flutter

I had firebase dynamic links integrated and working fine in my app and later i changed to use go_router and now i don't know what i should do to get it working or how to handle it.
The way i imagined it would be is that the FB dynamic link path will be the same as the path to the page in GoRouter routes, and go_router will redirect to the page automatically but i don't think that is how it works and i can't find any resources for this.
So the question is how to use Firebase DynamicLinks with go_router?

this is how I did it, looks like working fine :)
For when the app is in terminated mode:
Future main() async {
...
// Closed state: getInitialLink is Used
final PendingDynamicLinkData? initialLink =
await FirebaseDynamicLinks.instance.getInitialLink();
...
then through runApp(MtApp(initialRoute: initialLink)) pass it to GoRouter.initialLocation field or GoRouter.redirect function, depending on how you use it.
For when the app is in background:
wrap your top widget with a statefulWidget your created like:
class AHDynamicLinksManager extends
StatefulWidget {
final Widget child;
const AHDynamicLinksManager({required this.child, Key? key})
: super(key: key);
#override
State<AHDynamicLinksManager> createState() => _AHDynamicLinksManagerState();
}
class _AHDynamicLinksManagerState extends State<AHDynamicLinksManager> {
#override
void initState() {
super.initState();
FirebaseDynamicLinks.instance.onLink.listen((dynamicLinkData) {
String goingTo = dynamicLinkData.link.path;
GoRouter.of(context).go(goingTo);
}).onError((error) {
GoRouter.of(context).go("/errorpage/$error");
});
}
#override
Widget build(BuildContext context) {
return widget.child;
}
}
Hope it helped!

Related

What should I do when I have to use "void function() async" as a last resort in Flutter?

My function is as follows:
void function() async {
…
}
I used the above function in Widget build(BuildContext context).
Actually, I want it to be Future<void> function() async instead of void function() async.
However, I can't use await in Widget build(BuildContext context), so I can't use Future<void> function() async because Future requires await.
I shouldn't use FutureBuilder or then because I don't want to get the data in the function, I just want to run the function.
Appreciate if someone can advise. Thank you in advance!
You can define your function as future & call it in initState of StatefulWidget (without await)
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future<void> myFutureFunction() async {
// some future work with
}
#override
void initState() {
super.initState();
// this will be called once, when this widget is apear to the screen
myFutureFunction();
}
#override
Widget build(BuildContext context) {
return SizedBox();
}
}
build method can run up to 60(maybe more with the newest phones)times per second, so it's not a good idea to run any heavy computation or API call in the build method. It's simply for building(widgets).
You may want to rethink your app architecture if you think you must.
Consider bloc pattern or just checkout other lifecycle methods for stateful widgets. This article has a list of them.
As for the question, async functions technically don't require you to await them. You can just call them without adding await in front of them.
If you have a warning, it might be coming from lint rules. Related rule.

Flutter run a function before the buildcontext with FutureBuilder

The problem I have is that I want to run a function before the buildcontext is rendered.
What the function does is read the storage to get the token. The function works with Async Await.
Why do I want to do this?
Because in the build context I have a FutureBuilder and in this I need token data.
class ProfileScreen extends StatefulWidget {
ProfileScreen({Key? key}) : super(key: key);
#override
State<ProfileScreen> createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State<ProfileScreen> {
dynamic token;
getToken()async{
token= await decodeJwt();
print("first this");
print(token);
return token;
}
#override
void initState() {
getToken();
super.initState();
}
#override
Widget build(BuildContext context) {
print("after this");
final profileService=Provider.of<ProfileService>(context);
final listDropDownService=Provider.of<ListDropDown>(context);
return FutureBuilder(
future: Future.wait([profileService.getSpecificWalker(token["id"]), listDropDownService.getDropDown("services")]),
builder: (context,dynamic snapshot){
as you can see i used some print also to see what the sequence was like.
But first it prints "then this" and then "first this"
My strongest suggestion would be to use some state management solution, such as flutter_bloc, that is not directly dependent on the UI components.
If not, then why don't you just try and wrap the getToken() method and the profileService.getSpecificWalker(token["id"]) call in a single async method and call that from the FutureBuilder.
What you said shows that you need state manager, because in one state you want to get token then when token get available, call the futureBuilder. I recommended bloC.

setState is not updating the UI even though state is updating

I am trying to wait till amplify configuration is done then load the login screen. Even though state seems to be getting updated I am still getting the loadinscreen. Why is that?
I am not sure if setState is proper method on the init : Importance of Calling SetState inside initState
As per the doc : https://docs.amplify.aws/start/getting-started/integrate/q/integration/flutter/#configure-amplify
Future<void> main() async {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool _isAmplifyConfigured = false;
late AmplifyAuthCognito auth;
#override
void initState() {
_initializeApp();
super.initState();
}
Future<void> _initializeApp() async {
await _configureAmplify();
setState(() {
_isAmplifyConfigured = true;
});
}
Future<void> _configureAmplify() async {
auth = AmplifyAuthCognito();
try {
await Amplify.addPlugin(auth);
await Amplify.configure(amplifyconfig);
} on AmplifyAlreadyConfiguredException {
print(
'Amplify was already configured. Looks like app restarted on android.');
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
onGenerateRoute: AppRoutes.onGenerateRoute,
initialRoute: _isAmplifyConfigured
? LoginScreen.routeName
: LoadingScreen.routeName,
);
}
}
I think the issue is with you trying to reassign your initialRoute. I'm not super familiar with this property, but given the name I assume this is set once and is not rebuilt, not even when the state changes. It would make sense, also, because the rest of your code sounds like it should work.
Before trying anything else, I'd recommend you move your logic to initialize Amplify to the LoginScreen, and having its body depend on the _isAmplifyConfigured boolean value. So show spinner if it's false, and show Login fields when it's true.
Even better would be to create a HomeScreen, so you can keep this Amplify initialization at the bottom of your app's stack. And then have your HomeScreen either show the Login widgets, the home screen of your app, or a loading state.

Flutter: How to show a loading page while another page is loading

I'm making a website and I would like to show a loading_page until the home_page is loaded and then transition from one to the other as soon as possible (no fixed timers).
There are multiple ways to do this (ie., using the simplest setState, Streams, multiple packages for state management, just to name a few). I'll give you a simple example just by using a StatefulWidget where you call your API on initState and then navigate when you're done to your new screen.
class LoadingPage extends StatefulWidget {
const LoadingPage({Key? key}) : super(key: key);
#override
_LoadingPageState createState() => _LoadingPageState();
}
class _LoadingPageState extends State<LoadingPage> {
#override
void initState() {
super.initState();
_fetchFromAPI();
}
Future<void> _fetchFromAPI() async {
// Call some API, do anything you want while loading
Navigator.pushReplacementNamed(context, '/home_page');
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: const CircularProgressIndicator(),
);
}
}
You can use future builder for this purpose.
Have a look at: https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
You can fetch the data as snapshot and use the snapshot.hasdata to check if data is being received or not and till then you can show CircularProgreswIndicator() to show the loading..

FutureBuilder keeps executing when navigating between pages using Bottom Navigation Bar

I have a bottom navigation bar from my MaterialApp and one of the pages use FutureBuilder to retrieve data from my RESTful API. My bottom navigation bar needs to save the state of the pages, So I came across this guide on how to keep the state of my bottom navigation bar using PageStorage.
The issue I have encountered is that whenever I navigate out of the FutureBuilder page and back again, it rebuilds the entire page and re-executes my Future method.
I also read another guide on using AsyncMemoizer to run my Future method only once (It still rebuilds the page, but much faster). The snippet of code below is how I have implemented it.
//Unsure why AsyncMemoizer somehow only works if I use StatelessWidget, and not StatefulWidget
class FuturePage extends StatelessWidget {
/*I had to comment this constructor out because AsyncMemoizer must be
initialised with a constant value */
//const FuturePage({Key key}) : super(key: key);
//To store my PageStorageKey into bucket
FuturePage({Key key}) : super(key: key);
final _memoizer = new AsyncMemoizer();
_fetchData() => this._memoizer.runOnce(_myFutureMethod);
Future<MyType> _myFutureMethod() async => print("Executed"); //await post and return data
#override
Widget build(BuildContext context) {
return Container(
child: FutureBuilder(
future: _fetchData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
//set up my widgets
}
return Center(child: CircularProgressIndicator());
}
);
}
}
On the output log, Executed is only displayed once. However, I need to use a StatefulWidget instead of StatelessWidget, and AsyncMemoizer wouldn't work in my case.
class FuturePage extends StatefulWidget {
FuturePageState createState() => FuturePageState();
const FuturePage({Key key}) : super(key: key);
}
How do I save the state of my FutureBuilder Page using StatefulWidget? I'm still relatively new to flutter and the concepts of reactive programming. Sincerest apologies if I happen to be doing something wrongly!
To achieve this behavior you can use the AutomaticKeepAliveClientMixin
class FuturePage extends StatefulWidget {
const FuturePage({Key key}) : super(key: key);
#override
FuturePageState createState() => FuturePageState();
}
You need to implement AutomaticKeepAliveClientMixin
class FuturePageState extends State<FuturePage> with AutomaticKeepAliveClientMixin<FuturePage>{
#override
bool get wantKeepAlive => true;
#override
Widget build(BuildContext context) {
super.build(context); // call super method
return // Your Widget
}
}