How to update data in local json file in flutter - arrays

I'm the junior flutter developer, and I would like to seek your support.
Currently, I would need to update data in a local JSON file in Flutter as below.
{"title":"ករណី ពិនិត្យផ្ទៃពោះ",
"lists":[
{
"id":"127",
"faci_code":"20018",
"y_txt":"2022",
"m_txt":"1",
"ind_id":"1",
"qty":"100",
"rec_by":"od123456",
"rec_date":"2022-06-27 13:50:31",
"lock_txt":"0",
"sec_id":"1",
"ind_num":"1",
"ind_eng":"# of ANC 1",
"ind_kh":"ចំនួនស្រ្ដីបានពិនិត្យផ្ទៃពោះលើកទី១ទាំងអស់",
"HFAC_NAME":"Boeng Pram",
"HFAC_NAMEKh":"បឺងប្រាំ",
"OD_CODE":"201",
"OD_NAME":"Thma Koul",
"OD_NAME_KH":"ថ្មគោល",
"PRO_CODE":"2",
"PROVINCE":"Battambang",
"PROVINCE_KH":"បាត់ដំបង"
}]}
I have searched and tried multiple solutions, but I couldn't resolve this issue yet. 
I hope someone can provide a solution in advance.
Regard, thanks.

You can't right files in project folders.
Try local storage.
I used path_provider package in this example for get application directory path to write json file on local storage.
1. Create data class
Note. this data class should match your stored json structure
class DataClass {
String name;
int age;
DataClass(this.name, this.age);
factory DataClass.fromJson(Map<String, dynamic> json) {
return DataClass(json['name'], json['age']);
}
Map<String, dynamic> toJson() {
return {
'name': name,
'age': age,
};
}
}
2.create and update json file
DataClass? updateJson;
void createJson() async {
String data = await rootBundle.loadString('assets/data.json');
Directory appDocDir = await getApplicationDocumentsDirectory();
File jsonFile = File("${appDocDir.path}/data.json");
jsonFile.writeAsStringSync(data);
updateJson = DataClass.fromJson(jsonDecode(data));
setState(() {});
}
void updateJsonFile() async {
Directory appDocDir = await getApplicationDocumentsDirectory();
File jsonFile = File("${appDocDir.path}/data.json");
//update
final jsonAsString = jsonFile.readAsStringSync();
updateJson = DataClass.fromJson(jsonDecode(jsonAsString));
updateJson?.name = "updated name";
updateJson?.age = _counter;
jsonFile.writeAsStringSync(jsonEncode(updateJson?.toJson()));
setState(() {});
}

Related

How can I get firebase data as Querysnapshot?

