Opening the app when terminated with firebase dynamic link crashes the app - flutter

I have integrated firebase dynamic links with my app. It works fine with foreground and background states. However, the app crashes when I open it on terminated state. Here is my code to handle dynamic links
static Future<void> initDynamicLinks() async {
try {
// terminated state
final PendingDynamicLinkData initLink =
await FirebaseDynamicLinks.instance.getInitialLink();
if (initLink != null) {
print('init link $initLink');
final Uri deeplink = initLink?.link;
if (deeplink != null) {
print('routing');
// routing(deeplink, deeplink.queryParameters);
} else {
print('deep link is null');
}
} else {
print('init link is null');
}
} catch (e) {
print('error getting link $e');
}
// background and foreground states
FirebaseDynamicLinks.instance.onLink.listen((dynamicLinkData) async {
final Uri deeplink = dynamicLinkData.link;
final queryParams = deeplink.queryParameters;
if (deeplink != null) {
routing(deeplink, queryParams);
} else {
print('listen link is null');
}
}).onError((error) {
print('onLink error');
print(error.message);
});
}
The dynamic link gets printed and it is not null, but what I get is a blank screen, half of it black and the other half is red.
Note: routing is a function that navigates to a specific path.
Any idea what might be the issue?

Related

Null check operator used on a null value- futter chat

I'm creating a chat app with sending voice message. now I'm getting this error in my code. appreciate your help on this. I HAVE INSERTED FOLLOWING CODE ANS THEN ERROR APPEARS. Cant find a exact file error occurring.
Null check operator used on a null value
class SoundRecorder {
FlutterSoundRecorder? _audioRecorder;
bool _isRecorderInitialised = false;
bool get isRecording => _audioRecorder!.isRecording;
Future init() async {
_audioRecorder = FlutterSoundRecorder();
await _audioRecorder?.openAudioSession(); //start recording
//asking permisson
final status = await Permission.microphone.request();
if (status != PermissionStatus.granted) {
throw RecordingPermissionException("Microphone permission");
}
await _audioRecorder!.openAudioSession();
_isRecorderInitialised = true;
}
void dispose() {
if (!_isRecorderInitialised) return;
_audioRecorder!.closeAudioSession();
_audioRecorder = null;
_isRecorderInitialised = false;
}
Future _record() async {
if (!_isRecorderInitialised) return;
await _audioRecorder!.startRecorder(toFile: pathToSaveAudio);
}
Future _stop() async {
if (!_isRecorderInitialised) return;
await _audioRecorder!.stopRecorder();
}
Future toggleRecording() async {
if (_audioRecorder!.isStopped) {
await _record();
} else {
await _stop();
}
}
}
Looks like at some point you use the ! operator to assert that _audioRecorder isn't null but it actually is. From the stack, I think this would be from the isRecording getter.
A simple fix to this would be to make the getter bool get isRecording => _audioRecorder?.isRecording ?? false, since if _audioRecorder is null, then you can't be recording, right?

Flutter - IOS App Crashes everytime when click Firebase Dynamic Link but Android App works fine

