Flutter; save user generated data on phone - database

In my app I have a list that the user adds items to.
I want to be able to store this data (the list) locally, on the phone, so the list can show up next time the user opens the app.
How would I do that.
(the app is only for android phones)

you can use the Shared Preferences plugin which is a simple key-value storage
here is the link to the plugin https://pub.dev/packages/shared_preferences/install
here is an example of how u can store a list of items and also how to load them :
class PersonData {
final String id;
final String name;
final String lastName;
PersonData({this.id, this.name, this.lastName});
}
void saveItems(List<PersonData> items) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
List<String> personList = [];
items.forEach((element) {
personList.add("${element.id},${element.name},${element.lastName}");
});
sharedPreferences.setStringList("personData", personList);
}
Future<List<PersonData>> loadItems() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
List<String> personList = sharedPreferences.getStringList("personData");
List<PersonData> items = [];
personList.forEach((element) {
List<String> personData = element.split(",");
PersonData person = PersonData(
id: personData[0], name: personData[1], lastName: personData[2]);
items.add(person);
});
return items;
}

You can use different approaches,
Shared Preferences - Saving data into local cache.
Wraps platform-specific persistent storage for simple data (NSUserDefaults on iOS and macOS, SharedPreferences on Android, etc.). Data may be persisted to disk asynchronously, and there is no guarantee that writes will be persisted to disk after returning, so this plugin must not be used for storing critical data.
Sqflite - Using an SQLite local database.
SQLite plugin for Flutter. Supports iOS, Android and MacOS.
Support transactions and batches
Automatic version managment during open
Helpers for insert/query/update/delete queries
DB operation executed in a background thread on iOS and Android
If it's a small app i suggest to use Hive
Hive is a lightweight and blazing fast key-value database written in pure Dart
That's an example using Hive.
var box = Hive.box('myBox');
box.put('name', 'David');
var name = box.get('name');
print('Name: $name');
Of course there are many packages that use different ways to store data, you can search for the one that fits more your usage.

Related

Xamarin Forms - How do i use a Premade Local Database? [Solved] [duplicate]

I have started using the Xamarin plugin for Visual Studio to create an Android app.
I have a local SQL database, and I want to call it to display data. I don't see how I can do this. Is it possible?
After thinking this was a trivial thing to do, I was proven wrong when I tried setup a quick test project. This post will contain a full tutorial on setting up a DB for an Android App in Xamarin that will come in handy as a reference for future Xamarin users.
At a glance:
Add Sqlite.cs to your project.
Add your database file as an Asset.
Set your database file to build as an AndroidAsset.
Manually copy the database file out of your apk to another directory.
Open a database connetion using Sqlite.SqliteConnection.
Operate on the database using Sqlite.
Setting up a local database for a Xamarin Android project
1. Add Sqlite.cs to your project.
Start by going to this repository and downloading Sqlite.cs; this provides the Sqlite API that you can use to run queries against your db. Add the file to your project as a source file.
2. Add DB as asset.
Next, get your DB and copy it into the Assets directory of your Android project and then import it into your project so that it appears beneath the Assets folder within your solution:
I'm using the Chinook_Sqlite.sqlite database sample renamed to db.sqlite from this site throughout this example.
3. Set DB to build as AndroidAsset.
Right click on the DB file and set it to build action AndroidAsset. This will ensure that it is included into the assets directory of the APK.
4. Manually copy DB out of your APK.
As the DB is included as an Asset (packaged within the APK) you will need to extract it out.
You can do this with the following code:
string dbName = "db.sqlite";
string dbPath = Path.Combine (Android.OS.Environment.ExternalStorageDirectory.ToString (), dbName);
// Check if your DB has already been extracted.
if (!File.Exists(dbPath))
{
using (BinaryReader br = new BinaryReader(Android.App.Application.Context.Assets.Open(dbName)))
{
using (BinaryWriter bw = new BinaryWriter(new FileStream(dbPath, FileMode.Create)))
{
byte[] buffer = new byte[2048];
int len = 0;
while ((len = br.Read(buffer, 0, buffer.Length)) > 0)
{
bw.Write (buffer, 0, len);
}
}
}
}
This extracts the DB as a binary file from the APK and places it into the system external storage path. Realistically the DB can go wherever you want, I've just chosen to stick it here.
I also read that Android has a databases folder that will store databases directly; I couldn't get it to work so I've just ran with this method of using an existing DB.
5. Open DB Connection.
Now open a connection to the DB through the Sqlite.SqliteConnection class:
using (var conn = new SQLite.SQLiteConnection(dbPath))
{
// Do stuff here...
}
6. Operate on DB.
Lastly, as Sqlite.net is an ORM, you can operate on the database using your own data types:
public class Album
{
[PrimaryKey, AutoIncrement]
public int AlbumId { get; set; }
public string Title { get; set; }
public int ArtistId { get; set; }
}
// Other code...
using (var conn = new SQLite.SQLiteConnection(dbPath))
{
var cmd = new SQLite.SQLiteCommand (conn);
cmd.CommandText = "select * from Album";
var r = cmd.ExecuteQuery<Album> ();
Console.Write (r);
}
Summary
And that's how to add an existing Sqlite database to your Xamarin solution for Android! For more information check out the examples included with the Sqlite.net library, its unit tests and the examples in the Xamarin documentation.
Here is the one that I'm using and it's working
install the Sqlite plugin
create interface to access different platforms services
create a model for the table
implement the interface that you created earlier on all of the
platform you want to use
use the plugin to create, get, insert, etc on your table
for more detailed information check this