Im trying to get Array from firebase as a Querysnaoshot from flutter but how can I do that ?
This is the code
class Videos {
final String allhashtagsofeveryvideo;
final String categorie;
final String commentcount;
final String hashtag1;
final String hashtag2;
final String hashtag3;
final String likes;
final String previewimage;
final String profilepic;
final String sharecount;
final String uid;
final String username;
final String videourl;
Videos( {this.allhashtagsofeveryvideo,this.commentcount, this.hashtag1, this.hashtag2, this.hashtag3, this.likes, this.previewimage, this.profilepic, this.sharecount, this.uid, this.videourl, this.categorie, this.username});
}
Videos videosfromsnapshot(DocumentSnapshot snapshot) {
return Videos(
categorie: snapshot.data()['categorie'],
commentcount: snapshot.data()['commentcount'].toString(),
hashtag1: snapshot.data()['hashtag1'],
hashtag2: snapshot.data()['hashtag2'],
hashtag3: snapshot.data()['hashtag3'],
likes:snapshot.data()['likes'].length.toString(),
previewimage: snapshot.data()['previewimage'],
profilepic: snapshot.data()['profilepic'],
sharecount: snapshot.data()['sharecount'].toString(),
uid: snapshot.data()['uid'],
allhashtagsofeveryvideo:snapshot.data()['Hashtagsforallvideos'],
username: snapshot.data()['username'],
videourl: snapshot.data()['videourl'],
);
}
var firestore = FirebaseFirestore.instance;
List<QueryDocumentSnapshot> _allResults = [];
QuerySnapshot snapshots = await firestore.collection('videos').get();
for (var doc in snapshots.docs) {
_allResults.addAll(doc.data()["Hashtagsforallvideos"]);
Maybe I should map over it or something like that ?
So instead of adding it like you seeing I wanna use like that
QuerySnapshot snapshots = await firestore.collection('videos').get();
for (var doc in snapshots.docs) {
....
_allResults= qn.docs
But how can I do this maybe anyone can help
No im getting this error
[VERBOSE-2:ui_dart_state.cc(186)] Unhandled Exception: type 'List<dynamic>' is not a subtype of type 'QueryDocumentSnapshot'
#0 _OpenallsinglehashtagsState.getusers
package:wichtigdenyady/homesearchingall/openalldocs.dart:90
<asynchronous suspension>
And the line is this one
_allResults.addAll(doc.data()["Hashtagsforallvideos"]);
Probably something like that
var firestore = FirebaseFirestore.instance;
QuerySnapshot qn = await firestore.collection('videos').get();
if (!mounted) return;
setState(() {
_allResults = qn.docs;
});
It's a little bit hard withot the data but I hope this can help you:
Future<List<QueryDocumentSnapshot>> getVideos() async {
List<String> allVideoHastags = [];
List<QueryDocumentSnapshot> _allResults = [];
QuerySnapshot snapshots = await _firestore.collection('videos').get();
for (QueryDocumentSnapshot videoSnapshot in snapshots.docs) {
List<String> videoHastags =
List.from(videoSnapshot.data()['Hashtagsforallvideos']);
allVideoHastags.addAll(videoHastags);
_allResults.add(videoSnapshot);
}
return _allResults;
}
In most cases we miss the List.from constructor when we work with Firestore arrays and need them in a List.

How to save nested list with sqflite in flutter?

Here I want to add bookmark functionality in my flutter news application. I get data from APIs. I display that data below.
This Image shows you how I get data from APIs
I am using this snippet for saving data with SQflite which I display below. I save this file with name bookmark_db_provider.dart.
import 'dart:io';
import 'home_screen_data.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
class DBProvider {
static Database _database;
static final DBProvider db = DBProvider._();
DBProvider._();
Future<Database> get database async {
if (_database != null) _database = await initDB();
return _database;
}
initDB() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
final path = join(documentsDirectory.path, 'ProductData.db');
return await openDatabase(path, version: 1, onOpen: (db) {},
onCreate: (Database db, int version) async {
await db.execute('CREATE TABLE ProductData('
'id INTEGER PRIMARY KEY,' //id
'categoryName Text,' //headline
'publisherName Text,' //description
'isAvailable Text,' //content
'categoryImgUrl Text' //image
')');
});
}
createProductData(ProductData productData) async {
await deleteAllProductData();
final db = await database;
final res = await db.insert('ProductData', productData.toJson());
return res;
}
Future<int> deleteAllProductData() async {
final db = await database;
final res = await db.delete('DELETE FROM ProductData');
return res;
}
Future<List<ProductData>> getProductDataList() async {
final db = await database;
final res = await db.rawQuery("SELECT * FROM ProductData");
List<ProductData> list = res.isNotEmpty ? res.map((x) => ProductData.fromJson(x)).toList() : [];
return list;
}
}
So, I want to how to save data and get data this with SQflite database in flutter. How I accomplish this?
This is an old question and hopes you have gotten an answer to it. However, a lot of flutter dev using sqflite struggles with handling data in a nested array format of the type mentioned e.g
``` {
"id": 1,
"name": "xyz",
"images": [
{
"imageId": 1,
"image": "image1"
},
{
"imageId": 2,
"image": "image2"
}
]
}
```
Since json handling is not part of sqflite at the moment, it is suggested to either;
a., save inner array as a string/text field in the 'image' column of table 'data' like
**
"[{"imageId": 1, "image": 'image1'}, {"imageId": 2, "image":
'image2'},}"
** , no guaranty.
or, b., flatten out the inner array so as to have only **
data[id, name, image1, image2, image3,...].
** this approach may be possible in a simple array as given but in a complex system. flattening out may be really cumbersome.
my suggestion, create two tables, one for data, and another for images. let each row of images table have reference or relationship with corresponding data table row. Your data class and images class will be something like,
```
class Data {
int dataId;
String name;
List<Image> images;
data({this.id, this.images, this.name});
...
```
and
```
class Image {
int imageId;
int dataId;
String image;
Image({this.imageId, this.dataId, this.image});
...
}
```
Your sqflite table data will have only two fields 'dataId' and 'name' and the image table must include 'dataId' as the relationship between the two tables.
to save data, you can use transactions like
```
void saveData(Data data, Map<String, Object> map) async {
await db.execute(""" INSERT INTO "data" (name) values (?) """, [data.name]);
// retrieve dataId of the new row inserted. last_inserted_rowid can also be used if the database does not contain several tables that may have been updated or saved before completing the transaction.
int dataId;
List<Map> x = await db.query('data', columns: ['dataId'], where: 'name = ?', whereArgs: [data.name]);
dataId = x[x.length - 1]['dataId'];
db.transaction((txn) async {
var batch = txn.batch();
data.images
.map((e) => {
batch.rawInsert(
""" INSERT INTO "images" (dataId,image,) values (?,?,?,? ) """, [dataId, e.image])
}).toList();
});
}
```
to retrieve data and images, you can try something like
```
Data _data = new Data();
Future<void> fetchData() async {
if (db != null) {
// do not execute if db is not instantiate
//retrieve all data from data table and store in instantiated, also instantiate images array as empty list
final dataList = await db.query(data);
_data = (dataList as List)
.map(
(data) => Data(
dataId: data['dataId'],
name: data['name'],
images: data['images'] != null ? data['images'] : []
)) .toList();
_data.forEach((data) async {if (data.images != null) {
List<Image> _images = [];
var imageResults = await db.query(images,where: 'dataId =?', whereArgs: [data.dataId]);
_images = (imageResults as List).map((e) => Image(
imageId: e['imageId'],
dataId: e['dataId'],
image: e['image']
)).toList();
_data.images.addAll(_images);
} });
}
}
```
with that approach, you should be able to handle nested array in flutter and sqflite
I might be unclear with your question, but according to what I understood,
You need to call the method of this provider with data that you want:
DBProvider.init() // Use this only one which when the application is instaled
After that, you can call these methods from anywhere to put and get data
DBProvider.createProductData(productData) //insert data
Get data
DBProvider.getProductDataList() //get data