I stuck in this problem for a long time, but I haven't found any information or solution about this situation...
In Android App, it works very good which can even catch the queryParameters. But In Ios App, everytime I click my dynamic link, it will crash and get the error message below.
I will be very appreciated if anyone can help me to figure out this problem, thank you!!
My Dynamic Link
https://choudao.page.link/?efr=0&ibi=com.example.XXX&apn=com.choudao.XXX&imv=1&amv=1&link=https%3A%2F%2Fchoudao.page.link.com%2F%3Faction%3Dclose
Error Message
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[0]'
My Flutter Code
void createDynamicLink(String action) async {
final DynamicLinkParameters parameters = DynamicLinkParameters(
uriPrefix: 'https://choudao.page.link',
link: Uri.parse('https://choudao.page.link.com/?action=$action'),
// link: Uri.parse('https://choudao.page.link.com'),
androidParameters: AndroidParameters(
packageName: 'com.choudao.XXX',
minimumVersion: 1,
),
iosParameters: IOSParameters(
bundleId: 'com.example.XXX',
minimumVersion: '1',
appStoreId: '962194608',
),
);
final Uri dynamicUrl = await dynamicLinks.buildLink(parameters);
final ShortDynamicLink shortDynamicLink = await dynamicLinks.buildShortLink(parameters);
final Uri shortUrl = shortDynamicLink.shortUrl;
print('Url:' + dynamicUrl.toString());
print('$action+_shortUrl:' + shortUrl.toString());
}
Future<void> retrieveDynamicLink() async {
try {
dynamicLinks.onLink.listen((dynamicLink) {
final Uri deepLink = dynamicLink.link;
print('deepLink:' + deepLink.toString());
if (deepLink != null &&
deepLink.queryParameters.containsKey('action')) {
if (deepLink.queryParameters['action'] == 'open') {
Future.delayed(Duration(seconds: 2)).then((value) {
_doorAction.sendOpenData(
context,
widget.userLoginFamilyCode,
widget.userHouseNumber,
userName,
scaffoldMessengerHomePageDoorKey);
print('onLink_Open');
});
} else {
Future.delayed(Duration(seconds: 2)).then((value) {
_doorAction.sendCloseData(
context,
widget.userLoginFamilyCode,
widget.userHouseNumber,
userName,
scaffoldMessengerHomePageDoorKey);
print('onLink_Close');
});
}
}
}).onError((error){print('onLinkError:'+error.toString());});
final PendingDynamicLinkData data =
await dynamicLinks.getInitialLink();
final Uri deepLink = data?.link;
print('DataLink:' + deepLink.toString());
if (deepLink != null && deepLink.queryParameters.containsKey('action')) {
if (deepLink.queryParameters['action'] == 'open') {
Future.delayed(Duration(seconds: 2)).then((value) {
_doorAction.sendOpenData(
context,
widget.userLoginFamilyCode,
widget.userHouseNumber,
userName,
scaffoldMessengerHomePageDoorKey);
print('Init_Open');
});
} else {
Future.delayed(Duration(seconds: 2)).then((value) {
_doorAction.sendCloseData(
context,
widget.userLoginFamilyCode,
widget.userHouseNumber,
userName,
scaffoldMessengerHomePageDoorKey);
print('Init_Close');
});
}
}
} catch (e) {
print(e.toString());
}
}

how to check if getLogin() function worked or not to display login was successful or not

I am not understanding how do i check if getLogin() function worked or not and if user exists or not.
where do i write the code to display the message. When I checked by assigning a variable to this statement
res=dbService.getLogin(_email, _password); then it shows datatype mismatch. Can u plzz help me with this. I am developing flutter app in android studio and using sqlite. Please do help me I am stuck from 2 days.
I call _submit() on login button
bool validateAndSave() {
final form = _formKey.currentState;
if (form.validate()) {
form.save();
return true;
}
return false;
}
void _submit(){
if (validateAndSave()) {
setState(() {
dbService.getLogin(_email, _password);
});
}
}
And this is db_service.dart code for getLogin()
Future<RegisterUser> getLogin(String user, String password) async {
await DB.init();
var res = await DB.rawQuery("userDetails WHERE emailId = '$user' and password = '$password'");
if (res.length > 0) {
return RegisterUser.fromMap(res.first);
}
return null;
}
You could wait for the result of the login method.
void _submit() async {
if (validateAndSave()) {
var user = await dbService.getLogin(_email, _password);
if (user != null) {
Navigator.of(context).pushNamed('/home');
} else {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("Sign in error")));
}
}
}
In this case I pushed to the home screen if successfull and showed a snackbar in case of error. You could adjust this to your intended use case.

Firebase Dynamic Links in flutter seems to recognize it was opened through a link but the link is always null

