How to stop class database file getting created multiple times when using flutter_moor?

Issue

Whenever I am adding a new row to my database in my flutter app, I am getting this error :

WARNING (moor): It looks like you’ve created the database classAppDatabase multiple times. When these two databases use the same QueryExecutor, race conditions will ocur and might corrupt the database.

I read different articles and deduced that I may have been calling the constructor more than once, but still not figuring out the way out of it.

Here are some code snippets for the initial reference:

pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  basic_utils: ^2.6.3
  cupertino_icons: ^1.0.0
  flutter_blue: ^0.7.3
  google_fonts: ^1.1.1
  http: ^0.12.2
  json_annotation: ^3.1.1
  logging: ^0.11.4
  #moor: ^3.4.0
  moor_flutter: ^3.1.0
  provider: ^4.3.3
  sqflite: ^1.3.2+3
  sqlite3_flutter_libs: ^0.3.0
  url_launcher: ^5.7.10

dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^1.9.0
  chopper_generator: ^3.0.4
  json_serializable: ^3.3.0
  moor_generator: ^3.4.1

moor_database.dart:

import 'package:moor_flutter/moor_flutter.dart';
part 'moor_database.g.dart';

@DataClassName('AvailableFunctionTable') //Final Table Name
class AvailableFunctionsTable extends Table {
  IntColumn get id => integer()();
  TextColumn get make => text().withLength(min: 1, max: 50)();
  TextColumn get model => text().withLength(min: 1, max: 50).nullable()();
  IntColumn get year => integer().nullable()();
  TextColumn get functionType => text().withLength(min: 1, max: 100)();
  TextColumn get functionName => text().withLength(min: 1, max: 100)();

  @override
  Set<Column> get primaryKey => {id};
}

@UseMoor(tables: [AvailableFunctionsTable])
class AppDatabase extends _$AppDatabase {
  AppDatabase()
      : super(FlutterQueryExecutor.inDatabaseFolder(
            path: "db.sqlite", logStatements: true));
  int get schemaVersion => 1;

  Future<List<AvailableFunctionTable>> getAllAvailableFunctionss() =>
      select(availableFunctionsTable).get();

  Stream<List<AvailableFunctionTable>> watchAllAvailableFunctionss() =>
      select(availableFunctionsTable).watch();

  Future insertAvailableFunctions(AvailableFunctionTable availableFunctions) =>
      into(availableFunctionsTable).insert(availableFunctions);

  Future updateAvailableFunctions(AvailableFunctionTable availableFunctions) =>
      update(availableFunctionsTable).replace(availableFunctions);

  Future deleteAvailableFunctions(AvailableFunctionTable availableFunctions) =>
      delete(availableFunctionsTable).delete(availableFunctions);
}

main.dart:

void main() {
  runApp(AvFunctionsScreen());
}

class AvFunctionsScreen extends StatefulWidget {
  @override
  _AvFunctionsScreenState createState() => _AvFunctionsScreenState();
}

class _AvFunctionsScreenState extends State<AvFunctionsScreen> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData.dark(),
      home: Scaffold(
        appBar: AppBar(
          title: Text("AvFunctionsScreen List"),
        ),
        body: SingleChildScrollView(child: NewEntry()),
      ),
    );
  }
}

class NewEntry extends StatefulWidget {
  @override
  _NewEntryState createState() => _NewEntryState();
}

class _NewEntryState extends State<NewEntry> {
  TextEditingController idController = TextEditingController();

  TextEditingController makeController = TextEditingController();

  TextEditingController modelController = TextEditingController();
  TextEditingController functionNameController = TextEditingController();

  TextEditingController functionTypeController = TextEditingController();
  TextEditingController yearController = TextEditingController();
  bool isloading = false;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        TextField(
          decoration: InputDecoration(hintText: 'id'),
          keyboardType: TextInputType.number,
          controller: idController,
        ),
        TextField(
          decoration: InputDecoration(hintText: 'make'),
          keyboardType: TextInputType.text,
          controller: makeController,
        ),
        TextField(
          decoration: InputDecoration(hintText: 'model'),
          keyboardType: TextInputType.text,
          controller: modelController,
        ),
        TextField(
          decoration: InputDecoration(hintText: 'year'),
          keyboardType: TextInputType.number,
          controller: yearController,
        ),
        TextField(
          decoration: InputDecoration(hintText: 'func type'),
          keyboardType: TextInputType.text,
          controller: functionTypeController,
        ),
        TextField(
          decoration: InputDecoration(hintText: 'func name'),
          keyboardType: TextInputType.text,
          controller: functionNameController,
        ),
        RaisedButton(
          onPressed: () {
            setState(() {
              AppDatabase().insertAvailableFunctions(AvailableFunctionTable(
                id: int.parse(idController.text),
                make: makeController.text,
                model: modelController.text,
                year: int.parse(yearController.text),
                functionName: functionNameController.text,
                functionType: functionTypeController.text,
              ));
              idController.clear();
              makeController.clear();
              modelController.clear();
              yearController.clear();
              functionNameController.clear();
              functionTypeController.clear();
            });
          },
          color: Colors.green,
          child: Text("Add info"),
        ),
        Container(
          height: 700,
          width: double.infinity,
          child: StreamBuilder(
            stream: AppDatabase().watchAllAvailableFunctionss(),
            builder: (context,
                AsyncSnapshot<List<AvailableFunctionTable>> snapshot) {
              return ListView.builder(
                itemBuilder: (_, index) {
                  return Card(
                    color: Colors.blueAccent,
                    child: ListTile(
                        leading: CircleAvatar(
                          child: Text('${index + 1}'),
                          radius: 20,
                        ),
                        title: Text(snapshot.data[index].id.toString() +
                            ' ' +
                            snapshot.data[index].year.toString()),
                        subtitle: Text(snapshot.data[index].make +
                            ' ' +
                            snapshot.data[index].model +
                            ' ' +
                            snapshot.data[index].functionType +
                            ' ' +
                            snapshot.data[index].functionName),
                        trailing: IconButton(
                          icon: Icon(Icons.delete_outline),
                          onPressed: () {
                            setState(() {
                              AppDatabase().deleteAvailableFunctions(
                                  snapshot.data[index]);
                            });
                          },
                          color: Colors.red,
                        )),
                  );
                },
                itemCount: snapshot?.data?.length,
              );
            },
          ),
        )
      ],
    );
  }
}

Although I am getting my Output fine and the app seems working, It is giving me an error in the debug console every time on hot-restarting :

The method ‘[]’ was called on null.
Receiver: null
Tried calling: ” (The actual error has a pair of square brackets followed by a pair of round brackets having a zero between the round brackets instead of quotes)

So, I want to get rid of both these errors so that I can start integrating my APIs

Thanks in advance!

Solution

You should not call AppDatabase() more than once in your application. Calling AppDatabase() creates a new instance every time and you end up with many instances of the database. The best way to do this is to create it once and provide it to the needed components. Also, check the Singleton pattern in Dart.

Answered By – Stoyan Milev

Answer Checked By – Mildred Charles (FlutterFixes Admin)

Leave a Reply

Your email address will not be published.