How to configure Ignite to work as a full distributed database?

I'm trying to manage a decentralized DB around a huge number of partial DB instances. Each instance has a subset of the whole data and they are all nodes and clients, thus asking for some data the query must be spread to every (group) instance and which one have it will return the data.
Due to avoid lost of data if one instance goes down, I figured out they must replicate its contents with some others. How this scenario can be configured with Ignite?
Supose I have a table with the name and last access datetime of users in a distributed application, like ...
class UserLogOns
{
string UserName;
DateTime LastAccess;
}
Now when the program starts I prepare Ingite for work as a decentralizad DB ...
static void Main(string[] args)
{
TcpCommunicationSpi commSpi = new TcpCommunicationSpi();
// Override local port.
commSpi.LocalPort = 44444;
commSpi.LocalPortRange = 0;
IgniteConfiguration cfg = new IgniteConfiguration();
// Override default communication SPI.
cfg.CommunicationSpi = commSpi;
using (var ignite = Ignition.Start(cfg))
{
var cfgCache = new CacheConfiguration("mio");
cfgCache.AtomicityMode = CacheAtomicityMode.Transactional;
var cache = ignite.GetOrCreateCache<string, UserLogOns>(cfgCache);
cache.Put(Environment.MachineName, new UserLogOns { UserName = Environment.MachineName, LastAccess = DateTime.UtcNow });
}
}
And now ... I want to get LastAccess of other "computerB" when ever it was ..
Is this correct? How can it be implemented?
It depends on the exact use-case that you want to implement. In general, Ignite provides out of the box everything that you mentioned here.
This is a good way to start with using SQL in Ignite: https://apacheignite-sql.readme.io/docs
Create table with "template=partitioned" instead of "replicated" as it is shown in the example here: https://apacheignite-sql.readme.io/docs/getting-started#section-creating-tables, configure number of backups and select a field to be affinity key (a field that is used to map specific entries to cluster node) and just run some queries.
Also check out the concept of baseline topology if you are going to use native persistence: https://apacheignite.readme.io/docs/baseline-topology.
In-memory mode will trigger rebalance between nodes on each server topology change (node that can store data in/out) automatically.

Copy/Cache Azure database locally for offline use.

