Flutter/Backendkess cross-platform crash

My Flutter app is authenticating and storing data/retrieving data (thanks to help from Mark) through Backendless when run on the Android simulator (from Android Studio).

However, when run as a browser app (Chrome or Edge) the app crashes in the Backendless init call
static void initializeApp(BuildContext context) async {
await Backendless.initApp(
applicationId: appID,
iosApiKey: apiKeyiOS,
androidApiKey: apiKeyAndroid);

The debug info (below) seems to indicate a null safety issue in the Backendless sdk.dart lib with initApp … perhaps some thing with value in the android code is null in the browser code?

The (first 13) debug messages:
Error: Unexpected null value.
at Object.throw_ [as throw] (http://localhost:64092/dart_sdk.js:5061:11)
at Object.nullCheck (http://localhost:64092/dart_sdk.js:5388:30)
at initApp (http://localhost:64092/packages/backendless_sdk/backendless_sdk.dart.lib.js:5720:25)
at initApp.next ()
at runBody (http://localhost:64092/dart_sdk.js:38659:34)
at Object._async [as async] (http://localhost:64092/dart_sdk.js:38690:7)
at Function.initApp (http://localhost:64092/packages/backendless_sdk/backendless_sdk.dart.lib.js:5713:20)
at initializeApp (http://localhost:64092/packages/backendless_todo_starter/pages/todo_page.dart.lib.js:1358:43)
at initializeApp.next ()
at runBody (http://localhost:64092/dart_sdk.js:38659:34)
at Object._async [as async] (http://localhost:64092/dart_sdk.js:38690:7)
at Function.initializeApp (http://localhost:64092/packages/backendless_todo_starter/pages/todo_page.dart.lib.js:1357:20)

Hello, @Jim_Austin.

I think the problem is due to the fact that you have not initialized the jsApiKey.
Try this and tell me if your problem is fixed.

Best Regards, Nikita.

Good thought, Nikita, I believe I’ve read that the Dart code is translated into JavaScript for the browser targets. Added the jsApiKey to the appInit call but still getting the error:

static void initializeApp(BuildContext context) async {
String res = ‘OK’;
logger.i(‘Before initApp’);
await Backendless.initApp(
applicationId: appID,
iosApiKey: apiKeyiOS,
androidApiKey: apiKeyAndroid,
jsApiKey: apiKeyJS)
.onError((error, stackTrace) {res = error.toString();}
);
logger.w(‘After initApp - res = $res’);

Added the logging and error trap so could see what’s going on. onError returns
“PlatformException(error, TypeError: Cannot read properties of undefined (reading ‘initApp’), null, null)”

First three stack items:
│#0 C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart 908:28 get current
#1 packages/backendless_todo_starter/init.dart 26:12 initializeApp
#2 C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 45:50

Can send the entire stack if that would be useful.
Jim

Hello, can you share with me your index.html file?

Your index.html file should look something like this:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta content="IE=Edge" http-equiv="X-UA-Compatible">
  <meta name="description" content="A new Flutter project.">

  <!-- iOS meta tags & icons -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="MyAppChat">
  <link rel="apple-touch-icon" href="icons/Icon-192.png">

  <!-- Favicon -->
  <link rel="shortcut icon" type="image/png" href="favicon.png"/>

  <title>MyAppChat</title>
  <link rel="manifest" href="manifest.json">
  <script src="https://api.backendless.com/sdk/js/latest/backendless.min.js"></script>
</head>
<body>
  <!-- This script installs service_worker.js to provide PWA functionality to
       application. For more information, see:
       https://developers.google.com/web/fundamentals/primers/service-workers -->
  <script>
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', function () {
        navigator.serviceWorker.register('flutter_service_worker.js');
      });
    }
  </script>
  <script src="main.dart.js" type="application/javascript"></script>
</body>
</html>

Nikita, index.html copied into index.txt and attached … .html files don’t seem to be allowed to be uploaded. Screen shows it’s attached; let me know if it doesn’t get through

Seems to look similar to what you sent, although the script is much longer.
Jim
index.txt (3.7 KB)

Hello, @Jim_Austin.
I don’t see the following line in your index.html:

<script src="https://api.backendless.com/sdk/js/latest/backendless.min.js"></script>

Try adding it like in the example I uploaded earlier and let me know if it helped you.
Best Regards, Nikita.

Nikita,
Added that line; now the initApp call is returning ‘OK’ (no error). However, now seeing an error on other Backendless calls, all (I think) of the type under Browser below.

Here’s the code/responses for ‘login’; note

  • the logging of the parameter values before the call
  • the call itself with the parameters,
  • and the logging of the results, using the Browser target and the Android target.
    logMsg(‘loginUser: username = $username ; password = $password’, style: 1); => loginUser: username = jaustin@gmail.com ; password = 123

BackendlessUser? user = await Backendless.userService.login(username, password, true).onError((error, stackTrace) {
result = error.toString();

logMsg(‘loginUser 2 - result = $result’, style: 2);

Browser
loginUser 2 - result = PlatformException(error, Expected a value of type ‘Object’, but got one of type ‘Null’, null, null)

Android simulator
I/flutter ( 6068): login jaustin@gmail.com ; 123
I/flutter ( 6068): loginUser 2 - result = OK
I/flutter ( 6068): getTodos 1
I/flutter ( 6068): getTodos 2

Don’t know where this issue ranks in your support priorities, so let me say this:

  • I’m new to Backendless and still learning Flutter. Backendless won’t make money from me anytime soon, so if you want to put the issue on a ‘backburner’ at this time I’d understand.
  • OTOH, if you want to keep working at it I’m happy to help; I’m getting a lot out of the troubleshooting process. I’d be happy to make the app available and/or keep trying fixes. Just let me know.
    Jim

Hello, @Jim_Austin.

In simple words:

await is meant to interrupt the process flow until the async method has finished. then or onError however does not interrupt the process flow (meaning the next instructions will be executed) but enables you to run code when the async method is finished.

In your example, you cannot achieve what you want when you use onError because the code is not ‘waiting’ and the return statement is processed and thus returns an empty object.

When you add the await, you explicitly say: 'don’t go further until my Future method is completed (namely the orError part).

You could write your code as follows to achieve the same result using only await:

    BackendlessUser? user;
    try {
      user = await Backendless.userService
          .login('hdhdhd@gmail.com', '123234', true);
      print(user);
    } catch (e) {
      print(e);
    }

Or with then and catchError:

    BackendlessUser? user;
    Backendless.userService
        .login('hdhdhd@gmail.com', '123234', true)
        .then((value) {
      user = value;
      print(user);
    }).catchError((e) {
      print(e);
    });

But personally I would prefer await. If in the next operation you need to work with the user, and the operation has not yet completed, this can lead to execution errors.

Best Regards, Nikita.

Nikita,
Changed code as you suggested, getting same error. Coped the app method loginUser below so you can see:

  • the method is ‘async’ and the login api call is ‘await’
  • the (debugging) print statements
  • the console output for the Android target and the Browser target (Chrome and Edge give same response).

Suggestions?
Jim


Future loginUser(String username, String password) async {
String result = ‘OK’;
_showUserProgress = true;
_userProgressText = ‘Logging user in …’;
notifyListeners();
// stayLoggedIn parameter set to ‘true’
BackendlessUser? user;
try {
user = await Backendless.userService.login(username, password, true);
print(‘Backendless: $user - login parameters: loginUser: username = $username ; password = $password’);
} catch (e) {
print(‘Backendless login error: $e - login parameters: loginUser: username = $username ; password = $password’);
} // try/catch

Android console
I/flutter ( 8798): Backendless: BackendlessUser{{lastLogin: 2021-12-15 14:16:39.980, userStatus: ENABLED, created: 2021-11-28 16:07:55.000, accountType: BACKENDLESS, ownerId: 057A3911-F533-4E01-82C2-D4D619E4DE58, socialAccount: BACKENDLESS, oAuthIdentities: null, name: Jim, ___class: Users, blUserLocale: en, updated: null, objectId: 057A3911-F533-4E01-82C2-D4D619E4DE58, email: jaustin@gmail.com}} - login parameters: loginUser: username = jaustin@gmail.com ; password = 123

Browser console
Backendless login error: PlatformException(error, Expected a value of type ‘Object’, but got one of type ‘Null’, null, null) - login parameters: loginUser: username = jaustin@gmail.com ; password = 123

Hello, @Jim_Austin.

I see that everything works fine on Android. Is the problem on the web only?
What version of backendless-sdk are you using?

A similar problem was in version 7.1.6 and below.
You need to update your sdk to the latest version, namely 7.1.8.
This issue was fixed in 7.1.7

Best Regards, Nikita.

Nikita,
I’m sorry, I should have been more specific in the subject line; it’s always been about the difference in how this Flutter app works on a browser target (crashed) versus an android device (works).

The app has always worked on android; on the browser:

  • Adding the script line to index.html made the connection to Backendless work (initApp running)
  • Updating the Backendless-sdk package to 7.1.8 got the login and registration functions working
    I’m marking this topic ‘solved’: app no longer crashes on the browser target.

I still see a difference in android vs browser operation; it’s described below. Let me know if you want to deal with it here, or if I should submit it as a new topic. Support groups differ in how they handle ‘run-on’ issues.
Jim

The present problem: The same code returns different data formats (type) on the different targets. Note in the returns (third line):

  • for the android target, ‘created: 2021-12-08 17:47:22.000’
  • on the browser target, ‘created: 1639003642000’
    This causes a ‘Expected a value of type ‘DateTime’, but got one of type ‘int’’ error.

Code
Future getTodos(String username) async {
String result = ‘OK’;
DataQueryBuilder queryBuilder = DataQueryBuilder()…whereClause = “username = ‘$username’”;
_busyRetrieving = true;
notifyListeners();
List<Map<dynamic, dynamic>?>? map =
await Backendless.data.of(‘TodoEntry’).find(queryBuilder).onError((error, stackTrace) {
result = error.toString();
}); // get data from TodoEntry table
logMsg(‘getTodos 1’, style: 1); print(map);

Android return
I/flutter ( 8903): Backendless login successful!
I/flutter ( 8903): getTodos 1 (, )
I/flutter ( 8903): [{created: 2021-12-08 17:47:22.000, ___class: TodoEntry, todos: {0: {created: 1639089563387.0, title: bananas, done: 0}, 1: {created: 1639089552456.0, title: bread, done: 0}, 2: {created: 1639086051496.0, title: apples, done: 0}, 3: {created: 1639086041302.0, title: milk, done: 0}}, ownerId: 057A3911-F533-4E01-82C2-D4D619E4DE58, updated: 2021-12-09 17:42:39.000, objectId: FAC89F13-80B5-4E31-A72F-54EA4BB3A16A, username: jaustin@gmail.com}]
I/flutter ( 8903): fromJson (, )

Browser return
Backendless login successful!
getTodos 1 (, )
[{created: 1639003642000, ___class: TodoEntry, todos: {0: {done: 0, title: bananas, created: 1639089563387}, 1: {done: 0, title: bread, created: 1639089552456}, 2: {done: 0, title: apples, created: 1639086051496}, 3: {done: 0, title: milk, created: 1639086041302}}, ownerId: 057A3911-F533-4E01-82C2-D4D619E4DE58, updated: 1639089759000, objectId: FAC89F13-80B5-4E31-A72F-54EA4BB3A16A, username: jaustin@gmail.com}]
fromJson (, )
Error: Expected a value of type ‘DateTime’, but got one of type ‘int’

(Application ID: 6B43094C-5944-5D8A-FF17-CFAE0CED4500 if you want to look at the table [‘TodoEntry’])

Hello, @Jim_Austin.

Yes, there is such a problem. This will be corrected, but I cannot give an exact timeline. Therefore, for now, I can suggest that you process the data manually.

An example of how this is done, if you need convert to datetime:

    List<Map<dynamic, dynamic>?>? data =
        await Backendless.data.of("TestTable").find();

    if(data != null && data.isNotEmpty && kIsWeb)
      data.asMap().forEach((index, map) {
        map!['created'] = DateTime.fromMillisecondsSinceEpoch(map['created']);

        if(map['updated'] != null)
          map['updated'] = DateTime.fromMillisecondsSinceEpoch(map['updated']);

        data[index] = map;
      });

If you need convert to timestamp:

    List<Map<dynamic, dynamic>?>? data =
        await Backendless.data.of("TestTable").find();

    if(data != null && data.isNotEmpty && io.Platform.isAndroid)
      data.asMap().forEach((index, map) {
        map!['created'] = (map['created'] as DateTime).millisecondsSinceEpoch;

        if(map['updated'] != null)
          map['updated'] = (map['updated'] as DateTime).millisecondsSinceEpoch;

        data[index] = map;
      });

Don’t forget to add libraries:

import 'package:flutter/foundation.dart' show kIsWeb;
import 'dart:io' as io;

Best Regards, Nikita.

Thanks Nikita, I’ll give this workaround a try.

And thanks for the support in getting through these various problems!

Jim

Always happy to help :slightly_smiling_face: