I see no data from my database on app startup.
I get the impression that nothing was saved, but the database is not empty.
I added a few items, closed the app, reopened the app, and the UI shows empty data.
I am using Future Builder and I am doing refreshAllNames on startup.
Could someone please point out what’s wrong with my code.
Thank you
My Code:
import 'package:flutter/material.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
MyAppState createState() => MyAppState();
}
class DataFields {
static final List<String> values = [id, name];
static final String id = '_id';
static final String name = 'myName';
}
class DataModel {
int id;
String name;
DataModel({required this.id, required this.name});
DataModel copy(
{int? id, String? title, String? description, int? isSelected}) =>
DataModel(id: id ?? this.id, name: name ?? this.name);
Map<String, Object?> toJson() =>
{if (id != null) DataFields.id: id, DataFields.name: name};
static DataModel fromJson(Map<String, Object?> json) => DataModel(
id: json[DataFields.id] as int, name: json[DataFields.name] as String);
}
class DBHelper {
final String myTable = 'myDataTable';
static const String myDatabase = 'myDB.db';
DBHelper._init();
static final DBHelper instance = DBHelper._init();
static Database? db;
Future<Database> get dataBase async {
if (db != null) {
return db!;
} else {
db = await initDB(myDatabase);
return db!;
}
}
Future<Database> initDB(String filePath) async {
final dbPath = await getDatabasesPath();
final path = join(dbPath, filePath);
return await openDatabase(path, version: 1, onCreate: createDB);
}
Future createDB(Database db, int version) async {
var idType = 'INTEGER PRIMARY KEY AUTOINCREMENT';
var textType = 'TEXT NOT NULL';
await db.execute('''CREATE TABLE $myTable (${DataFields.id} $idType,${DataFields.name} $textType)''');
}
Future<DataModel> create(DataModel datamodel) async {
final db = await instance.dataBase;
final id = await db.insert(myTable, datamodel.toJson());
return datamodel.copy(id: id);
}
Future<DataModel> readData(int id) async {
final db = await instance.dataBase;
final maps = await db.query(myTable,
columns: DataFields.values,
where: '${DataFields.id} = ?',
whereArgs: [id]);
if (maps.isNotEmpty) {
return DataModel.fromJson(maps.first);
} else {
throw Exception('ID $id not found');
}
}
Future<List<DataModel>> readAllNames() async {
final db = await instance.dataBase;
final result = await db.rawQuery('SELECT * FROM $myTable');
final list = result.map((json) => DataModel.fromJson(json)).toList();
if (list.isNotEmpty)
return list;
else
throw Exception('Empty List');
}
Future<int> countAllDataRows() async {
final db = await instance.dataBase;
var x = await db.rawQuery('SELECT COUNT(1) FROM $myTable');
return x.length;
}
Future<int> update(DataModel datamodel) async {
final db = await instance.dataBase;
return db.update(myTable, datamodel.toJson(),
where: '${DataFields.id} = ?', whereArgs: [datamodel.id]);
}
Future<int> delete(int id) async {
final db = await instance.dataBase;
return await db
.delete(myTable, where: '${DataFields.id} = ?', whereArgs: [id]);
}
Future close() async {
final db = await instance.dataBase;
db.close();
}
}
class MyAppState extends State<MyApp> {
TextEditingController mc = TextEditingController();
static late List<DataModel> mylist = <DataModel>[];
int index = 0;
int myindex = 0;
bool isLoading = false;
@override
void initState() {
super.initState();
refreshNames();
}
@override
void dispose() {
mc.dispose();
DBHelper.instance.close();
super.dispose();
}
Future refreshNames() async {
setState(() => isLoading = true);
mylist = await DBHelper.instance.readAllNames();
setState(() => isLoading = false);
}
addItem() {
setState(() {
if (mc.text.isNotEmpty) {
index++;
mylist.add(DataModel(id: index, name: mc.text));
DBHelper.instance.create(DataModel(id: index, name: mc.text));
refreshNames();
}
});
}
removeItem() {
setState(() {
if (index > 0) {
index;
mylist.removeAt(index);
DBHelper.instance.delete(index);
refreshNames();
}
});
}
@override
Widget build(BuildContext context) {
Widget space = SizedBox(width: 100, height: 20, child: Text(""));
Widget myinput = TextField(
controller: mc,
decoration: InputDecoration(border: OutlineInputBorder(), labelText: 'Name'));
Widget btnAdd = ElevatedButton(onPressed: addItem, child: Text('Add'));
Widget btnRemove = ElevatedButton(onPressed: removeItem, child: Text('Remove'));
Widget newList = SizedBox(
width: 800,
height: 500,
child: FutureBuilder<List<DataModel>?>(builder:
(BuildContext context, AsyncSnapshot<List<DataModel>?> snapshot) {
return ListView.separated(
padding: const EdgeInsets.all(8),
itemCount: mylist.length,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 100,
child: Center(child: Text('${mylist[index].name}')),
);
},
separatorBuilder: (BuildContext context, int index) => const Divider(),
);
}));
return MaterialApp(
home: Scaffold(
resizeToAvoidBottomInset: false,
body: SingleChildScrollView(scrollDirection: Axis.vertical,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
padding: const EdgeInsets.all(5),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment:MainAxisAlignment.spaceEvenly,
children: <Widget>[
space,space,myinput,space,btnAdd,space,btnRemove,space,newList,space
])))
]))));
}
}
2
Answers
What seems as issue here is your readAllNames() function, this function only select the myName column from the table. but, the DataModel class has two columns: id and name. So, the fromJson() method of DataModel is not able to create a DataModel object from the result of this query since it expects both id and name fields.
Here are some changes you could try:
There is error while mapping data to class, you have not select id from table, whereas id field is not null in DataModel