'ControllerBase.File(byte[], string)' is a method, which is not valid in the given context (CS0119) - in method

I am trying to create an app where user can upload a text file, and gets the altered text back.
I am using React as FE and ASP.NET Core for BE and Azure storage for the database storage.
This is how my HomeController looks like.
I created a separate "UploadToBlob" method, to post the data
public class HomeController : Controller
{
private readonly IConfiguration _configuration;
public HomeController(IConfiguration Configuration)
{
_configuration = Configuration;
}
public IActionResult Index()
{
return View();
}
[HttpPost("UploadFiles")]
//OPTION B: Uncomment to set a specified upload file limit
[RequestSizeLimit(40000000)]
public async Task<IActionResult> Post(List<IFormFile> files)
{
var uploadSuccess = false;
string uploadedUri = null;
foreach (var formFile in files)
{
if (formFile.Length <= 0)
{
continue;
}
// read directly from stream for blob upload
using (var stream = formFile.OpenReadStream())
{
// Open the file and upload its data
(uploadSuccess, uploadedUri) = await UploadToBlob(formFile.FileName, null, stream);
}
}
if (uploadSuccess)
{
//return the data to the view, which is react display text component.
return View("DisplayText");
}
else
{
//create an error component to show there was some error while uploading
return View("UploadError");
}
}
private async Task<(bool uploadSuccess, string uploadedUri)> UploadToBlob(string fileName, object p, Stream stream)
{
if (stream is null)
{
try
{
string connectionString = Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION_STRING");
// Create a BlobServiceClient object which will be used to create a container client
BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);
//Create a unique name for the container
string containerName = "textdata" + Guid.NewGuid().ToString();
// Create the container and return a container client object
BlobContainerClient containerClient = await blobServiceClient.CreateBlobContainerAsync(containerName);
string localPath = "./data/";
string textFileName = "textdata" + Guid.NewGuid().ToString() + ".txt";
string localFilePath = Path.Combine(localPath, textFileName);
// Get a reference to a blob
BlobClient blobClient = containerClient.GetBlobClient(textFileName);
Console.WriteLine("Uploading to Blob storage as blob:\n\t {0}\n", blobClient.Uri);
FileStream uploadFileStream = File.OpenRead(localFilePath);
await blobClient.UploadAsync(uploadFileStream, true);
uploadFileStream.Close();
}
catch (StorageException)
{
return (false, null);
}
finally
{
// Clean up resources, e.g. blob container
//if (blobClient != null)
//{
// await blobClient.DeleteIfExistsAsync();
//}
}
}
else
{
return (false, null);
}
}
}
but the console throws errors, saying "'ControllerBase.File(byte[], string)' is a method, which is not valid in the given context (CS0119)"
And because of this error, another error follows "'HomeController.UploadToBlob(string, object, Stream)': not all code paths return a value (CS0161)"
my questions are
Is it a better idea to create a separate method like I did?
how can I resolve the issue regarding the "File" being valid inside of the UploadToBlob method?
If I want to add the file type validation, where should it happen? t.ex. only text file is alid
If I want to read the text string from the uploaded text file, where should I call the
string contents = blob.DownloadTextAsync().Result;
return contents;
How can I pass down the "contents" to my react component? something like this?
useEffect(() => {
fetch('Home')
.then(response => response.json())
.then(data => {
setForcasts(data)
})
}, [])
Thanks for helping this super newbie with ASP.NET Core!
1) It is ok to put uploading into separate method, it could also be put into a separate class for handling blob operations
2) File is the name of one of the controllers methods, if you want to reference the File class from System.IO namespace, you need to fully qualify the name
FileStream uploadFileStream = System.IO.File.OpenRead(localFilePath);
To the other compile error, you need to return something from the UploadToBlob method, now it does not return anything from the try block
3) File type validation can be put into the controller action method
4) it depends on what you plan to do with the text and how are you going to use it. Would it be a new action of the controller (a new API endpoint)?
5) you could create a new API endpoint for downloading files
UPDATE:
For word replacement you could use a similar method:
private Stream FindMostFrequentWordAndReplaceIt(Stream inputStream)
{
using (var sr = new StreamReader(inputStream, Encoding.UTF8)) // what is the encoding of the text?
{
var allText = sr.ReadToEnd(); // read all text into memory
// TODO: Find most frequent word in allText
// replace the word allText.Replace(oldValue, newValue, stringComparison)
var resultText = allText.Replace(...);
var result = new MemoryStream();
using (var sw = new StreamWriter(result))
{
sw.Write(resultText);
}
result.Position = 0;
return result;
}
}
it would be used in your Post method this way:
using (var stream = formFile.OpenReadStream())
{
var streamWithReplacement = FindMostFrequentWordAndReplaceIt(stream);
// Upload the replaced text:
(uploadSuccess, uploadedUri) = await UploadToBlob(formFile.FileName, null, streamWithReplacement);
}
You probably have this method inside MVC controller in which File method exists. Add in your code System.IO.File instead of File

