Flutter Error - The method isn't defined for the type 'Future' - flutter sqflite database error - flutter

I am making an app in a flutter in which I can select the contacts from phone book and need to save them into the flutter local database sqflite. I have separate class for database named database_services.dart. In my other .dart file i m using
var _data = DatabaseService.instance.database;
then after that i m calling
_data.getcontacts() // Error statement
and
data.insertContacts(model)
but it is giving me error below
The method 'insertContacts' isn't defined for the type 'Future'. ry correcting the name to the name of an existing method, or defining a method named 'insertContacts'.
Both .dart files are attached for reference. What should i do ? what is wrong ?
my database class :
`
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
import 'contact_model.dart';
class DatabaseService{
DatabaseService._();
static final DatabaseService instance = DatabaseService._();
Database? _database;
String dbName = 'contact.db';
ContactModel model = ContactModel();
Future<Database?> get database async {
if(_database!= null){ return _database!;}
else{
_database = await initializeDB(dbName);
return _database!;
}
}
Future<Database> initializeDB(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 {
//make like johannes mike at end
await db.execute(
'CREATE TABLE contacts (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT, number TEXT)',
);
}
insertContacts(ContactModel modelObj) async {
// Get a reference to the database.
final db = await instance.database;
//
await db?.insert(
dbName,
modelObj.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<List<ContactModel>?> getcontacts() async {
// Get a reference to the database.
final db = await instance.database;
var response = await db?.query("contacts");
List<ContactModel>? list = response?.map((c) => ContactModel.fromMap(c)).toList();
return list;
/*
return List.generate(maps?.length as int , (i) {
return ContactModel(
id: maps![i]['id'] != null ? toString() : ' ',
displayName: maps[i]['name'] != null ? toString() : ' ',
phoneNumber: maps[i]['number'] != null ? toString() : ' ',
);
});
*/
}
}
`
my other class:
import 'package:firebase_contacts_test/contact_model.dart';
import 'package:firebase_contacts_test/database_services.dart';
import 'package:flutter/material.dart';
import 'package:fluttercontactpicker/fluttercontactpicker.dart';
import 'package:permission_handler/permission_handler.dart';
import 'contact_model.dart';
import 'package:sqflite/sqflite.dart';
class Plugin1 extends StatefulWidget {
const Plugin1({Key? key}) : super(key: key);
#override
State<Plugin1> createState() => _Plugin1State();
}
class _Plugin1State extends State<Plugin1> {
var _data = DatabaseService.instance.database;
ContactModel model = ContactModel();
List<ContactModel> _list = [];
PhoneContact? _contact;
String? _phoneContact;
String? _name;
String? _id;
final Permission _permission = Permission.contacts;
PermissionStatus _permissionStatus = PermissionStatus.denied;
Future<PermissionStatus> _getContactPermission() async {
_permissionStatus = await _permission.status;
if (_permissionStatus != PermissionStatus.granted) {
_permissionStatus = await _permission.request();
return _permissionStatus;
} else {
return _permissionStatus;
}
}
Future<void> printDB() async {
print('printintgdsfssfdgsdfg DB');
if(_data != null) {
print( await _data.getcontacts());
}
else{
print('no contacts slected');
}
}
pickContact() async {
PermissionStatus permissionStatus = await _getContactPermission();
if (permissionStatus == PermissionStatus.granted) {
final PhoneContact contact =
await FlutterContactPicker.pickPhoneContact();
print("my_contact");
print(contact);
_phoneContact = contact.phoneNumber?.number;
_name = contact.fullName;
_id = '1';
//set into model
model = ContactModel( id : _id , displayName: _name, phoneNumber: _phoneContact);
_data.insertContacts(model);
_list.add(model);
setState(() {
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
RaisedButton(
onPressed: () {
printDB();
},
child: Text('show Db contacts'),
),
RaisedButton(
onPressed: () {
pickContact();
},
child: Text('select contacts'),
),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: _list.length,
itemBuilder: (context, position) {
return ListTile(
leading: Icon(Icons.contacts),
title: Text(
_list[position].displayName.toString()
),
trailing: Icon(Icons.delete));
},
),
)
// Text(_phoneContact ?? "contact not selected" ),
// Text(_name ?? "name not selected" ),]
],
));
}
}
my contact model class
import 'package:flutter/cupertino.dart';
class ContactModel {
String? id = 'id';
String? displayName = 'name';
String? phoneNumber = 'number';
ContactModel({ this.id, this.displayName, this.phoneNumber});
//setters
set contactName(String? dispName) {
displayName = dispName;
}
set contactNumber(String? num)
{
phoneNumber = num;
}
//getters
String? get contactName{ return displayName;}
String? get contactNumber{ return phoneNumber;}
// Convert a Dog into a Map. The keys must correspond to the names of the
// columns in the database.
Map<String, dynamic> toMap() {
return {
'id': id,
'name': displayName,
'number': phoneNumber,
};
}
factory ContactModel.fromMap(Map<String, dynamic> json) => new ContactModel(
id: json["id"],
displayName: json["name"],
phoneNumber: json["number"],
);
// Implement toString to make it easier to see information about
// each dog when using the print statement.
#override
String toString() {
return 'Dog{id: $id, name: $displayName, age: $phoneNumber}';
}
}//class ends

