objectbox / objectbox-dart

Flutter database for super-fast Dart object persistence
https://docs.objectbox.io/getting-started
Apache License 2.0
927 stars 115 forks source link

ObjectBox is storing all parameters with 'null' flutter/dart #215

Closed BlackReaper94 closed 3 years ago

BlackReaper94 commented 3 years ago

Describe the bug I want to store one type of object because its all I need. None of the parameters are final (I saw there might be problems with that). When checking the database it keeps getting filled. For my tests I checked the length of the database then placed an object, checked the length again and tried to get that object immediatly again. Unfortunately all fields of the object I just stored are 'null' now.

Basic info (please complete the following information):

Code

import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:personal_expenses/objectbox.g.dart';
import 'transaction.dart';
import 'package:objectbox/objectbox.dart';

class DataBase {
  Store _store;
  Box<Transaction> _dataBox;
  Query<Transaction> _query;

  DataBase(Directory dir)
      : _store =
            Store(getObjectBoxModel(), directory: dir.path + '/objectbox') {
    _dataBox = Box<Transaction>(_store);
    _query = _dataBox
        .query()
        .order(Transaction_.dataId, flags: Order.descending)
        .build();
  }

  void addTransaction(Transaction newtx) => _dataBox.put(newtx);

  void deleteTransaction(Transaction tx) => _dataBox.remove(tx.dataId);

  void nuke() {
    _dataBox.removeAll();
  }

  Stream<Query<Transaction>> get queryStream => _query.stream;

  List<Transaction> get allTx => _query.find();

  void dispose() {
    _query.close();
    _store.close();
  }
}
import 'package:flutter/foundation.dart';
import 'package:objectbox/objectbox.dart';

@Entity()
class Transaction {
  @Id()
  int dataId ;

  String id;
  String title;
  DateTime date;
  bool addingMoney;
  String account;
  double amount;
  bool monthly;

  Transaction({
    @required this.id,
    @required this.title,
    @required this.date,
    @required this.addingMoney,
    @required this.account,
    @required this.amount,
    @required this.monthly,
  });
}
  @override
  void initState() {
    super.initState();
    getApplicationDocumentsDirectory().then((dir) {
      _dataBox = new DataBase(dir);
      print(_dataBox.allTx.length);
      _userTransactions = _dataBox.allTx;
      print(_userTransactions[0].amount);
    });
    //WidgetsBinding.instance.addPostFrameCallback((_) => _readData());
  }

Logs, stack traces

The following NoSuchMethodError was thrown building: The method 'toStringAsFixed' was called on null. Receiver: null Tried calling: toStringAsFixed(2)

Additional context The path is provided as in the example app of objectbox dart


Edit @vaind: fixed code blocks formatting

vaind commented 3 years ago

I've tried to reproduce the issue with the provided info and can't (in other words, inserting and reading works as expected). I don't see how you insert data (it's not in the code you've provided), so I've made an assumption:

final _dataBox = DataBase(Directory('repro'));
// on the first run (when the database is initially empty), I've had the following line uncommented to insert an item
// _dataBox.addTransaction(Transaction(title: 'foo', amount: 10));
print(_dataBox.allTx.length);
final _userTransactions = _dataBox.allTx;
print(_userTransactions[0].amount);
vaind commented 3 years ago

Could you please add the missing pieces to make this reproducible? Also, you don't say what platform (OS) you're running the test on, so that may also make sense to add.

BlackReaper94 commented 3 years ago

Could you please add the missing pieces to make this reproducible? Also, you don't say what platform (OS) you're running the test on, so that may also make sense to add.

Hey, thanks for checking back. I will provide the missing code in about 1 hour

BlackReaper94 commented 3 years ago
 void _addNewTransaction(
    String txTitle,
    double txAmount,
    DateTime choosenDate,
    String txAccount,
    bool txAddingMoney,
    bool txMonthly,
  ) {
    final newTx = Transaction(
      title: txTitle,
      amount: txAmount,
      date: choosenDate,
      id: DateTime.now().toString(),
      account: txAccount == null ? 'Ausgaben' : txAccount,
      addingMoney: txAddingMoney == null ? false : txAddingMoney,
      monthly: txMonthly == null ? false : txMonthly,
    );
    print(newTx.amount);
    print(_dataBox.allTx.length);
    _dataBox.addTransaction(newTx);
    print(_dataBox.allTx.length);
    setState(() {
      _userTransactions.add(newTx);
    });
    _userTransactions = _dataBox.allTx;
        print(_userTransactions[0].amount);
  }

This method is responsible for adding the new Transaction to the DataBase + it shows the list of Transactions (setState) However the output when adding a transaction was following:

I/flutter ( 7763): 250.0
I/flutter ( 7763): 0
I/flutter ( 7763): 1
I/flutter ( 7763): null