I get no such table when I run this method. I have my db in my asset which I copy to the file directory and I try to read from the file directory

This method is supposed to load a list of of items from my query. I copy my database (which is in my assets folder) to the flutter directory and when I try to read from the directory it gives a "No such table" error
`Future<<"List<<"Model>> model(String searchParam) async {
var dbDir = await getDatabasesPath();
var dbPath = join(dbDir, "app.db");
var exists = await databaseExists(dbPath);
if (!exists) {
// Should happen only the first time you launch your application
print("Creating new copy from asset");
// Create the writable database file from the bundled demo database file:
ByteData data = await rootBundle.load("assets/mydb.db");
List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
await File(dbPath).writeAsBytes(bytes);
}
print("Opening existing db");
var db = await openDatabase(dbPath);
print(db.isOpen);
// Get a reference to the database.
// final Database db = await database;
// Query the table for all The Hymns.
final List<Map<String, dynamic>> maps = await db.rawQuery("SELECT * FROM Test WHERE title LIKE '%$searchParam%' OR id LIKE '%$searchParam%'");
// final List<Map<String, dynamic>> maps = await db.query('hymns');
print(maps.length);
// Convert the List<Map<String, dynamic> into a List<Dog>.
return List.generate(maps.length, (i) {
return Model(
id: maps[i]['id'],
title: maps[i]['title'],
text: maps[i]['text'],
favorite: maps[i]['favorite'],
);
});
}
`

