I am trying to save a favourite list of articles in my news app in Flutter by using shared preferences, I'd need to pass data article from JSON model into the favourite article page once the article is bookmarked. Once I save the article I got this message in my terminal, but when I go to the favourite page, the page is empty.
Does anybody know how to solve this issue?
Here my code
Article model
class Article{
String? urlImage;
String? urlImageSource;
String? title;
final String? description;
Article({
required this.urlImage,
this.urlImageSource,
required this.title,
required this.description,
});
factory Article.fromJson(Map<String, dynamic> json) => Article(
urlImage: json["_embedded"]["wp:featuredmedia"][0]["link"],
urlImageSource: json["_embedded"]['wp:featuredmedia'][0]["media_details"]["sizes"]["thumbnail"]["source_url"],
title: json["title"]["rendered"],
description: json['content']['rendered'],
);
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['urlImage'] = urlImage;
data['urlImageSource'] = urlImageSource;
data['title'] = title;
data['desciption'] = description;
date:['urlImage'] != null ? ['urlImage'].toString().replaceFirst('https:', 'urlImageSource') : null;
return data;
}
}
Article page
class ArticlePage extends StatefulWidget {
const ArticlePage({Key? key, required this.data,}) : super(key: key);
final data;
@override
_ArticlePageState createState() => _ArticlePageState(data);
}
class _ArticlePageState extends State<ArticlePage> {
final data;
//late List _post;
_ArticlePageState(this.data);
/// Save article
TextEditingController controller = TextEditingController();
late SharedPreferences preferences;
late List<Article>? _posts = preferences.getStringList('articles')?.cast<Article>();
//static const _savedArticle = "articles";
late bool _isSaved = false;
//late final bool _isRemoved = false;
@override
void initState() {
super.initState();
_savedArticle(Article);
_persistentPrefences();
// SharedPreferences.getInstance().then((preferences) {
// preferences.setBool("articles", _isSaved);
// setState(() {
// _isSaved = _isSaved;
// });
// });
_posts = [];
}
/// Save articles
Future<void> _savedArticle(Article) async {
preferences = await SharedPreferences.getInstance();
preferences.setBool('_isSaved', true);
await preferences.setStringList('articles', <String>[
data["_embedded"]["wp:featuredmedia"][0]["link"],
data["title"]["rendered"].replaceAll("<p>", "").replaceAll("’", "'").replaceAll("</p>", ""),
]);
//Map<String, dynamic> jsonDetails = jsonDecode(preferences.getString('articles')!);
// Article article = Article.fromJson(jsonDetails);
//
// if(jsonDetails.isNotEmpty){
// print(data["_embedded"]["wp:featuredmedia"][0]["link"],);
// print(data["title"]["rendered"].replaceAll("<p>", "").replaceAll("’", "'").replaceAll("</p>", ""),);
// data.value = Image.network(data);
// data.value = Text(data);
// }
SharedPreferences.getInstance().then((preferences) {
preferences.setBool("articles", _isSaved);
setState(() {
_isSaved = _isSaved;
});
});
if (_isSaved) {
_isSaved = false;
print("Ready to save article");
} else {
_isSaved = true;
CoolAlert.show(
context: context,
type: CoolAlertType.success,
title: "Articolo Salvato",
text: 'Hai salvato l\'articolo nella pagina dei preferiti',
backgroundColor: Colors.white,
autoCloseDuration: const Duration(seconds: 3),
confirmBtnText: "Ok",
confirmBtnColor: const Color(0xFF0D47A1),
confirmBtnTextStyle: const TextStyle(color: Colors.white, fontWeight:FontWeight.w600, fontSize: 18.0,fontFamily: "Raleway"),
onConfirmBtnTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SavedArticlesPage(data: data),
),
);
},
);
print("Saved article"
"${data["_embedded"]["wp:featuredmedia"][0]["link"]},"
"${data["title"]["rendered"].toString().replaceAll("<p>", "").replaceAll("’", "'").replaceAll("</p>", "")}",
);
var preferences = await SharedPreferences.getInstance();
}
}
/// Store articles
void _storedArticles(List<String> _posts) async {
var preferences = await SharedPreferences.getInstance();
late final List<String>? _posts = preferences.getStringList('articles',);
// Retreive article
bool status = preferences.getBool('_isSaved') ?? false;
//var liked = preferences.getBool(_savedArticle);
setState(() => this._isSaved = _isSaved);
}
/// Persistent articles
void _persistentPrefences() async {
setState(() => _isSaved = !_isSaved);
var preferences = await SharedPreferences.getInstance();
late final List<String>? _posts = preferences.getStringList('articles',);
//preferences.setBool(_savedArticle, _isSaved);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
leading: Builder(
builder: (BuildContext context) {
return GestureDetector(
child: Center(
child: Container(
color: Colors.white70.withOpacity(0),
child: Container(
height: 30,
width: 30,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/images/previous.png',
),
),
),
),
),
),
onTap: () {
Navigator.of(context).pop();
},
);
},
),
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
background: CachedNetworkImage(
imageUrl: data["_embedded"]['wp:featuredmedia'][0]["media_details"]["sizes"]["medium"]["source_url"],
// data["_embedded"]["wp:featuredmedia"][0]["link"],
fit: BoxFit.cover,
placeholder: (context, url) => Image.asset("assets/gif/shimmer.gif", fit: BoxFit.cover,),
errorWidget: (context, url, error) => Image.asset("assets/images/unloadedImage.png", width: 250, height: 250),
),
),
actions: [
Container(
margin: const EdgeInsets.only(top: 12.0, bottom: 10.0),
width: 35,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
color: Colors.white,
),
child: Center(
child: IconButton(
icon: _isSaved
? const Icon(Icons.bookmark_outlined, color: Color(0xFF0D47A1), size: 18,)
: const Icon(Icons.bookmark_border_rounded, color: Color(0xFF0D47A1), size: 18,),
onPressed: () async {
await _savedArticle(Article);
},
),
),
),
const SizedBox(width: 15,),
],
floating: true,
expandedHeight: 320,
),
SliverList(
delegate: SliverChildListDelegate([
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const SizedBox(height: 20,),
// Title container
Container(
padding: const EdgeInsets.all(16),
child: Text(data["title"]["rendered"]
.toString()
.replaceAll("<p>", "")
.replaceAll("’", "'")
.replaceAll("</p>", ""),
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 20,
fontFamily: "Raleway",
),
),
),
const SizedBox(height: 0),
Container(
padding: const EdgeInsets.all(16),
child: HtmlWidget(
data['content']['rendered']
.toString()
.replaceAll("<p>", "")
.replaceAll("’", "'")
.replaceAll("</p>", ""),
),
),
const SizedBox(height: 20),
],
),
],
),
),
],
),
);
}
}
Favourite article page
class SavedArticlesPage extends StatefulWidget {
const SavedArticlesPage({Key? key, required this.data}) : super(key: key);
final data;
@override
_SavedArticlesPageState createState() => _SavedArticlesPageState(this.data);
}
class _SavedArticlesPageState extends State<SavedArticlesPage> {
final sharedPreferences = SharedPreferences.getInstance();
final data;
static var preferences;
var i;
_SavedArticlesPageState(this.data);
late List _post;
static const savedArticle = "articles";
late bool _loadedArticle = false;
final List<Article>? _posts = preferences?.getStringList('articles').toList();
@override
void initState() {
super.initState();
_loadArticles();
_clearArticlePermanently();
}
/// Get articles list of strings from 'articles' key and Save persistent articles.
Future<List<Article>?> _loadArticles() async {
//preferences ??= await SharedPreferences.getInstance();
if(sharedPreferences == null){
preferences = await SharedPreferences.getInstance();
}
//var liked = preferences!.getBool(_loadedArticle);
await preferences.getStringList(_posts);
preferences.setBool(_loadedArticle, _loadedArticle);
print("Article Loaded");
setState(() {
this._loadedArticle = true;
});
print("Article Loaded");
return _posts;
}
/// Delete articles permanently
void _clearArticlePermanently() async {
//SharedPreferences preferences = await SharedPreferences.getInstance();
final removeArticle = await preferences.remove('articles');
final clearArticle = await preferences.clear('articles');
setState(() {
this._posts;
});
print("Article removed");
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.blue[900],
centerTitle: true,
title: const Text("Articoli Preferiti",
style: TextStyle(
color: Colors.white,
fontFamily: "Raleway",
fontSize: 18,
fontWeight: FontWeight.w600),
),
leading: Builder(
builder: (BuildContext context) {
return IconButton(
icon: const Icon(
Icons.arrow_back_ios_rounded,
color: Colors.white,
),
onPressed: () {Navigator.pop(context);},
);
},
),
),
body: Padding(
padding: const EdgeInsets.all(12),
child: generateArticleList(),
),
);
}
generateArticleList() {
return ListView.builder(
itemCount: _posts == null ? 0 : _posts!.length,
itemBuilder: (context, index) {
var article = _posts![index];
return Dismissible(
key: Key(_post as String),
child: Card(
margin: const EdgeInsets.only(top: 8),
elevation: 5,
shadowColor: Colors.black26,
color: Colors.white,
child: InkWell(
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: 190,
width: double.infinity,
child: CachedNetworkImage(
imageUrl: _post[i]["_embedded"]['wp:featuredmedia'][0]["media_details"]["sizes"]["medium"]["source_url"],
//_posts[index] as String,
//_posts![i] as String,
fit: BoxFit.cover,
placeholder: (context, url) => Image.asset("assets/gif/shimmer.gif",
width: double.infinity,
height: 190,
fit: BoxFit.cover,
),
errorWidget: (context, url, error) => Image.asset("assets/images/unloadedImage.png",
width: 250, height: 250),
),
),
// Title article
Column(
children: [
Padding(
padding: const EdgeInsets.only(left:16, top: 16, bottom: 16),
child: Row(
children: [
Expanded(
child: Text(
_post[i]["title"]["rendered"]
.toString()
.replaceAll("<p>", "")
.replaceAll("’", "'")
.replaceAll("</p>", "")
.toString(),
//_posts[index] as String,
style: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 20,
fontFamily: "Raleway",
),
overflow: TextOverflow.ellipsis,
maxLines: 2,
//softWrap: false,
),
),
],
),
)
],
),
],
),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ArticlePage(
data: data,
//data: _post[i]
),
),
);
}
),
),
//background: slideRightBackground(),
secondaryBackground: slideLeftBackground(),
confirmDismiss: (direction) async {
if (direction == DismissDirection.endToStart) {
final bool res = await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: const Text("Sei sicuro di voler eliminare l'articolo?",
style: TextStyle(
color: Color(0xFf6d6e75),
fontFamily: "Raleway",
fontWeight: FontWeight.w500,
),
),
actions: <Widget>[
/// Cancel btn
Container(
height: 35,
decoration: const BoxDecoration(
color: Color(0xFFeef1f7),
borderRadius: BorderRadius.all(Radius.circular(5))
),
child: MaterialButton(
child: const Text('No',
style: TextStyle(
fontFamily: "Raleway",
color: Color(0xFF6b6f73),
fontWeight: FontWeight.w600,
),
),
onPressed: () => Navigator.of(context).pop(true),
elevation: 0,
),
),
/// Delete btn
Container(
height: 35,
decoration: const BoxDecoration(
color: Color(0xFF0D47A1),
borderRadius: BorderRadius.all(Radius.circular(5))
),
child: MaterialButton(
child: const Text('Elimina',
style: TextStyle(
color: Colors.white,
fontFamily: "Raleway",
fontWeight: FontWeight.w600,
),
),
onPressed: _clearArticlePermanently,
elevation: 0,
),
)
],
);
});
return res;
}
return null;
// ArticlePage(data: _post[i]);
//ArticlePage(data: data);
//ArticlePage(data: data);
},
);
},
);
}
/// Empty btn
// Widget slideRightBackground() {
// return Container();
// }
/// Right bottom delete article
Widget slideLeftBackground() {
return Container(
color: Colors.red,
child: Align(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: const <Widget>[
Icon(Icons.delete, color: Colors.white,),
SizedBox(width: 10,),
Text("Elimina",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
fontFamily: "Raleway",
),
textAlign: TextAlign.right,
),
SizedBox(width: 20,),
],
),
alignment: Alignment.centerRight,
),
);
}
}