I have a piece of software that connects to an Azure database to gather formulas for several varieties of colors to allow the user to follow a recipe to create their own product.
Basically there is just one big database pull when the application launches, that pulls down all the formulas, and from there the user can simply use, modify, or even delete the formulas as they wish.
The problem is that where this software is used, there are seldom constant internet connections, and the application so far is simply designed to shutdown if there isn't one present.
I am looking for a solution for being able to allow the application to BOTH connect to the database on application startup(If a connection is present) and save a copy locally, or if no connection is present, check for a locally saved copy to work with.
I have looked everywhere, but have been unable to find any methods to "programmatically" retrieve the pertinent(or all, if necessary) data, and either export it to a local file, or cache it somehow for offline use.
Any suggestions?
You don't mention what kind of technology you're using on the client side, and if you require access to a database on the client or just the data - but regardless there are several ways to do this.
Azure Mobile apps have a quickstart which will implements an Azure SQL <-> SQL Lite (mobile) database sync framework (table controllers on the server side). This lets you use libraries on the mobile side to use the local db for getting its data, and when you know you're online you can sync to/from the server. This is quite sophisticated and probably gives you more than just the caching that you're looking for.
I've used two other strategies for caching - one for HTML/javascript based applications (phonegap/cordova) and the other for Xamarin c# apps on iOS and Android. I'm assuming if it's a standard windows desktop app you know how to persist data so you can use whatever kind of cache/file system/db you like.
JavaScript/html - use the html5 localStorage functions to store the JSON output of the web server calls you're making. This is really easy then to abstract, where your app before is making an ajax call to the server to get some data, instead move that to a "liveorcache" class, which can determine whether to go to the server or just use the local storage. Code snippet for saving/loading json in localstorage below:
$scope.saveFixtures = function () {
localStorage["fixtures"] = JSON.stringify($scope.fixtures);
};
$scope.loadFixtures = function () {
if (localStorage["fixtures"] != undefined) {
$scope.fixtures = JSON.parse(localStorage["fixtures"]);
}
};
If you're writing your app in Xamarin you can do the same kind of thing, but using a PCL library - I used "PCLStorage" which works on Android and iOS. Same strategy though, in my code I just write the JSON data to a file with an appropriate filename, but usually wrap the object in another object that contains the cache write date/time. You then serialise the object to the file - something like below.
public class CacheProvider
{
public static async Task<CacheModel> ReadCache<T>(string filename)
{
IFolder rootFolder = FileSystem.Current.LocalStorage;
IFolder cache = await rootFolder.CreateFolderAsync("sportenzaCache", CreationCollisionOption.OpenIfExists);
try
{
IFile file = await cache.GetFileAsync(filename);
var data = await file.ReadAllTextAsync();
return JsonConvert.DeserializeObject<T>(data) as CacheModel;
}
catch(FileNotFoundException ex)
{
return null;
}
}
public static async void WriteCache<T>(string filename, CacheModel data)
{
IFolder rootFolder = FileSystem.Current.LocalStorage;
IFolder cache = await rootFolder.CreateFolderAsync("sportenzaCache", CreationCollisionOption.OpenIfExists);
IFile file = await cache.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
if (file != null)
{
data.CacheCreated = DateTime.Now;
string json = JsonConvert.SerializeObject(data);
await file.WriteAllTextAsync(json);
}
}
public static async void DeleteCache(string filename)
{
IFolder rootFolder = FileSystem.Current.LocalStorage;
IFolder cache = await rootFolder.CreateFolderAsync("sportenzaCache", CreationCollisionOption.OpenIfExists);
IFile file = await cache.GetFileAsync(filename);
if (file != null)
await file.DeleteAsync();
}
}

NHibernate Performance Optimization | Suggestions invited!

