I am building an app in which a user can create multiple projects.
I want each project to have its unique database, since although columns (there will be 6-7 columns) name will be the same, but the values under columns for a new project will be different.
How to create new SQFlite database each time when a user creates a new project?
Or is there some other better way to achieve to achieve this?
Here is my code for the database that i have manually created if you want to go through it:
class DatabaseHelper {
static final DatabaseHelper _instance = new DatabaseHelper.internal();
factory DatabaseHelper() => _instance;
final String tableUser = "userTable";
final String columnId = "id";
final String columnRuns = "runs";
static Database _db;
Future<Database> get db async {
if (_db != null) {
return _db;
}
_db = await initDb();
return _db;
}
DatabaseHelper.internal();
initDb() async {
Directory documentDirectory = await getApplicationDocumentsDirectory();
String path = join(documentDirectory.path,
"maindbnew.db"); //home://directory/files/maindb.db
var ourDb = await openDatabase(path, version: 1, onCreate: _onCreate);
return ourDb;
}
void _onCreate(Database db, int newVersion) async {
await db.execute(
"CREATE TABLE $tableUser($columnId INTEGER PRIMARY KEY, $columnRuns INTEGER)");
}
//CRUD - CREATE, READ, UPDATE, DELETE
//Insertion
Future<int> saveUser(User user) async {
var dbClient = await db;
int res = await dbClient.insert("$tableUser", user.toMap());
return res;
}
//Get Users
Future<List> getAllUsers() async {
var dbClient = await db;
var result = await dbClient.rawQuery("SELECT * FROM $tableUser");
return result.toList();
}
Future<int> getCount() async {
var dbClient = await db;
return Sqflite.firstIntValue(
await dbClient.rawQuery("SELECT COUNT(*) FROM $tableUser"));
}
Future<User> getUser(int id) async {
var dbClient = await db;
var result = await dbClient
.rawQuery("SELECT * FROM $tableUser WHERE $columnId = $id");
if (result.length == 0) return null;
return new User.fromMap(result.first);
}
Future<int> deleteUser(int id) async {
var dbClient = await db;
return await dbClient
.delete(tableUser, where: "$columnId = ?", whereArgs: [id]);
}
Future<int> updateUser(User user) async {
var dbClient = await db;
return await dbClient.update(tableUser, user.toMap(),
where: "$columnId = ?", whereArgs: [user.id]);
}
Future close() async {
var dbClient = await db;
return dbClient.close();
}
}
class User {
int _runs;
int _id;
User(this._runs);
User.map(dynamic obj) {
this._runs = obj['runs'];
this._id = obj['id'];
}
int get runs => _runs;
int get id => _id;
Map<String, dynamic> toMap() {
var map = new Map<String, dynamic>();
map["runs"] = _runs;
if (id != null) {
map["id"] = _id;
}
return map;
}
User.fromMap(Map<String, dynamic> map) {
this._runs = map["runs"];
this._id = map["id"];
}
}
P.S. I just have one column right now, I'll add more later.
Related
I've just implemented some localStorage functions in a class to a song lyrics app I'm developing and I think I'm calling it wrong in some way but not sure how. Either that or something is blocking the async function from completing.
Storage Class
public class Sections
{
readonly string searchesFileName = "RecentSearches.txt";
readonly string songsFileName = "RecentSongs.txt";
readonly string playlistFileName = "Playlists.txt";
IFolder localFolder = FileSystem.Current.LocalStorage;
public async void VerifySectionFiles()
{
ExistenceCheckResult searchFileExists = await localFolder.CheckExistsAsync(searchesFileName);
if (searchFileExists != ExistenceCheckResult.FileExists)
{
await localFolder.CreateFileAsync(searchesFileName, CreationCollisionOption.FailIfExists);
}
ExistenceCheckResult songsFileExists = await localFolder.CheckExistsAsync(songsFileName);
if (songsFileExists != ExistenceCheckResult.FileExists)
{
await localFolder.CreateFileAsync(songsFileName, CreationCollisionOption.FailIfExists);
}
ExistenceCheckResult playlistFileExists = await localFolder.CheckExistsAsync(playlistFileName);
if (playlistFileExists != ExistenceCheckResult.FileExists)
{
await localFolder.CreateFileAsync(playlistFileName, CreationCollisionOption.FailIfExists);
}
}
public async void AddRecentSong(string title, int id, string artist)
{
Song[] recentSongs = await ReadRecentSongsFromFile() ?? new Song[10];
recentSongs[9] = null;
for (int i = 9; i > 0; i--)
{
recentSongs[i] = recentSongs[i - 1];
}
recentSongs[0] = new Song(title, artist, id);
IFile songFile = await localFolder.CreateFileAsync(songsFileName, CreationCollisionOption.OpenIfExists);
string songsJsonString = JsonConvert.SerializeObject(recentSongs);
await songFile.WriteAllTextAsync(songsJsonString);
}
public async Task<Song[]> ReadRecentSongsFromFile()
{
IFile recentSongs = await localFolder.CreateFileAsync(songsFileName, CreationCollisionOption.OpenIfExists).ConfigureAwait(false);
string songsJsonString = await recentSongs.ReadAllTextAsync();
Song[] songsArray = JsonConvert.DeserializeObject<Song[]>(songsJsonString);
return songsArray;
}
public async void AddRecentSearch(string searchTerm)
{
string[] recentSearches = await ReadRecentSearchesFromFile() ?? new string[10];
recentSearches[9] = null;
for (int i = 9; i > 0; i--)
{
recentSearches[i] = recentSearches[i - 1];
}
recentSearches[0] = searchTerm;
IFile songFile = await localFolder.CreateFileAsync(songsFileName, CreationCollisionOption.OpenIfExists);
string songsJsonString = JsonConvert.SerializeObject(recentSearches);
await songFile.WriteAllTextAsync(songsJsonString);
}
public async Task<string[]> ReadRecentSearchesFromFile()
{
IFile recentSearches = await localFolder.CreateFileAsync(searchesFileName, CreationCollisionOption.OpenIfExists);
string searchesJsonString = await recentSearches.ReadAllTextAsync();
string[] searchesArray = JsonConvert.DeserializeObject<string[]>(searchesJsonString);
return searchesArray;
}
public async void CreatePlaylist(Playlist playlist)
{
List<Playlist> playlists = await ReadPlaylistsFromFile()?? new List<Playlist>();
playlists.Add(playlist);
IFile playlistsFile = await localFolder.CreateFileAsync(playlistFileName, CreationCollisionOption.OpenIfExists);
string playlistsJsonString = JsonConvert.SerializeObject(playlists);
await playlistsFile.WriteAllTextAsync(playlistsJsonString);
}
public async void RemovePlaylist(Playlist playlist)
{
List<Playlist> playlists = await ReadPlaylistsFromFile() ?? new List<Playlist>();
Playlist playlistToRemove = playlists.Find(x => x == playlist);
playlists.Remove(playlistToRemove);
IFile playlistsFile = await localFolder.CreateFileAsync(playlistFileName, CreationCollisionOption.OpenIfExists);
string playlistsJsonString = JsonConvert.SerializeObject(playlists);
await playlistsFile.WriteAllTextAsync(playlistsJsonString);
}
public async Task<List<Playlist>> ReadPlaylistsFromFile()
{
IFile playlists = await localFolder.CreateFileAsync(playlistFileName, CreationCollisionOption.OpenIfExists);
string playlistsString = await playlists.ReadAllTextAsync();
List<Playlist> playlistList = JsonConvert.DeserializeObject<List<Playlist>>(playlistsString);
return playlistList;
}
}
And when it comes to the implementation, I've used both this by instantiating the class inside the page I'm using it:
public partial class MainPortrait : ContentView
{
Sections sections = new Sections();
public string[] RecentSearches = new string[10];
public string Search { get; set; }
public MainPortrait()
{
InitializeComponent();
BindingContext= this;
sections.VerifySectionFiles();
RecentSearches = sections.ReadRecentSearchesFromFile().Result;
//Do stuff with returned string[]
}
And this, where I added it to the App.xaml.cs
public partial class MainPortrait : ContentView
{
public string[] RecentSearches = new string[10];
public string Search { get; set; }
public MainPortrait()
{
InitializeComponent();
BindingContext= this;
((App)App.Current).sections.VerifySectionFiles();
RecentSearches = ((App)App.Current).sections.ReadRecentSearchesFromFile().Result;
//Do stuff with returned string[]
and it had the same problem both times, so I'm assuming it's a problem with the function itself. And if that's the case, there is probably a similar problem with all the functions in the Section class.
Any ideas?
I created a class called 'tasks' and implement it. I edit gradle files for connecting with firebase. I gain some errors in my code. So please help me to solve this error.
class _MyHomePageState extends State<MyHomePage> {
late List<Task> items;
FirestoreService fireServ = new FirestoreService();
late StreamSubscription<QuerySnapshot> todoTasks;
#override
void initState() {
super.initState();
items= [];
todoTasks.cancel();
todoTasks=fireServ.getTaskList().listen((QuerySnapshot snapshot){
final List<Task> tasks=snapshot.docs
.map((documentSnapshot) => Task. fromMap(documentSnapshot.data))
.toList();
setState(() {
this.items = tasks;
});
});
}
This is my firestore class service
class FirestoreService {
Future<Task> createTODOTask(String taskname, String taskdetails,String taskdate,String tasktime,String tasktype) async {
final TransactionHandler createTransaction = (Transaction tx) async {
final DocumentSnapshot ds = await tx.get(myCollection.doc());
final Task task = new Task(taskname, taskdetails,taskdate,tasktime,tasktype);
final Map<String, dynamic> data = task.toMap();
await tx.set(ds.reference, data);
return data;
};
return FirebaseFirestore.instance.runTransaction(createTransaction).then((mapData) {
return Task.fromMap(mapData);
}).catchError((error) {
print('error: $error');
return null;
});
}
Stream<QuerySnapshot> getTaskList({int offset=0, int limit=0}) {
Stream<QuerySnapshot> snapshots = myCollection.snapshots();
if (offset != null) {
snapshots = snapshots.skip(offset);
}
if (limit != null) {
snapshots = snapshots.take(limit);
}
return snapshots;
}
}
You need to change:
documentSnapshot.data
to this:
documentSnapshot.data()
.data() is a method and not a property of the DocumentSnapshot object.
Ive created a flutter app for keeping recipes.
Every recipe has a list of ingredient.
I create the recipe using a Recipe model and inside i have a list of ingredient.
By default when i save the above Recipe model to Firebase Firestore the Ingredient list is saved as an array.
I plan on expanding the concepts over time and would like to store the ingredient in a sub collection.
I am obviously able to iterate through the questions and add them as a document to their own collection, however this seems messy and likely to cause me problems in the future.
Is there a way in which i can specify that child models are created as a sub collection rather than an array?
Below is what im using currently to write the data
class FirestoreService {
FirestoreServiceNew._();
static final instance = FirestoreServiceNew._();
Future<void> setData({
#required String path,
#required Map<String, dynamic> data,
bool mergeBool = false,
}) async {
try {
final reference = FirebaseFirestore.instance.doc(path);
print('$path: $data');
await reference.set(data);
} catch (e) {
print('error: $e');
}
}
Future<void> bulkSet({
#required String path,
#required List<Map<String, dynamic>> datas,
bool merge = false,
}) async {
final reference = FirebaseFirestore.instance.doc(path);
final batchSet = FirebaseFirestore.instance.batch();
print('$path: $datas');
}
Future<void> deleteData({#required String path}) async {
final reference = FirebaseFirestore.instance.doc(path);
print('delete: $path');
await reference.delete();
}
Stream<List<T>> collectionStream<T>({
#required String path,
#required T builder(Map<String, dynamic> data, String documentID),
Query queryBuilder(Query query),
int sort(T lhs, T rhs),
}) {
Query query = FirebaseFirestore.instance.collection(path);
if (queryBuilder != null) {
query = queryBuilder(query);
}
final Stream<QuerySnapshot> snapshots = query.snapshots();
return snapshots.map((snapshot) {
final result = snapshot.docs
.map((snapshot) => builder(snapshot.data(), snapshot.id))
.where((value) => value != null)
.toList();
if (sort != null) {
result.sort(sort);
}
return result;
});
}
Stream<List<T>> requestStream<T>({
#required String path,
#required T builder(Map<String, dynamic> data, String documentID),
Query queryBuilder(Query query),
int sort(T lhs, T rhs),
}) {
Query query = FirebaseFirestore.instance.collection(path);
if (queryBuilder != null) {
query = queryBuilder(query);
}
final Stream<QuerySnapshot> snapshots = query.snapshots();
return snapshots.map((snapshot) {
final result = snapshot.docs
.map((snapshot) => builder(snapshot.data(), snapshot.id))
.where((value) => value != null)
.toList();
if (sort != null) {
result.sort(sort);
}
return result;
});
}
Stream<T> documentStream<T>({
#required String path,
#required T builder(Map<String, dynamic> data, String documentID),
}) {
final DocumentReference reference = FirebaseFirestore.instance.doc(path);
final Stream<DocumentSnapshot> snapshots = reference.snapshots();
return snapshots.map((snapshot) => builder(snapshot.data(), snapshot.id));
}
}
and my model
class Recipe {
String id;
List<Ingredient> ingredients;
String title;
String description;
Timestamp createdOn;
Recipe(
{this.id = '',
this.ingredients,
this.title,
this.description,
this.createdOn});
Recipe.fromData(Map<String, dynamic> json, String docid) {
id = docid ?? '';
title = json['title'];
description = json['description'];
createdOn = json['createdOn'];
if (json['questions'] != null) {
ingredients= new List<Ingredients>();
json['ingredients'].forEach((v) {
questions.add(new Ingredients.fromData(v, null));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['description'] = this.description;
data['title'] = this.title;
data['createdOn'] = this.createdOn;
if (this.ingredients != null) {
data['ingredients'] = this.ingredients.map((v) => v.toJson()).toList();
}
return data;
}
}
You can build a path to documents nested in subcollections by alternating calls to collection() and doc(), which return CollectionReference and DocumentReference objects respectively.
FirebaseFirestore.instance
.collection("top-level-collection")
.doc("document-id-1")
.collection("nested-subcollection")
.doc("document-id-2")
Hello i have the following code
class TodoProvider {
Database db;
TodoProvider() {
_initDatabase();
print("Db is >>>>> $db");
}
void _initDatabase() async {
final database = await AppDatabase().connect();
db = database;
}
Future<Todo> insertTodo(Todo todo) async {
todo.id = await db.insert('todo', todo.toMap());
return todo;
}
Future<Todo> getTodo(int id) async {
List<Map<String, dynamic>> maps =
await db.query('todo', where: 'id=?', whereArgs: [id]);
if (maps.length > 0) {
return Todo.fromMap(maps.first);
}
return null;
}
Future<int> deleteTodo(int id) async {
return await db.delete('todo', where: 'id=?', whereArgs: [id]);
}
Future<int> updateTodo(Todo todo) async {
return await db
.update('todo', todo.toMap(), where: 'id=?', whereArgs: [todo.id]);
}
Future close() async => db.close();
}
The AppDatabase is a class that exposes the connect() method which returns a future.
The Goal is to assign the value returned from AppDatabase into the db class variable
Most methods in the TodoProvider class relays on the db class variable to work with the database.
The problem is that class variable db is always null
You code right now depends on the db variable is being set which happens inside an async operation. There are two ways to fix this:
Use static builder method
You can create a private constructor which can be called by a static method which are allowed to return Future<TodoProvider> which the user can await on.
class TodoProvider {
...
TodoProvider._(this.db) {
print("Db is >>>>> $db");
}
static Future<TodoProvider> getInstance() async {
return TodoProvider._(await AppDatabase().connect());
}
...
}
Use the Future as member of the class
This is not the prettiest solution but still a way to do it. Since you are allowed to await on the same Future multiple times, you can just await every time you need to access the Database object.
class TodoProvider {
Future<Database> _db;
TodoProvider() {
_initDatabase();
print("Db is >>>>> $db");
}
void _initDatabase() {
_db = AppDatabase().connect();
}
Future<Todo> insertTodo(Todo todo) async {
final db = await _db;
todo.id = await db.insert('todo', todo.toMap());
return todo;
}
Future<Todo> getTodo(int id) async {
final db = await _db;
List<Map<String, dynamic>> maps =
await db.query('todo', where: 'id=?', whereArgs: [id]);
if (maps.length > 0) {
return Todo.fromMap(maps.first);
}
return null;
}
Future<int> deleteTodo(int id) async {
final db = await _db;
return await db.delete('todo', where: 'id=?', whereArgs: [id]);
}
Future<int> updateTodo(Todo todo) async {
final db = await _db;
return await db
.update('todo', todo.toMap(), where: 'id=?', whereArgs: [todo.id]);
}
Future close() async {
final db = await _db;
db.close();
}
}
I'm having a simple problem which is how to get specific values from database Firebase.
For example, I want to get the value of "name" and put it in text. How can I do that? Can you write a detailed code?
class _HomePageState extends State<HomePage> {
String myuid;
FirebaseUser currentUser;
// To get id
void _loadCurrentUser() {
FirebaseAuth.instance.currentUser().then((FirebaseUser user) {
setState(() { // call setState to rebuild the view
this.currentUser = user;
});
});
}
#override
void initState() {
super.initState();
_loadCurrentUser();
}
#override
Widget build(BuildContext context) {
myuid = currentUser.uid;
var getname;
Future<void> getName() async {
DocumentSnapshot ds =
await Firestore.instance.collection('users').document(myuid).get();
getname = ds.data['name'];
}
Try
String name;
Future<null> getName() async {
DocumentSnapshot document = await Firestore.instance.collection('users').document(FirebaseUser().uid).get();
name = document.data['name']
}
This is how you can get data from the Firestore Database Document once
val docRef = db.collection("users").document("mhPtwy..........")
docRef.get()
.addOnSuccessListener { document ->
if (document != null) {
Log.d(TAG, "DocumentSnapshot data: ${document.data}")
} else {
Log.d(TAG, "No such document")
}
}
.addOnFailureListener { exception ->
Log.d(TAG, "get failed with ", exception)
}
This is a kind of cheeky way to get the data and store it in a variable
var name;
Future<void> getName(){
DocumentSnapshot ds = await
Firestore.instance.collection('users').document(uid).get();
name = ds.data['name']
}
then just throw that in your text field
Text(name);