How to create multiple table in Sqlite database at different times in Flutter

So Initially I created a table named "TABLE" in the database. I wanted to add another table. So I added another query to create a table. However I get an error saying "TABLE1 does not exist" when I run the app.
I do feel like there is a flaw in my code. I think the _onCreate() method will only be called the first time I run the app. So any code I add on _onCreate() method afterwards will not run. Any help will be appreciated.
class DBHelper {
static Database _db;
static const String DB_NAME = 'employeeDB';
static const String ID = 'id';
static const String NAME = 'name';
static const String TABLE = 'Employee';
static const String ID1 = 'id1';
static const String NAME1 = 'name1';
static const String TABLE1 = 'Employee1';
Future<Database> get db async {
if (_db != null) {
return _db;
}
_db = await initDb();
return _db;
}
initDb() async {
io.Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, DB_NAME);
var db = await openDatabase(path, version: 1, onCreate: _onCreate);
return db;
}
_onCreate(Database db, int version) async {
await db.execute("CREATE TABLE $TABLE ($ID INTEGER PRIMARY KEY,$NAME TEXT)");
await db.execute("CREATE TABLE $TABLE1 ($ID1 INTEGER PRIMARY KEY,$NAME1 TEXT)");
}
Future<Employee> save(Employee employee) async {
var dbClient = await db;
employee.id = await dbClient.insert(TABLE, employee.toMap());
return employee;
}
Future<Employee1> saveEmp1(Employee1 employee) async {
var dbClient = await db;
employee.id = await dbClient.insert(TABLE1, employee.toMap());
return employee;
}
Future<List<Employee>> getEmployees() async {
var dbClient = await db;
List<Map> maps = await dbClient.query(TABLE, columns: [ID, NAME]);
List<Employee> employees = [];
if (maps.length > 0) {
for (int i = 0; i < maps.length; i++) {
employees.add(Employee.fromMap(maps[i]));
}
}
return employees;
}
Future<List<Employee1>> getEmployees1() async {
var dbClient = await db;
List<Map> maps = await dbClient.query(TABLE1, columns: [ID1,
NAME1]);
List<Employee1> employees = [];
if (maps.length > 0) {
for (int i = 0; i < maps.length; i++) {
employees.add(Employee.fromMap(maps[i]));
}
}
return employees;
}
}
The first time run this app and initDb() in emulator, the db file employeeDB has created.
and it will not be created again
For only test app execution,
you can change
String DB_NAME = 'employeeDB'
to another name
String DB_NAME = 'employeeDB1'
or you can uninstall this app from Emulator first then run it again.
source code snippet of https://github.com/tekartik/sqflite/blob/master/sqflite/lib/sqlite_api.dart
/// Called when the database is created.
OnDatabaseCreateFn onCreate;
For schema migration, you can use OnUpgrade, detail reference https://efthymis.com/migrating-a-mobile-database-in-flutter-sqlite/
code snippet
await openDatabase(path,
version: 1,
onCreate: (Database db, int version) async {
await db.execute(initialSchema));
},
onUpgrade: (Database db, int oldVersion, int newVersion) async {
await db.execute(migrationScript));
});
You have to create migrations and run them against your existing database using the onUpgrade handler. Basically you need to check the existing database version and upgrade if the version is smaller than the migration number.
You can check out the detail steps/code tutorial here.
I needed to create more than just multiple tables; but multiple database. I have since developed a dart package; sqlite_at_runtime that stretches to as far as creating all these entities at run time.
Below is an example code that creates three tables with similar attributes.
await Sqlartime.tableCreate(['sample1','sample2','sample3'],['name TEXT','age INTEGER','temp REAL']);

Resources