I’m facing an issue with NHibernate performance and can you please suggest me some optimizations? Below mentioned is a small summary of my application architecture
I have a windows service which is listening to a messaging bus. On receiving a message the service creates an object out of which a property is the received xml snippet and saves the message to the DB (uses NH). There is a WPF UI with a readonly connection to the DB, and on refresh of the UI it displays the objects on the screen.
While the UI does a refresh, it retrieves the xml and deserializes it , from which the object’s properties are derived and binded to the screen.
For example assume an xml XXX is received by the service, it deserializes the xml , creates the book object and save it to the DB and a property/column is SCHEMA which contains the xml snippet.
The UI while refreshed searches all book objects by ID and creates the book objects out of the xml which is being saved (yes, the xml is the constructor param).
Now my issue is that the refresh takes more than 2 minutes to display say 50 book objects. I analyzed it using the NHibernate profiler, and found that the time spend within the DB is negligible, however time spent to create the entities is proportionally huge(10ms:1990 ms).I guess it’s due to the fairly huge size of xml snippet and it’s deserialization.
My question is, how can I improve the performance. I dispose sessions after every refresh and is not lazy loading (please note that the time spend in DB is negligible). On every refresh it’s possible that all objects are updated by some downstream systems or maybe one of them are updated.Can I implement some sort of caching mechanism in this case?
Thanks in advance for any suggestions.
Regards,
-Mike
The entire list of 50 books could be saved in a singleton class meant for caching. Like a cache manager. You could also use say an enterprise library cache but I would suggest an in memory cache. If a book gets added you could update the cache. The cache would have the entire xml so no deserialisation would happen. Also you could update the db in an ansynchronous thread and reduce the time.
Here is the pseudo code
On the service, whenever I receive a message
public void OnMessage(string message)
{
//deserializes the message
DeserializedObject schema = deserializationFactory.Deserialize(message);
var book = new Book(schema,message);
// saves the book using a new session
repository.Save(book);
}
The book object:
public class Book
{
public DeserializedObject Schema{get;set;}
private string xml;
public string Xml{get{return xml;}}
public Book(DeserializedObject schema,string xml):this(schema)
{
this.xml = xml;
}
public Book(DeserializedObject schema):this()
{
this.Schema = schema;
}
public virtual XmlDocument XmlSchema
{
get
{
var doc = new XmlDocument();
if (Schema!= null)
{
var serializer = new XmlSerializer(typeof(DeserializedObject));
var stream = new MemoryStream();
serializer.Serialize(stream, Schema);
stream.Position = 0;
doc.Load(stream);
}
return doc;
}
}
public virtual string SerializedSchema
{
get { return XmlSchema.OuterXml; }
set
{
if (value != null)
Schema = value.Deserialize< DeserializedObject >();
}
}
public string Author
{
get{return Schema.Author;}
}
}
Now the Mapping for Book(uses FNH)
public class BookMap:ClassMap<Book>
{
LazyLoad();
Table("Books");
IdGenerator.Instance.GenerateId(this, "book_id_seq", book => book.Id);
Map(book=> book.SerializedSchema, "SERIALIZED_SCHEMA")
.CustomSqlType("Clob")
.CustomType("StringClob");
}
On UI:
public void OnRefresh()
{
//In reality the call to DB runs on a background worker and the records are binded to the grid after a context switch.
//GetByCriteria creates a new session every time a refresh happens.
datagrid.DataContext = repository.GetByCriteria(ICriterion allBooksforToday);
}
The important thing to note here is Book type is shared between the service and the UI. However, only service can do a write to the DB, wherin the UI can update the trade object (basically the xml) and sends it over the messaging bus (again the xml). The service once receiving it updates the DB.
The xml size will be approximately 20 KB, so that would mean that if I'm loading say 50 books I'll be loading close to an MB of data.
Thanks,-Mike

Provide a database packaged with the .APK file or host it separately on a website?