I'm using dynamic links ^0.7.0+1
When I press the link it loads the app, but doesn't navigate to the page I wanted, I've printed the link I receive, and it's always null, both when the app is in the background or not running at all.
Future<void> retrieveDynamicLink(BuildContext context) async {
List<String> linkData;
print('in retrieve link');
final PendingDynamicLinkData data =
await FirebaseDynamicLinks.instance.getInitialLink();
_handleDynamicLink(data);
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLink) async {
linkData = await _handleDynamicLink(dynamicLink);
}, onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
});
if (data != null) {
AppRoutes.pushLinkEntryPage(
context: context, spreadsheetId: linkData[0], grade: linkData[1]);
}
}
static Future<List<String>> _handleDynamicLink(
PendingDynamicLinkData data) async {
String ssId = '';
String grade = '';
final Uri deepLink = data?.link;
print('deepLink = $deepLink');
if (deepLink == null) {
return null;
}
if (deepLink.queryParameters.containsKey('ss')) {
ssId = deepLink.queryParameters['ss'];
print('in retrieve link: ssID = $ssId');
}
if (deepLink.queryParameters.containsKey('gd')) {
grade = deepLink.queryParameters['gd'];
print('in retrieve link: gd = $grade');
}
return [ssId, grade];
}
I wasted couple of hours on this, and found the fix to be using the "Long dynamic link" instead of the "Deep link".
See attached image below:(This show details of the dynamic link in console)

Flutter redirect when encountered http 401

I have a navigation like this for now:
main > app > account > address
right now i encountered issue if user fetching address and their session expires how can I redirect them back to login page? Still new to flutter, can't find the esolution how to send unauthenticated status to main so it can redirect it.
I'm using dio and MobX package.
dio.interceptors
.add(InterceptorsWrapper(onRequest: (RequestOptions options) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String token = prefs.getString('access_token');
if (token != null) {
options.headers['Authorization'] = 'Bearer $token';
}
options.headers['Accept'] = 'application/json';
return options; //continue
}, onResponse: (Response response) {
final int statusCode = response.statusCode;
var results = {};
if (statusCode == 200 || statusCode == 201 || statusCode == 204) {
final dynamic decodeResponse = this.decodeResponse(response);
bool responseIsList = decodeResponse is List;
if (!responseIsList && decodeResponse['token'] != null) {
final token = decodeResponse['token'];
setAuthorizationToken(token['access_token'], token['refresh_token']);
}
if (responseIsList) {
return decodeResponse;
} else {
final resultToAdd = decodeResponse;
results.addAll(resultToAdd);
return results;
}
}
return response;
}, onError: (DioError e) async {
final r = e.response;
if (r.statusCode == 401) {
AuthStore().unauthorize(prefs: _sharedPreferences);
}
if (r != null) {
return {"has_error": true, ...r.data};
}
// Do something with response error
return e;
}));
I've tried to put Observer on my main.dart to keep checking on the status
#override
Widget build(BuildContext context) {
return Observer(builder: (_) {
if (store.statusAuth == 401) {
Navigator.pushReplacementNamed(context, '/'); // this is error
}
// other material app function
});
Any solution?
The solution only I can think of for now is checking every time API is called (every store). if 401 will automatically send it back to login page. but this approach will be very redundant.
I have the same issue. To resolve this issue, we need a Navigation without context to can navigate on interceptors.
Api service is initializated before MaterialApp. MaterialApp is allowed pass the navigatorKey for top navigator.
I create GlobalKey when define interceptors of apiservice and use it for return MaterialApp.
final GlobalKey<NavigatorState> navigator;//Create a key for navigator
and create apiService
void _configAuthApiService() {
...
if (error.response?.statusCode == 401) {
navigator.currentState.pushReplacementNamed('/login');
}
...
}
When create material app:
MaterialApp(
...
navigatorKey: navigator,
...
)