════════ Exception caught by widgets library ═══════════════════════════════════ The following NoSuchMethodError was thrown building: The method 'toStringAsFixed' was called on null. Receiver: null Tried calling: toStringAsFixed(2)`

I am working on a Win10 Laptop with a Android Studio Vm of a Pixel 2 with sdk version 30

vaind commented 3 years ago

Still can't reproduce the issue (used 64-bit Pixel2 emulator on Linux, unfortunately, can't try Windows), I've updated our example to "match" your code but everything seems to work: see latest commits in branch https://github.com/objectbox/objectbox-dart/tree/215-storing-null

I/flutter ( 9939): 10.0
I/flutter ( 9939): 3
I/flutter ( 9939): 4
I/flutter ( 9939): [Instance of 'Transaction', Instance of 'Transaction', Instance of 'Transaction', Instance of 'Transaction']
I/flutter ( 9939): 10.0
I/flutter ( 9939): 10.0
I/flutter ( 9939): 4
I/flutter ( 9939): 5
I/flutter ( 9939): [Instance of 'Transaction', Instance of 'Transaction', Instance of 'Transaction', Instance of 'Transaction', Instance of 'Transaction']
I/flutter ( 9939): 10.0

Maybe try running the example app in that branch if it also works for you and if it does, see what makes the difference.

BlackReaper94 commented 3 years ago

I downloaded the example and tried to run it on the vm and my personal phone (Android). Unfrotunately it failed on both devices too. This time the method 'allTx' was null.

Im not sure what the issue may be but I will try to rerun both apps on my second pc to ensure its not an local issue with my laptop.

Thanks for your help so far, I will keep you updated

BlackReaper94 commented 3 years ago

I tried to compile the application on my Win 8.1 Laptop but the issue remains the same. I don't get what the issue might be. Even with dummy objects and the exact same code it still wont do it.

If wanted I can share the repository with you or the main.dart but the code relevant for the database seems to be the same as in your example.

greenrobot-team commented 3 years ago

The example and https://github.com/objectbox/objectbox-dart/tree/215-storing-null assembles fine on my Windows 10 x64 machine, works as expected on a real Android device (Android 10) and emulator (Android 11/SDK 30).

@BlackReaper94 Can you maybe tell us exactly what your steps are between "download the example" and "run it on my personal phone"? Also your flutter doctor output might help.

BlackReaper94 commented 3 years ago

@greenrobot-team I created a new project, installed objectbox as instructed in the manual, added all necessary dependencies and then copied the whole main.dart from your example in mine.

Then I run the command 'flutter pub run build_runner build' to create the objectbox.g.dart and objectbox-model.json. After all I compile the app and get the result:

════════ Exception caught by gesture ═══════════════════════════════════════════
The following NoSuchMethodError was thrown while handling a gesture:
The getter 'allTx' was called on null.
Receiver: null
Tried calling: allTx

The output of flutter doctor is:

Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 2.0.3, on Microsoft Windows [Version 10.0.18363.1316], locale de-DE)
[√] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[√] Chrome - develop for the web
[√] Android Studio (version 4.1.0)
[√] VS Code (version 1.55.0)
[√] Connected device (1 available)
greenrobot-team commented 3 years ago

OK, did the same: use Android Studio 4.1.3 with Flutter plugin 55.0.1 to create a new Flutter application project. This is how the pubspec.yaml looks like (I removed some comments):

name: gh_all_tx_null
description: A new Flutter application.

publish_to: 'none' # Remove this line if you wish to publish to pub.dev

version: 1.0.0+1

environment:
  sdk: ">=2.7.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  path_provider: ^2.0.0
  intl: any
  objectbox: ^0.14.0
  objectbox_flutter_libs: any

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2

dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^1.0.0
  objectbox_generator: any

# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

App runs fine on Android emulator using main.dart from the example, also allTx is found.

The only difference from flutter doctor: my machine has Flutter 2.0.4 and the latest Windows 10 with the latest updates.

Not sure there is much more to do here as this can not be reproduced.

BlackReaper94 commented 3 years ago

The pubspec.yaml looks the same. I will reinstall flutter, upgrade to the latest version of it and try again.

Thanks for your assitance

BlackReaper94 commented 3 years ago

I think I found the issue. In the objectbox.g.dart following is part of the file:

 objectFromFB: (Store store, Uint8List fbData) {
        final buffer = fb.BufferContext.fromBytes(fbData);
        final rootOffset = buffer.derefObject(0);

        final object = Transaction(
            monthly:
                const fb.BoolReader().vTableGetNullable(buffer, rootOffset, 6))
          ..dataId =
              const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 4);

        return object;
      });

When testing I got the value for the boolean parameter 'monthly' from the database but it is the only parameter stored, all others are null which seems reasonable since its the only parameter besides 'dataId' written to the 'Transaction' object.

The objectbox-model.json is the following:

"_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.",
  "_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
  "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
  "entities": [
    {
      "id": "1:9154729744038933001",
      "lastPropertyId": "2:3092191708828397437",
      "name": "Transaction",
      "properties": [
        {
          "id": "1:3321424842976390297",
          "name": "dataId",
          "type": 6,
          "flags": 1
        },
        {
          "id": "2:3092191708828397437",
          "name": "monthly",
          "type": 1
        }
      ],
      "relations": []
    }
  ],
  "lastEntityId": "1:9154729744038933001",
  "lastIndexId": "0:0",
  "lastRelationId": "0:0",
  "lastSequenceId": "0:0",
  "modelVersion": 5,
  "modelVersionParserMinimum": 5,
  "retiredEntityUids": [],
  "retiredIndexUids": [],
  "retiredPropertyUids": [],
  "retiredRelationUids": [],
  "version": 1
}

It seems to be a problem with generating the files via 'flutter pub run build_runner build'

vaind commented 3 years ago

Is that objectbox-model.json after you've run flutter pub run build_runner build? What's the output of the command (with --verbose at the end) if you run it now? And does the objectbox-model.json change?

BlackReaper94 commented 3 years ago

Yes it is. Due to a mistake on my side the parameters in the Transaction.dart were final again. Thats why the files were generated without them.

After rerunning the command it still didn't worked so i deleted the vm (for the third time) recreated it with a different sdk version, restarted my phone and now it works .....

Sry for keeping you busy with this unnecessary issue ._.

Thanks for your help

greenrobot commented 3 years ago

After rerunning the command it still didn't worked so i deleted the vm (for the third time) ...

That does not sound right!? Please keep that in mind, maybe you'll have a similar situation and can provide additional info.

Also, we should find a way to inform about e.g. final properties to increase awareness for users like you.