Here is some background about my app:
I am developing an Android app that will display a random quote or verse to the user. For this I am using an SQLite database. The size of the DB would be approximately 5K to 10K records, possibly increasing to upto 1M in later versions as new quotes and verses are added. Thus the user would need to update the DB as and when newer versions are of the app or DB are released.
After reading through some forums online, there seem to be two feasible ways I could provide the DB:
1. Bundle it along with the .APK file of the app, or
2. Upload it to my app's website from where users will have to download it
I want to know which method would be better (if there is yet another approach other than these, please do let me know).
After pondering this problem for some time, I have these thoughts regarding the above approaches:
Approach 1:
Users will obtain the DB along with the app, and won't have to download it separately. Installation would thereby be easier. But, users will have to reinstall the app every time there is a new version of the DB. Also, if the DB is large, it will make the installable too cumbersome.
Approach 2:
Users will have to download the full DB from the website (although I can provide a small, sample version of the DB via Approach 1). But, the installer will be simpler and smaller in size. Also, I would be able to provide future versions of the DB easily for those who might not want newer versions of the app.
Could you please tell me from a technical and an administrative standpoint which approach would be the better one and why?
If there is a third or fourth approach better than either of these, please let me know.
Thank you!
Andruid
I built a similar app for Android which gets periodic updates with data from a government agency. It's fairly easy to build an Android compatible db off the device using perl or similar and download it to the phone from a website; and this works rather well, plus the user gets current data whenever they download the app. It's also supposed to be possible to throw the data onto the sdcard if you want to avoid using primary data storage space, which is a bigger concern for my app which has a ~6Mb database.
In order to make Android happy with the DB, I believe you have to do the following (I build my DB using perl).
$st = $db->prepare( "CREATE TABLE \"android_metadata\" (\"locale\" TEXT DEFAULT 'en_US')");
$st->execute();
$st = $db->prepare( "INSERT INTO \"android_metadata\" VALUES ('en_US')");
$st->execute();
I have an update activity which checks weather updates are available and if so presents an "update now" screen. The download process looks like this and lives in a DatabaseHelperClass.
public void downloadUpdate(final Handler handler, final UpdateActivity updateActivity) {
URL url;
try {
close();
File f = new File(getDatabasePath());
if (f.exists()) {
f.delete();
}
getReadableDatabase();
close();
url = new URL("http://yourserver.com/" + currentDbVersion + ".sqlite");
URLConnection urlconn = url.openConnection();
final int contentLength = urlconn.getContentLength();
Log.i(TAG, String.format("Download size %d", contentLength));
handler.post(new Runnable() {
public void run() {
updateActivity.setProgressMax(contentLength);
}
});
InputStream is = urlconn.getInputStream();
// Open the empty db as the output stream
OutputStream os = new FileOutputStream(f);
// transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[1024 * 1000];
int written = 0;
int length = 0;
while (written < contentLength) {
length = is.read(buffer);
os.write(buffer, 0, length);
written += length;
final int currentprogress = written;
handler.post(new Runnable() {
public void run() {
Log.i(TAG, String.format("progress %d", currentprogress));
updateActivity.setCurrentProgress(currentprogress);
}
});
}
// Close the streams
os.flush();
os.close();
is.close();
Log.i(TAG, "Download complete");
openDatabase();
} catch (Exception e) {
Log.e(TAG, "bad things", e);
}
handler.post(new Runnable() {
public void run() {
updateActivity.refreshState(true);
}
});
}
Also note that I keep a version number in the filename of the db files, and a pointer to the current one in a text file on the server.
It sounds like your app and your db are tightly bound -- that is, the db is useless without the database and the database is useless without the app, so I'd say go ahead and put them both in the same .apk.
That being said, if you expect the db to change very slowly over time, but the app to change quicker, and you don't want your users to have to download the db with each new app revision, then you might want to unbundle them. To make this work, you can do one of two things:
Install them as separate applications, but make sure they share the same userID using the sharedUserId tag in the AndroidManifest.xml file.
Install them as separate applications, and create a ContentProvider for the database. This way other apps could make use of your database as well (if that is useful).
If you are going to store the db on your website then I would recommend that you just make rpc calls to your webserver and get data that way, so the device will never have to deal with a local database. Using a cache manager to avoid multiple lookups will help as well so pages will not have to lookup data each time a page reloads. Also if you need to update the data you do not have to send out a new app every time. Using HttpClient is pretty straight forward, if you need any examples please let me know

Resources