It should be the following:
await (await _data).getcontacts();
...
await (await _data).insertContacts(model);
This is because _data is an async property and needs an await to get the value out of Future. Also, getcontacts/insertContacts is async and needs another await.
Maybe it'd be nice to get the DB out of _data in another variable like:
final db = await _data;
...
await db.getcontacts();
...
await db.insertContacts(model);

Related

how this two classes works and how to fetch data using it

i'm a beginner in flutter and firebase. i have a school project.
we start coding using providers at class..
the project has two services one is db and the other is global:
the db page looks like this
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:rxdart/rxdart.dart';
import './service.dart';
//t list of object api class Collection<T> { final FirebaseFirestore
_db = FirebaseFirestore.instance; final String? path; CollectionReference? ref;
Collection({this.path}) {
ref = _db.collection(path!); }
Future<List<T>> getData() async {
var snapshots = await ref!.get().then((snapshot) =>
snapshot.docs.map((doc) => Global.models[T](doc.data()) as T).toList());
return snapshots; }
Stream<List<T>> streamData() {
return ref!.snapshots().map((list) =>
list.docs.map((doc) => Global.models[T](doc.data()) as T).toList()); } }
class UserData<T> { final FirebaseFirestore _db = FirebaseFirestore.instance; final FirebaseAuth _auth = FirebaseAuth.instance; final String? collection;
UserData({this.collection});
Stream<T> get documentStream {
return _auth.authStateChanges().switchMap((user) {
if (user != null) {
Document<T> doc = Document<T>(path: '$collection/${user.uid}');
return doc.streamData();
} else {
return Stream<T>.value(null!);
}
}); }
Future<T> getDocument() async {
User? user = _auth.currentUser;
if (user != null) {
Document<T> doc = Document<T>(path: '$collection/${user.uid}');
return doc.getData();
} else {
return null!;
} }
Future<void> upsert(Map data) async {
User? user = _auth.currentUser;
Document<T> ref = Document<T>(path: '$collection/${user!.uid}');
return ref.upsert(data); } }
class Document<T> { final FirebaseFirestore _db = FirebaseFirestore.instance; final String? path; DocumentReference? ref;
Document({this.path}) {
ref = _db.doc(path!); }
Future<T> getData() {
return ref!.get().then((value) => Global.models[T](value.data()) as T); }
Stream<T> streamData() {
return ref!.snapshots().map((v) => Global.models[T](v.data) as T); }
Future<void> upsert(Map data) {
return ref!.set(Map<String, dynamic>.from(data), SetOptions(merge: true)); } }
and this is the code of the global :
import 'package:flutter_application_3/services/service.dart';
// Static global state. Immutabel services that do not care about
build context. class Global { //App Data static final String?
title = 'application de reclamation';
//Data Models static final Map models = {
UserInformation: (data) => UserInformation.fromMap(data),
LocationReclamation: (data) => LocationReclamation.fromMap(data), };
// Firestore reference for writes. static final
UserData<UserInformation> userInfo =
UserData<UserInformation>(collection: 'user');
static final Collection<LocationReclamation> reclamationLocation =
Collection<LocationReclamation>(path: 'reclamation'); }
this is the main
import 'package:flutter_application_3/screens/screens.dart'; import 'package:flutter_application_3/services/service.dart'; import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:provider/provider.dart';
Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(MyApp()); }
class MyApp extends StatelessWidget { #override Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<UserInformation>.value(
value: Global.userInfo.getDocument().asStream(),
initialData: UserInformation.initialData(),
),
StreamProvider<List<LocationReclamation>>.value(
value: Global.reclamationLocation.getData().asStream(),
initialData: []),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
brightness: Brightness.light,
primaryColor: Colors.white,
accentColor: Colors.white,
// cardColor: Colors.white,
fontFamily: 'NotoSansJP'),
// home: AuthenticationPage(),
// Named Routes
routes: {
'/': (context) => AuthenticationPage(),
'/ReclamationReported': (context) => ReclamationReported(),
'/authentication': (context) => AuthenticationPage(),
'/affichage': (context) => LocationReclamationScreen(),
},
),
); } }
I want to know how to work with this... i just want to fetch data from the reclamation class and I tried so hard to do it
this is relamation model
class LocationReclamation { final String? latitude; final String? longitude; final String? description; final int? reportNumber; final List? img;
LocationReclamation(
{this.latitude,
this.longitude,
this.reportNumber,
this.img,
this.description});
factory LocationReclamation.fromMap(Map data) {
return LocationReclamation(
latitude: data['latitude'] ?? '',
longitude: data['longitude'] ?? '',
description: data['description'] ?? '',
reportNumber: data['reportNumber'] ?? '',
img: data['img'] ?? 'default.png',
); }

Flutter shared_preferences save list<any_class>

I tried to save a list to my shared preferences but the list isn't a String list it is a list of a special type class "todo". I tried it with ".cast<Todo>();", this worked with prefs.getStringList.... but dont work with prefs.setStringList.
Here is a screenshot:
When I try to do prefs.setStringList("shoppingCard", _todos); it says: "The argument type 'List' can't be assigned to the parameter type 'List'."
This is the source code of the class todo:
class Todo {
Todo({required this.name, required this.checked});
final String name;
bool checked;
}
class TodoItem extends StatelessWidget {
TodoItem({
required this.todo,
required this.onTap,
}) : super(key: ObjectKey(todo));
final Todo todo;
final Function onTap;
TextStyle? _getTextStyle(bool checked) {
if (!checked) return null;
return const TextStyle(
color: Colors.black54,
decoration: TextDecoration.lineThrough,
);
}
#override
Widget build(BuildContext context) {
return ListTile(
onTap: () {
onTap(todo);
},
leading: CircleAvatar(
child: Text(todo.name[0]),
),
title: Text(todo.name, style: _getTextStyle(todo.checked)),
);
}
}
If you need to save list of custom class you need convert it to string. First change your class model to this:
class Todo {
Todo({required this.name, required this.checked});
final String name;
bool checked;
static Todo fromJson(Map<String, dynamic> json) {
return Todo(name: json['name'], checked: json['checked']);
}
Map<String, dynamic> toJson() {
return {
'name': name,
'checked': checked,
};
}
}
then for saving your list in SharedPreferences, try this:
var prefs = await SharedPreferences.getInstance();
prefs.setString('shoppingCard',
jsonEncode({'data': _todos.map((e) => e.toJson()).toList()}));
and for getting it, try this:
var prefs = await SharedPreferences.getInstance();
String? str = prefs.getString('shoppingCard');
if (str != null) {
var result = (jsonDecode(str) as Map)['data'] as List;
result.map((e) => Todo.fromJson(e)).toList();
}
for example in you case, lets say we have list below:
List<Todo> _todos = [
Todo(checked: false, name: 'test1'),
Todo(checked: true, name: 'test2')
];
we add this list to SharedPreferences, like this:
Future<void> _addTodoItem(String name) async {
var prefs = await SharedPreferences.getInstance();
prefs.setString('shoppingCard',
jsonEncode({'data': _todos.map((e) => e.toJson()).toList()}));
_textFieldController.clear();
}
and we get list from SharedPreferences, like this:
Future<void> _getodoItem(String name) async {
var prefs = await SharedPreferences.getInstance();
var value = prefs.getString('shoppingCard');
if (value != null) {
var result = (jsonDecode(value) as Map)['data'] as List;
setState(() {
_todos = result.map((e) => Todo.fromJson(e)).toList();
});
}
}

Retrieve Data from Realtime Database in Flutter

I want to retrieve data from a realtime database for a flutter application. My data is built like this.
I need to loop through this data to display it on the application (ecommerce app, obviously). I have tried and failed in many ways. Currently when trying to get the data I see "Instance of '_Future'" as the message.
class Cart extends StatefulWidget {
Cart({Key? key}) : super(key: key);
#override
State<Cart> createState() => _CartState();
}
class _CartState extends State<Cart> {
DatabaseReference ref = FirebaseDatabase.instance.ref();
Object? products;
List productList = [];
String displayText = 'Results go here!';
snapshot() async {
final snapshot = await ref.child('Products').get();
productList = [];
if (snapshot.exists) {
productList.add(snapshot.value);
products = (snapshot.value);
print(snapshot);
print(snapshot.value);
} else {
print('No Data Available');
}
}
#override
void initState() {
super.initState();
snapshot();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: const PreferredSize(
preferredSize: Size.fromHeight(60), child: MyAppBar()),
body: Column(
children: [
ElevatedButton(
onPressed: () async {
// await ref.set({"name": "Tyler"});
snapshot();
},
child: Text("Add Data"),
),
Text("${snapshot()}", style: TextStyle(color: Colors.white))
],
)
);
}
}
I also have this data class built from other posts I have seen. I have to admit, I am not entirely sure how to use it.
import 'dart:convert';
class ProductData {
final int productID;
final String productCategory;
final String productDesc;
final String productName;
final String productPrice;
final String productSize;
final bool productInStock;
final String productImage1;
final String productGender;
final String productImage2;
ProductData(
{required this.productID,
required this.productCategory,
required this.productDesc,
required this.productName,
required this.productPrice,
required this.productSize,
required this.productInStock,
required this.productImage1,
required this.productGender,
required this.productImage2});
ProductData copyWith(
{int? productID,
String? productCategory,
String? productDesc,
String? productName,
String? productPrice,
String? productSize,
bool? productInStock,
String? productImage1,
String? productGender,
String? productImage2}) {
return ProductData(
productID: productID ?? this.productID,
productCategory: productCategory ?? this.productCategory,
productDesc: productDesc ?? this.productDesc,
productName: productName ?? this.productName,
productPrice: productPrice ?? this.productPrice,
productSize: productSize ?? this.productSize,
productInStock: productInStock ?? this.productInStock,
productImage1: productImage1 ?? this.productImage1,
productGender: productGender ?? this.productGender,
productImage2: productImage2 ?? this.productImage2,
);
}
Map<String, dynamic> toMap() {
return <String, dynamic>{
'productID': productID,
'productCategory': productCategory,
'productDesc': productDesc,
'productName': productName,
'productPrice': productPrice,
'productSize': productSize,
'productInStock': productInStock,
'productImage1': productImage1,
'productGender': productGender,
'productImage2': productImage2,
};
}
factory ProductData.fromMap(Map<String, dynamic> map) {
return ProductData(
productID: map['productID'] as int,
productCategory: map['productCategory'] as String,
productDesc: map['productDesc'] as String,
productName: map['productName'] as String,
productPrice: map['productPrice'] as String,
productSize: map['productSize'] as String,
productInStock: map['productInStock'] as bool,
productImage1: map['productImage1'] as String,
productGender: map['productGender'] as String,
productImage2: map['productImage2'] as String,
);
}
String toJson() => json.encode(toMap());
factory ProductData.fromJson(String source) =>
ProductData.fromMap(json.decode(source) as Map<String, dynamic>);
#override
String toString() {
return 'ProductData(productID: $productID, productCategory: $productCategory, productDesc: $productDesc, productName: $productName, productPrice: $productPrice, productSize: $productSize, productInStock: $productInStock, productImage11: $productImage1, productGender: $productGender, productImage2: $productImage2)';
}
#override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is ProductData &&
other.productID == productID &&
other.productCategory == productCategory &&
other.productDesc == productDesc &&
other.productName == productName &&
other.productPrice == productPrice &&
other.productSize == productSize &&
other.productInStock == productInStock &&
other.productImage1 == productImage1 &&
other.productGender == productGender &&
other.productImage2 == productImage2;
}
#override
int get hashCode {
return productID.hashCode ^
productCategory.hashCode ^
productDesc.hashCode ^
productName.hashCode ^
productPrice.hashCode ^
productSize.hashCode ^
productInStock.hashCode ^
productImage1.hashCode ^
productGender.hashCode ^
productImage2.hashCode;
}
}
Since the data is loaded from Firebase asynchronously, its get() method returns a Future. That's also why you had to declare your snapshot() function as async, which means that you also return a Future.
On its own the rendering code doesn't know anything about Futures, so it renders it by calling its toString() method, which leads to the output you see:
Instance of '_Future'
What you want instead is to wait for the future to resolve, which is just a fancy way of saying that you want to wait for the data to load. An easy way to do that is to use a FutureBuilder, which handles the asynchronous nature of a Future and all possible states it can be in.
That'd look something like:
snapshot() async {
final snapshot = await ref.child('Products').get();
productList = [];
if (snapshot.exists) {
productList.add(snapshot.value);
products = (snapshot.value);
} else {
print('No Data Available');
}
return productList;
}
body: Column(
children: [
ElevatedButton(
onPressed: () async {
snapshot();
},
child: Text("Add Data"),
),
FutureBuilder(
future: snapshot(),
builder: (BuildContext context, AsyncSnapshot asyncSnapshot) {
if (snapshot.hasData) {
var productList = asyncSnapshot.data! as List;
return Text(productList.length.toString());
} else if (snapshot.hasError) {
return Text('Error: ${asyncSnapshot.error}');
} else {
return CircularProgressIndicator(),
}
}
)
],
)

