データベースが読み込めない
解決したいこと
Flutterで単語帳のスマホアプリを作っています。
Driftを使って、データベースの操作をしています。
データベースを読み込みたいのですが、エラーが出ているので、その解決方法を教えてほしいです。
発生している問題・エラー
SqliteException(11): while executing, database disk image is malformed, database disk image is malformed (code 11)
Causing statement: CREATE TABLE IF NOT EXISTS "words" ("id" INTEGER NOT NULL, "sort" TEXT NOT NULL, "name" TEXT NOT NULL, "english" TEXT NOT NULL, "rank" INTEGER NOT NULL, "read" TEXT NOT NULL, "start" TEXT NOT NULL, "end" TEXT NOT NULL, "nerve" TEXT NOT NULL, "medulla" TEXT NOT NULL, "action" TEXT NOT NULL, "sub" TEXT NOT NULL, "comment" TEXT NOT NULL, PRIMARY KEY ("name"));, parameters:
wordlist.dartの
void _getAllWords() async{
_wordList = await database.allWords;
setState(() {});
}
という箇所で、エラー文が出ます。データベースの中身を、画面に表示したいと思って、これを書いているのですが…。
エミュレータを起動して、デバッグすると、アプリは立ち上がるのですが、途中でエラーが出てしまい、画面上にデータベースの中身は表示されません。
該当するソースコード
main.dart
//一番基本の土台画面。
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:kinnikutango/db/database.dart';
import 'screens/app.dart';
import 'package:path/path.dart';//手入力付けたし
import 'package:path_provider/path_provider.dart';//手入力付けたし
late MyDatabase database;
void main() async{
WidgetsFlutterBinding.ensureInitialized();
var dbPath = await getDbPath();
database = MyDatabase(dbPath: dbPath);
runApp(MyApp());
}
Future<String> getDbPath() async{
var dbDir = await getApplicationDocumentsDirectory();
var dbPath = join(dbDir.path, "word.db");
if(FileSystemEntity.typeSync(dbPath) == FileSystemEntityType.notFound){
ByteData byteData = await rootBundle.load("assets/db/word.db");
List<int> bytes = byteData.buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes);
await File(dbPath).writeAsBytes(bytes);
}
return dbPath;
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "筋肉単語", //TODO 後でタイトル変更する
home: AppScreen(),
theme: ThemeData(
fontFamily: "NotoSansJapanese",//フォント変更
appBarTheme: AppBarTheme(
backgroundColor: Color(0xFF96BBFF),
centerTitle: true,
),
),
debugShowCheckedModeBanner: false,//デバッグの右上のリボンみたいなのを消す
);
}
}
wordlist.dart
import 'package:flutter/material.dart';
import '../db/database.dart';
import '../main.dart';
import 'edit_screen.dart';
class WordListScreen extends StatefulWidget {
const WordListScreen({Key? key}) : super(key: key);
@override
State<WordListScreen> createState() => _WordListScreenState();
}
class _WordListScreenState extends State<WordListScreen> {
List<Word> _wordList = [];
@override
void initState() {
super.initState();
_getAllWords();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('単語一覧'),
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: () => _addNewWord(),
tooltip: "新しい単語の登録", //長押しすると説明文を出す
),
],
),
body: _wordListWidget(),
);
}
_addNewWord() {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => EditScreen()));
}
void _getAllWords() async{
_wordList = await database.allWords;
setState(() {});
}
Widget _wordListWidget() {
return ListView.builder(
itemCount: _wordList.length,
itemBuilder: (context, int position) => _wordItem(position));
}
Widget _wordItem(int position) {
return Card(
child: ListTile(
title: Text(
"${_wordList[position].name}"
),
subtitle: Text(
"${_wordList[position].read}"
),
),
);
}
}
database.dart
import 'dart:io';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart';
import 'package:sqlite3/sqlite3.dart';
part 'database.g.dart';
class Words extends Table {
IntColumn get id => integer()();
TextColumn get sort => text().nullable()();
TextColumn get name => text()();
TextColumn get english => text().nullable()();
IntColumn get rank => integer().nullable()();
TextColumn get read => text().nullable()();
TextColumn get start => text().nullable()();
TextColumn get end => text().nullable()();
TextColumn get nerve => text().nullable()();
TextColumn get medulla => text().nullable()();
TextColumn get action => text().nullable()();
TextColumn get sub => text().nullable()();
TextColumn get comment => text().nullable()();
@override
Set<Column> get primaryKey => {name};
}
@DriftDatabase(tables: [Words])
class MyDatabase extends _$MyDatabase {
final String dbPath;
MyDatabase({required this.dbPath})
: super(_openConnection(dbPath)); //null許容のエラーが出たから、「required」追加した。
@override
int get schemaVersion => 1;
//Creates
Future addWord(Word word) => into(words).insert(word);
//Read
Future<List<Word>> get allWords => select(words).get();
//Update
Future updateWord(Word word) => update(words).replace(word);
//Delete
Future deleteWord(Word word) =>
(delete(words)..where((t) => t.name.equals(word.name))).go();
}
LazyDatabase _openConnection(String dbPath) {
// the LazyDatabase util lets us find the right location for the file async.
return LazyDatabase(() async {
// put the database file, called db.sqlite here, into the documents folder
// for your app.
//final dbFolder = await getApplicationDocumentsDirectory();//本当にいらんのか不明。
final file = File(dbPath);
// Also work around limitations on old Android versions
if (Platform.isAndroid) {
await applyWorkaroundToOpenSqlite3OnOldAndroidVersions();
}
// Make sqlite3 pick a more suitable location for temporary files - the
// one from the system may be inaccessible due to sandboxing.
final cachebase = (await getTemporaryDirectory()).path;
// We can't access /tmp on Android, which sqlite3 would try by default.
// Explicitly tell it about the correct temporary directory.
sqlite3.tempDirectory = cachebase;
return NativeDatabase.createInBackground(file);
});
}
自分で試したこと
・エラー文をgoogleで検索してみたけど、いまいち分かりませんでした。
・driftの自動生成コード前からやり直し。
・最初はデータベースにnullがあったので、database.dartの、wordsテーブルで、nullableで書き直して、自動生成コードをやり直しました。
・データベースでnullのところを、「0」に置き換えてみて、自動生成コードをやり直し。
・その他、チャットgptに言われた通りにやってみましたが、私には何をしていたのかは不明でした…。言われた通りに続けていっても、次から次へとエラーが出ました。
ここまでお読みいただき、ありがとうございます。
プログラミング自体、かなりの初心者で、ここで質問させていただくのも初めてなので、質問が分かりにくかったら申し訳ございません。
よろしくお願いします。