0

I am trying to connect to a static database as it is explained in this answer. I therefore created an asynchronous function that looks like this:

Future<void> loadDataBase() async {
  // Construct a file path to copy database to
  Directory documentsDirectory = await getApplicationDocumentsDirectory();
  String path = join(documentsDirectory.path, "asset_worldcities.db");

  // Only copy if the database doesn't exist
  if (FileSystemEntity.typeSync(path) == FileSystemEntityType.notFound) {
    // Load database from asset and copy
    ByteData data = await rootBundle.load(join('assets', 'worldcities.db'));
    List<int> bytes = data.buffer.asUint8List(
        data.offsetInBytes, data.lengthInBytes);

    // Save copied asset to documents
    await new File(path).writeAsBytes(bytes);
  }
}

Now I thought I could access my database inside my main widget by using this function and then call

Directory appDocDir = await getApplicationDocumentsDirectory();
String databasePath = join(appDocDir.path, 'asset_database.db');
this.db = await openDatabase(databasePath);
initialized = true;
Future<List<Page>> search(String word, int parentId) async {
    if (!initialized) await this._initialize();
    String query = '''
      SELECT * FROM users
      LIMIT 25''';
    return await this.db.rawQuery(query);
}

but this way I am not allowed to use this.db and also not await as I am not inside an async function. Where do I need to put this database request so that it works?

Axel
  • 1,415
  • 1
  • 16
  • 40
  • I would put anything like `getApplicationDocumentsDirectory` or `openDatabase` inside `main` before `runApp`. For `search` you should use FutureBuilder. – Tirth Patel Oct 07 '20 at 20:57
  • Hi thanks for your comment! Then I need to make `main` an asynchronous function, correct? – Axel Oct 07 '20 at 21:02
  • This seems not to be allowed :/ – Axel Oct 07 '20 at 21:04
  • You can definitely make `main` an asynchronous function, all my apps do. You might have to do a `flutter clean` before building again. – rmtmckenzie Oct 07 '20 at 21:14
  • Android Studio marks the async with red underline and says `Unexpected text async` if I do `void main() asnyc {`. How can I prevent this? – Axel Oct 07 '20 at 21:22
  • hmm I use intellij and haven't seen that. Try removing the `void`? – rmtmckenzie Oct 07 '20 at 21:31

1 Answers1

1

Depending whether you need to do this every time and the database could grow, or whether it's a one-time operation (which it seems like it might be?) and the database is small enough that it's not going to take long to query it, there are different approaches I'd take.

If it's a one-time per install sort of thing and the database will always be small, making the user wait while it copies the file across probably isn't a huge deal. In that case I'd do something like this:

main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  if (needToLoadDatabase()) {
    await loadDatabase();
  }

  let users = await queryUsers();

  runApp(MainWidget(users: users));
}

However, if you're reading from the database and it's something that could take any significant amount of time, I'd recommend initiating the load and then passing the future into your main widget, where it could use a FutureBuilder to build an intermediate UI.

That'd look something like this:

main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  let loadUsers = () async {
    if (needToLoadDatabase()) {
      await loadDatabase();
    }

    return await queryUsers();
  }();


  runApp(MainWidget(loadUsers: loadUsers));
}

class MainApp extends StatelessWidget {

  final Future<Users> loadUsers;

  MainApp({@required this.loadUsers, Key key}): super(key: key);

  Widget build(BuildContext context) {
    return FutureBuilder(
      builder: (ctx, snapshot) {
        if (snapshot.hasData) {
          // build your UI with data
        } else {
          // build your UI without data
        }
      }
    );
  }
}

Also note that there's no reason you have to do the loading in the main function - you could make your widget stateful and kick that off in the initState, or any number of places like directly where you use the list. You could also look at the FutureProvider from the Provider package.

rmtmckenzie
  • 37,718
  • 9
  • 112
  • 99
  • Thank you for this thorough answer! I think I will open another question for my strange `async` issue and then I can hopefully come back here and implement the database access. – Axel Oct 07 '20 at 21:37