fetch data by using flutter http request and load more data on scroll down the screen

i fetch data from server using flutter http request and load more data when user scroll to bottom of screen. i receive this error "Unhandled Exception: type 'List' is not a subtype of type 'Product'". Please help, i struggle all day without success.
model.dart file
class Product {
final int id;
final String accountName,
callNumber,
whatsappNumber,
businessLocation,
caption;
final List<Images> productPhoto;
Product({
this.id,
this.accountName,
this.callNumber,
this.whatsappNumber,
this.businessLocation,
this.caption,
this.productPhoto,
});
// this is static method
factory Product.fromJson(Map<String, dynamic> json) {
return Product(
id: json['id'],
accountName: json['account_name'],
callNumber: json['call_number'],
whatsappNumber:
json['whatsapp_number'] != null ? json['whatsapp_number'] : null,
businessLocation: json['business_location'],
caption: json['caption'],
productPhoto:
(json['post_photos'] as List).map((i) => Images.fromJson(i)).toList(),
);
}
}
class Images {
final String filename;
Images({this.filename});
factory Images.fromJson(Map<String, dynamic> json) {
return Images(
filename: json['filename'],
);
}
}
explore.dart file (i import models.dart to this file)
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:windowshoppi/models/global.dart';
import 'package:windowshoppi/models/product.dart';
import 'package:http/http.dart' as http;
class Explore extends StatefulWidget {
#override
_ExploreState createState() => _ExploreState();
}
class _ExploreState extends State<Explore> {
ScrollController _scrollController = ScrollController();
List<Product> data;
String nextUrl;
#override
void initState() {
// TODO: implement initState
super.initState();
this.fetchProduct(http.Client(), ALL_PRODUCT_URL);
_scrollController.addListener(() {
// print(_scrollController.position.pixels);
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
if (nextUrl != null) {
this.fetchProduct(http.Client(), nextUrl);
}
// print(nextUrl);
}
});
}
Future<List<Product>> fetchProduct(http.Client client, url) async {
final response = await client.get(url);
if (response.statusCode == 200) {
Map<String, dynamic> mapResponse = json.decode(response.body);
nextUrl = mapResponse['next'];
if (mapResponse["count"] != "") {
final products = mapResponse["results"].cast<Map<String, dynamic>>();
final listOfProducts = await products.map<Product>((json) {
return Product.fromJson(json);
}).toList();
// return listOfProducts;
setState(() {
data.add(listOfProducts);
});
} else {
return [];
}
} else {
throw Exception('failed to load data from internet');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('http get'),
),
body: ListView.builder(
controller: _scrollController,
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Container(
height: 200,
color: Colors.blue,
child: Text(data[index].caption),
),
);
},
),
);
}
}
Have a look at this part of the code.
final listOfProducts = await products.map<Product>((json) {
return Product.fromJson(json);
}).toList();
In the .map() method you are casting it to type < Product >. So judging by the error you have mentioned, "Unhandled Exception: type 'List' is not a subtype of type Product"
I think the json data being returned contains a List, instead of the product fields. I would highly recommend you to once check the json data being returned, and double-check if you are targeting the correct JSON tree nodes.
Let me know if this solved the issue.

