Flutter - Saving user data
If you want to save data in a persitance way in Dart code and be able to use it on Android and iOS I suggest you an sqlite plugin like that:
https://github.com/tekartik/sqflite
Otherwise if you only need to save a bunch of data use the shared_preferences plugin
https://github.com/flutter/plugins/tree/master/packages/shared_preferences
These two plugins both support either Android and iOS
You are requesting a lot of code ^_^ (ain't it).
So first of all you need to get your json through an HTTP call. For that use the http flutter package:
const request = "https://newsapi.org/v2/top-headlines?sources=crypto-coins-news&apiKey=d40a757cfb2e4dd99fc511a0cbf59098";
http.Response response = await http.get(request);
debugPrint("Response: " + response.body);
Wrap it up in an async method:
void _jsonAndSqlite() async {
...
}
And in the response
variable you have your full JSON
.
Now you need to serialize and I suggest you this really good reading.
I've choose for this answer the Manaul JSON Decoding
Manual JSON decoding refers to using the built-in JSON decoder in dart:convert. It involves passing the raw JSON string to the json.decode() method, and then looking up the values you need in the Map the method returns. It has no external dependencies or particular setup process, and it’s good for a quick proof of concept.
var myBigJSONObject = json.decode(response.body);
var status = myBigJSONObject['status'];
var totalResults = myBigJSONObject['totalResults'];
var myArticles = myBigJSONObject['articles'];
debugPrint("articles: " + myArticles.toString());
Now that we have articles will try to save them on Sqlite DB through Sqflite package
var myFirstArticle = myArticles[0];
var author = myFirstArticle['author'];
var title = myFirstArticle['title'];
// Get a location using getDatabasesPath
var databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'test.db');
// Delete the database
await deleteDatabase(path);
// open the database
Database database = await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
// When creating the db, create the table
await db.execute(
'CREATE TABLE Article (id INTEGER PRIMARY KEY, author TEXT, title TEXT)');
});
// Insert some records in a transaction
await database.transaction((txn) async {
int id1 = await txn.rawInsert(
'INSERT INTO Article(author, title) VALUES("$author", "$title")');
debugPrint('inserted1: $id1');
});
And that's it! Have fun studing and coding. Read the articles I've posted for you for JSON serailization and play around with my code, and trying to adding some other best practices they may better fit your needs. This is just a quick playground to, well, play with.
So I ended up with this method:
[...]
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
[...]
void _jsonAndSqlite() async {
const request =
"https://newsapi.org/v2/top-headlines?sources=crypto-coins-news&apiKey=d40a757cfb2e4dd99fc511a0cbf59098";
http.Response response = await http.get(request);
debugPrint("Response: " + response.body);
var myBigJSONObject = json.decode(response.body);
var status = myBigJSONObject['status'];
var totalResults = myBigJSONObject['totalResults'];
var myArticles = myBigJSONObject['articles'];
debugPrint("articles: " + myArticles.toString());
var myFirstArticle = myArticles[0];
var author = myFirstArticle['author'];
var title = myFirstArticle['title'];
// Get a location using getDatabasesPath
var databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'test.db');
// Delete the database
await deleteDatabase(path);
// open the database
Database database = await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
// When creating the db, create the table
await db.execute(
'CREATE TABLE Article (id INTEGER PRIMARY KEY, author TEXT, title TEXT)');
});
// Insert some records in a transaction
await database.transaction((txn) async {
int id1 = await txn.rawInsert(
'INSERT INTO Article(author, title) VALUES("$author", "$title")');
debugPrint('inserted1: $id1');
});
}
Jake
Work email: jake(at)squaredsoftware.co.uk Please support me by downloading my new iOS application, travelrecce!
Updated on December 08, 2022Comments
-
Jake over 1 year
I'm currently creating an application that tracks general crypto data, along with this data we also post news articles tailored to crypto enthusiasts. Every aspect of this news article data is stored in string form, from image url to date published - full list below.
I'm looking for a way to save this data to the user's device. In a perfect situation I'd just have this data saved in a JSON array, but apart from not knowing how to perform this, I'm not sure if it's the most efficient way to save this data for later display.
If you decide that JSON would be the best way of saving this data, all I need to know is how to properly manage this data into an array of different saved articles and how to import this properly into my Dart code.
An example of this code would be great, I'm looking to publish this app before the new year so I need all the help I can get. Many thanks.
This is the aforementioned data I'm looking to save / display from this source:
- Source -
source
- Author -
author
- Description -
description
- Date Published -
publishedAt
- Article Title -
title
- Url to Article -
url
- Article Image -
urlToImage
Edit: Trying to rework shadowsheep's answer to fit an index model
Each news widget is a new
inkwell
that allows a newscaffold
to be built. From thisscaffold
you are presented with the option to save the article. On saving, the code currently just changes the value of the following strings with thetitle
,description
,URL
andImage URL
._sTitle
_sDescription
_sURL
_sURLtoImage
I would really like a way of having the database, as described by shadowsheep, saved to the user's device. This means that the saved articles will become persistent on the device despite the user closing and opening the app.
The following code is the exact use case in which I'm displaying my news data.
CarouselSlider( items: [1,2,3,4,5,6,7,8,9,10].map((index) { return Builder( builder: (BuildContext context) { return Padding( padding: EdgeInsets.only( top: 5.0, bottom: 20.0, ), child: InkWell( borderRadius: BorderRadius.only( topLeft: const Radius.circular(15.0), topRight: const Radius.circular(15.0), ), onTap: () { print('Opened article scaffold: "' + articles[index].title + "\""); Navigator.push( context, MaterialPageRoute( builder: (context) => Scaffold( resizeToAvoidBottomPadding: false, appBar: AppBar( backgroundColor: const Color(0xFF273A48), elevation: 0.0, title: Container( width: _width*0.90, height: 30, padding: const EdgeInsets.only( bottom: 5, top: 5, left: 10, right: 10, ), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.all( Radius.circular(10.0), ), ), alignment: Alignment.center, child: AutoSizeText( 'Published ' + DateFormat.yMMMd().format(DateTime.parse(articles[index].publishedAt)) + ", "+ DateFormat.jm().format(DateTime.parse(articles[index].publishedAt)), overflow: TextOverflow.ellipsis, maxLines: 1, minFontSize: 5, maxFontSize: 20, textAlign: TextAlign.center, style: TextStyle( color: Colors.black, fontFamily: 'Poppins', ), ), ), ), body: Center( child: Scaffold( resizeToAvoidBottomPadding: false, body: Center( child: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ const Color(0xFF273A48), Colors.blueGrey ], ), ), padding: const EdgeInsets.only( top: 20, left: 10, right: 10, bottom: 50 ), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[ Column( mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[ FutureBuilder<Null>(future: _launched, builder: _launchStatus), AutoSizeText( articles[index].title, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, maxFontSize: 30, minFontSize: 15, maxLines: 3, style: TextStyle( color: Colors.white, fontFamily: 'Poppins', ), ), Divider( color: Colors.transparent, height: 15.0, ), Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(15.0), color: Colors.transparent, boxShadow: [ BoxShadow( color: Colors.black.withAlpha(70), blurRadius: 50.0, ) ], image: DecorationImage( image: NetworkImage(articles[index].urlToImage), fit: BoxFit.fitHeight, ), ), height: 220, width: 317.5, ), Divider( color: Colors.transparent, height: 15.0, ), ], ), Container( padding: const EdgeInsets.only( left: 20, right: 20 ), decoration: BoxDecoration( color: Colors.transparent, borderRadius: BorderRadius.all( Radius.circular(10.0), ), ), child: AutoSizeText( articles[index].description, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, maxFontSize: 30, minFontSize: 10, maxLines: 10, style: TextStyle( color: Colors.white, fontFamily: 'Poppins', ), ), width: _width*0.90, height: _height*0.20, ), Container( padding: const EdgeInsets.all(4.0), decoration: BoxDecoration( color: const Color(0xFF273A48), borderRadius: BorderRadius.all( Radius.circular(10.0), ), ), child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ IconButton( icon: Icon( Icons.favorite_border, color: Colors.red ), iconSize: 35.0, onPressed: () { _sTitle = articles[index].title; _sDescription = articles[index].description; _sURL = articles[index].url; _sURLtoImage = articles[index].urlToImage; Navigator.push( context, MaterialPageRoute(builder: (context) => _favoritesScreen()) ); } ), IconButton( icon: Icon( Icons.mobile_screen_share, color: Colors.white, ), iconSize: 35.0, onPressed: () { Share.share( articles[index].title + "\n\nCheck out this article at:\n" + articles[index].url + "\n\nLearn more with Cryp - Tick Exchange", ); } ), IconButton( icon: Icon(Icons.launch, color: Colors.lightBlueAccent), iconSize: 32.5, onPressed: () => setState(() { _launched = _launchInWebViewOrVC(articles[index].url);}), ), ], ), ), ], ), ), ), ), ), ) ) ); }, child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(10.0), color: const Color(0xFF273A48), boxShadow: [ BoxShadow( color: Colors.black.withAlpha(70), offset: const Offset(5.0, 5.0), blurRadius: 12.5, ) ], image: DecorationImage( alignment: Alignment.topCenter, image: NetworkImage(articles[index].urlToImage), fit: BoxFit.cover, ), ), height: _height*0.35, width: _width*0.725, child: Stack( children: <Widget>[ Align( alignment: Alignment.bottomCenter, child: Stack( alignment: Alignment.bottomRight, children: <Widget>[ Container( padding: EdgeInsets.only(left: 10.0, right: 10.0), decoration: BoxDecoration( color: const Color(0xFF273A48), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(10.0), bottomRight: Radius.circular(10.0) ), ), height: 60.0, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Flexible( child: Container( width: _width*0.725, child: Text( articles[index].title, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, maxLines: 2, style: TextStyle( color: Colors.white, fontFamily: 'Poppins', ), ), ), ), Text( 'Published ' + DateFormat.yMMMd().format(DateTime.parse(articles[index].publishedAt)) + ", "+ DateFormat.jm().format(DateTime.parse(articles[index].publishedAt)), overflow: TextOverflow.ellipsis, maxLines: 1, style: TextStyle( color: Colors.blueGrey, fontSize: 10.0, fontFamily: 'Poppins', ), ), ], ) ), Container( width: 25.0, height: 20.0, alignment: Alignment.center, child: Text( "$index", textAlign: TextAlign.center, style: TextStyle( color: Colors.blueGrey, fontSize: 10.0, fontFamily: "Poppins" ), ) ), ], ), ) ], ), ), ), ); }, ); }).toList(), height: 400, autoPlay: true, )
- Source -
-
Jake over 5 yearsDo you reckon you could give me a code example using either the provided values or dummy strings? Ideally, this would be for Dart, but if you'd know what this may look like in the JSON file it'd be nice if you could also post that so I can get a better picture of what the final product looks like. Thanks
-
Jake over 5 yearsThanks shadow, I’ll take a look at this soon.
-
Jake over 5 yearsI'll come back with a proper conclusion soon, but I think this may be just the ticket. Thanks for such a well-written response
-
Jake over 5 yearsThanks for this answer, looking over it I've now been able to apply this to saving
title
,description
etc and I've managed to find thedatabase
file in my iOS simulator's hidden library folder. All I need to manage now is allowing the user to delete articles they don't wish to be saved any longer, and how to prevent duplicate entries. I was originally going to ask with help with all this but I've managed to either complete this myself or am working towards it. Thanks again