How to load data on app start from file with Flutter and sqflite?

I have a whole list of data I want to load into my app when it starts up. I was able to extract into a JSON file so I can load it into my table, but it's not quite loading correctly and also seems to get added multiple times. It looks like I am loading in the file correctly via assets (printing the values in _loadIngredients gives me the right results). But when it is saved into the DB, it just gets saved as an integer and that is what shows up in the view.
I could not find a good example of this anywhere.
Where am I going wrong?
In my DatabaseHandler:
class DatabaseHandler{
DatabaseHandler._();
static final DatabaseHandler db = DatabaseHandler._();
static DatabaseHandler get() {
return db;
}
static Database _database;
final databaseName = "recipe.db";
DatabaseHandler._createInstance();
Future<Database> get database async {
if (_database != null)
return _database;
_database = await initDB();
return _database;
}
initDB() async {
var path = await getDatabasesPath();
print("Creating tables at path: $path");
var dbPath = join(path, 'recipe.db');
Database dbConnection = await openDatabase(dbPath, version: 1,
onCreate: (Database db, int version) async {
return db.execute(
"CREATE TABLE ingredients(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)",
);
}
);
await _loadIngredients(dbConnection);
return dbConnection;
}
_loadIngredients(Database db) async {
Batch batch = db.batch();
List<Map<String, dynamic>> records = await db.query('ingredients');
print(records);
String ingredientsJson = await rootBundle.loadString('assets/json/ingredients.json');
List ingredientsList = json.decode(ingredientsJson);
ingredientsList.forEach((val) {
print(val);
Ingredient ingredient = Ingredient.fromMap(val);
batch.insert("ingredients", ingredient.toMap(false));
});
var results = await batch.commit();
}
}
My ingredient class:
class Ingredient {
int id;
String name;
String categoryName;
DateTime dateCreated;
Ingredient(this.id, this.name, this.categoryName, this.dateCreated);
Map<String, dynamic> toMap(bool forUpdate) {
if(dateCreated == null) {
dateCreated = new DateTime.now();
}
var data = {
// 'id': id, since id is auto incremented in the database we don't need to send it to the insert query.
'name': utf8.encode(name),
'category_name': utf8.encode(categoryName),
'date_created': epochFromDate( dateCreated )
};
if(forUpdate){
data["id"] = this.id;
}
return data;
}
Ingredient.fromMap(Map map){
id = map["id"];
name = map["name"];
categoryName = map["category_name"];
dateCreated = map["date_created"];
}
// Converting the date time object into int representing seconds passed after midnight 1st Jan, 1970 UTC
int epochFromDate(DateTime dt) {
return dt.millisecondsSinceEpoch ~/ 1000 ;
}
// overriding toString() of the note class to print a better debug description of this custom class
#override toString() {
return {
'id': id,
'name': name,
'category_name': categoryName,
'date_created': epochFromDate( dateCreated )
}.toString();
}
}
My homepage class where I am initializing my DB:
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<Map<String, dynamic>> _allIngredientsInQueryResult = [];
var notesViewType ;
#override void initState() {
super.initState();
notesViewType = viewType.Staggered;
DatabaseHandler.db.initDB();
retrieveAllIngredientsFromDatabase();
}
#override
Widget build(BuildContext context) {
return
Container(
child: _ingredientList()
);
}
Widget _ingredientList() {
return Container(
child: ListView.separated(
padding: const EdgeInsets.all(8),
itemCount: _allIngredientsInQueryResult.length,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 50,
color: Colors.amber[100],
child: Center(child: Text('Entry ${_allIngredientsInQueryResult[index]["name"]}')),
);
},
separatorBuilder: (BuildContext context, int index) => const Divider(),
)
);
}
retrieveAllIngredientsFromDatabase() async {
var _testData = await DatabaseHandler.db.selectAllIngredients();
setState(() {
_allIngredientsInQueryResult = _testData;
});
}
}
Image of what I am seeing in the app:
Ingredients json
if you use utf8.encode(name), you convert your String in bytes something like flour = [102, 108, 111, 117, 114]
and when you display this values you must also set a utf8.decode(map["name"])
in your example something like
Text('Entry ' + utf8.decode(${_allIngredientsInQueryResult[index]["name"]})))
every time your initDB() is calling, the Data comes again in the DB. You can do it only in the onCreate part of the Sqlite DB
initDB() async {
var path = await getDatabasesPath();
print("Creating tables at path: $path");
var dbPath = join(path, 'recipe.db');
Database dbConnection = await openDatabase(dbPath, version: 1,
onCreate: (Database db, int version) async {
await db.execute(
"CREATE TABLE ingredients(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, category_name TEXT, date_created INTEGER)",
);
await _loadIngredients(db);
}
);
return dbConnection;
}
i would also use your model class and not a dynimac map
Define _allIngredientsInQueryResult
List<Ingredient> _allIngredientsInQueryResult = new List();
get selectAllIngredients, with the fromMap()
Future<List<Ingredient>> selectAllIngredients() async {
var dbClient = await database;
List<Map> result = await dbClient.query('ingredients');
List<Ingredient> r_ingredient = result.map((i) => Ingredient.fromMap(i)).toList();
return r_ingredient;
}
set Decode in the fromMap()
Ingredient.fromMap(Map map){
id = map["id"];
name = utf8.decode(map["name"]);
categoryName = utf8.decode(map["category_name"]);
dateCreated = DateTime.fromMillisecondsSinceEpoch(map["date_created"]);
}
get Ingredients
retrieveAllIngredientsFromDatabase() async {
DatabaseHandler.db.selectAllIngredients().then((List<Ingredient> r_ingredient) {
setState(() {
_allIngredientsInQueryResult = r_ingredient;
});
});
}
display in the Listview
Text('Entry ' + _allIngredientsInQueryResult[index].name)