Save json data in favorite list in flutter - json

I'm trying to add my data to a Favorited list but I have no idea how to do that I just saved it in Favorited list but when restart the app nothing saved any idea how to save in shared preferences or some thing like it
code for loading json as follows
List mydata = List();
final _savedata = Set<Map>();
Future<void> getjsondata() async {
final String response = await rootBundle.loadString('assets/data.json');
final data = await json.decode(response);
setState(() {
mydata = data["InfoPlate"];
});
}
and this one for add data to favorited list
void _pushSaved() {
Navigator.of(context).push(
new MaterialPageRoute<void>(
builder: (BuildContext context) {
final Iterable<ListTile> tiles = _savedata.map(
(Myinfo) {
return new ListTile(
title: Text(Myinfo['type']),
);
},
);
final List<Widget> divided = ListTile.divideTiles(
context: context,
tiles: tiles,
).toList();
return new Scaffold(
appBar: new AppBar(
title: const Text('Favorited info Plate '),
),
body: new ListView(children: divided),
);
},
),
);
}
and this is the rest of code for the adding button in card
Widget _buildDataList() {
return ListView.builder(
itemCount: mydata.length,
padding: const EdgeInsets.all(8.0),
itemBuilder: (context, int index) {
return _buildRowInfo(mydata[index]);
});
}
Widget _buildRowInfo(Map myinfo) {
final bool favourited = _savedata.contains(myinfo);
void _favorscreen() {
setState(() {
if (favourited) {
_savedata.remove(myinfo);
} else {
_savedata.add(myinfo);
}
});
}
return Directionality(
textDirection: TextDirection.rtl,
child: Card(
child: ListTile(
visualDensity: VisualDensity.comfortable,
title: Text(myinfo['type']),
trailing: new IconButton(
icon: Icon(favourited ? Icons.favorite : Icons.favorite_border),
color: favourited ? Colors.red : null,
onPressed: _favorscreen,
),
),
),
);
}
}

It is possible to store data locally using shared_preferences.
All you need is an instance of SharedPreferences.
class ExampleWidget extends StatefulWidget {
#override
_ExampleWidgetState createState() => _ExampleWidgetState();
}
class _ExampleWidgetState extends State<ExampleWidget> {
SharedPreferences sharedPreferences;
#override
void initState() {
super.initState();
Future.delayed(Duration.zero, () async {
sharedPreferences = await SharedPreferences.getInstance();
});
}
Then, to read or write data, sharedPreferences is a key-value database and exposes setters and getters. It does not allow to store Set, so you will need to encode and decode data payload depending on the operation you perform.
Future<bool> _writeData(Set<Map> setOfMaps) {
return sharedPreferences.setString(
'your_key',
jsonEncode(setOfMaps.toList()),
);
}
Set<Map> _readData() {
final encodedData = sharedPreferences.getString('your_key');
return jsonDecode(encodedData).toSet();
}

Related

How to fetch and show data from a json file which has a list of nested maps

I'm trying to fetch and show data from the json file which contains a list of nested maps, as a beginner I'm facing lot of difficulties while coding it, please help me to fetch and show data from the json file. I don't know how to fetch data, I have to show the name, email, city, age etc in the App UI. I don't know what is the procedure to work this nested maps. Help me to make this code work.
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:http/http.dart';
import 'package:task/model/model_class.dart';
class EmployeeListView extends StatefulWidget {
#override
_EmployeeListViewState createState() => _EmployeeListViewState();
}
class _EmployeeListViewState extends State<EmployeeListView> {
List<Results> _results = List<Results>();
#override
void initState() {
super.initState();
fetchResults.then((value){
setState(() {
_results.addAll(value);
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Employee"),
backgroundColor: Colors.black,
),
body: ListView.builder(
itemCount: 10,
itemBuilder: (context, index){
return Card(
color: Colors.white,
shadowColor: Colors.black,
elevation: 4.5,
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.transparent,
radius: 25.0,
backgroundImage: NetworkImage("url"),
),
title: Text("Name"),
subtitle: Text("Email"),
trailing: IconButton(icon: Icon(Icons.call), onPressed: (){}),
),
);
}),
);
}
Future<List<Results>> fetchResults() async{
var url = "https://randomuser.me/api?page=2&results=10&seed=99d7541361f1e116";
var response = await http.get(url);
if(response.statusCode == 200){
var resultsJson = json.decode(response.body);
for(var resultsJson in resultsJson){
_results.add(Results.fromJson(resultsJson));
return _results;
}
}
}
}
I can see you made a mistake somewhere in your for loop.
for(var resultsJson in resultsJson){
_results.add(Results.fromJson(resultsJson));
return _results;
You used the JsonList(resultsJson) to represent a Json object in the list.
Declare a new list too, and return outside the loop. Change this to;
List<Results> resultsList = [];
for(var result in resultsJson){
resultsList.add(Results.fromJson(result));
}
return resultsList;
Try that out and give feedback please.
Okay according to your code in init state. i don't recommend that you use setState in initState but rather inside didChangeDependences.
You have to decode the json data from the http request
From response its returning the [result] and [info]
I have use the result section and provided the details
Step 1:
class Results {
String gender;
String phone;
String nat;
DocumentReference reference;
Results(this.gender, this.phone, this.nat);
factory Results.fromSnapshot(DocumentSnapshot snapshot) {
Results newEmployee = Results.fromJson(snapshot.data());
newEmployee.reference = snapshot.reference;
return newEmployee;
}
factory Results.fromJson(Map<String, dynamic> json) =>
_resultsFromJson(json);
Map<String, dynamic> toJson() => _resultsToJson(this);
#override
String toString() => 'employeeName ${Results}';
}
Results _resultsFromJson(Map<String, dynamic> data) {
return Results(
data['gender'],
data['phone'],
data['nat'],
);
}
Map<String, dynamic> _resultsToJson(Results instance) {
return {
'gender': instance.gender,
'phone': instance.phone,
'nat': instance.nat,
};
}
Step 2:
List<Results> collectionData = [];
#override
void initState() {
super.initState();
fetchResults().then((value) {
setState(() {
collectionData.addAll(value);
});
});
}
Future<List<Results>> fetchResults() async {
List<Results> _results = [];
var url =
"https://randomuser.me/api?page=2&results=10&seed=99d7541361f1e116";
var response = await http.get(url);
if (response.statusCode == 200) {
var resultsJson = json.decode(response.body)['results'].cast<Map<String,dynamic>>();
await Future.forEach(resultsJson, (element) {
_results.add(Results.fromJson(element));
});
}
return Future.value(_results);
}

Flutter: using shared preferences plugin to store saved favorite list

The following is my JSON file which include the data:
[
{
"city": "City1",
"attractions": [
"attraction1",
"attraction2"
],
},
{
"city": "city2",
"attractions": [
"attraction1",
"attraction2",
],
},
]
My implementation code is a listview builder that gets data from the JSON file. the code also have an option option to save cities as favorite that can be shown in another page as a list of saved favorites:
class Index extends StatefulWidget {
#override
_IndexState createState() => _IndexState();
}
List data;
List<Cities> citylist = List();
List<Cities> citysavedlist = List();
int index;
class _IndexState extends State<Index> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: listView(),
);
}
Future<String> fetchData() async {
String data =
await DefaultAssetBundle.of(context).loadString("assets/data.json");
final jsonResult = json.decode(data);
this.setState(() {
jsonResult
.forEach((element) => citylist.add(new Cities.fromJson(element)));
});
return "Success!";
}
#override
void initState() {
super.initState();
fetchData();
}
listView() {
return ListView.builder(
itemCount: citylist == null ? 0 : citylist.length,
itemBuilder: (context, index) {
return Column(
children: <Widget>[_buildRow(index, citylist)],
);
},
);
}
Widget _buildRow(index, citylist) {
final bool alreadySaved = citysavedlist.contains(citylist[index]);
return Padding(
padding: const EdgeInsets.only(top: 5.0, left: 5.0, right: 5.0),
child: Card(
child: ListTile(
title:
Text(citylist[index].title, style: TextStyle(fontSize: 22.0)),
trailing: IconButton(
icon: Icon(
alreadySaved ? Icons.star : Icons.star_border,
color: alreadySaved ? Colors.blue : Colors.blue,
),
onPressed: () {
setState(() {
if (alreadySaved) {
citysavedlist.remove(citylist[index]);
} else {
citysavedlist.add(citylist[index]);
}
});
},
), //subtitle: Text(subtitle),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Detail(citylist[index])));
}),
),
);
}
void _pushSaved() {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) {
final Iterable<ListTile> tiles = citysavedlist.map(
(Cities pair) {
return ListTile(
title: Text(
pair.city,
),
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => Detail(pair)));
});
},
);
final List<Widget> divided = ListTile.divideTiles(
context: context,
tiles: tiles,
).toList();
return Scaffold(
appBar: AppBar(
title: const Text('Saved Suggestions'),
),
body: ListView(children: divided),
);
},
),
);
}
}
This is model class :
List<Cities> citiesFromJson(String str) =>
List<Cities>.from(json.decode(str).map((x) => Cities.fromJson(x)));
String citiesToJson(List<Cities> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Cities {
Cities({
this.city,
this.attractions,
});
String city;
List<String> attractions;
factory Cities.fromJson(Map<String, dynamic> json) => Cities(
city: json["city"],
attractions: List<String>.from(json["attractions"].map((x) => x)),
);
Map<String, dynamic> toJson() => {
"city": city,
"attractions": List<dynamic>.from(attractions.map((x) => x)),
};
}
Now I am facing an issue which is the saved favorite cities is not stored, so when the app is closed and reopened again, the favorite list will disappear as it is not stored.
I know that there is a plugin called Shared preferences that will do this functionality of storing saved data, but I am not able to integrate this plugin into my code. Can someone help to do that.
First what you have to do is get the package in the pubspec then
import 'package:shared_preferences/shared_preferences.dart';
SharedPreferences prefs = await SharedPreferences.getInstance();
Now to save the json data you have to do is
prefs.setString('mydata', json.encode(yourjsondata));
to retrieve this data
you must use the exact name you assigned , in my case 'mydata'
json.decode(preferences.getString('mydata'))
Remember If you want to save so many data items sqflite or hive is recommended or If you still want to use shared_preferences you might save a counter as
To save
var counter = (prefs.getInt('Counter')??0)+1;
prefs.setString('Counter:$counter', json.encode(yourdata));
To get back in the order
var listCount = preferences.getInt('Counter');
loop through using listCount and then use
json.decode(preferences.getString('Counter:$variable'))

Store List of Objects or Map in SharedPreference using dart

I am returning data from MySQL in JSON using this piece of code
while($row = mysqli_fetch_assoc($queryResult)) {
$resultArray[]=$row;
}
echo json_encode($resultArray);
The result is in this format
[{
"reg_number": "FA16-BCS-106",
"teacher_id": "1",
"qr_code": "jamshaid",
"course_name": "COURSE 1"
}, {
"reg_number": "FA16-BCS-106",
"teacher_id": "EMP_FA10_10",
"qr_code": "jamoo",
"course_name": "COURSE 2"
}]
I am decoding the response and storing it in a list using this method which is working fine.
class Student {
final String reg_number;
final String teacher_id;
final String qr_code;
final String course_name;
Student({this.reg_number, this.teacher_id, this.qr_code, this.course_name});
factory Student.fromJson(Map<String, dynamic> json) {
return Student(
reg_number: json['reg_number'],
teacher_id: json['teacher_id'],
qr_code: json['qr_code'],
course_name: json['course_name'],
);
}
}
final parsed =
json.decode(jsonResponse.body).cast<Map<String, dynamic>>();
List<Student> st =
parsed.map<Student>((json) => Student.fromJson(json)).toList();
I am trying to store this List of objects of Student class in SharedPreference using version ^0.5.6. There is no direct method available for this. I've tried using this method but having the following error.
Unhandled Exception: type 'List' is not a subtype of type 'Map'
jsonResponse.body is supposed to be a string but it is reading it as List<dynamic>. Why is that happening? Am I doing anything wrong while parsing the result? Thanks
Here is a simple example created for you to understand how to do this. This is ok for small list but if you have a large list, I dont recommend this because of we are doing too much stuff here.
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Todo {
final String title;
final String description;
Todo(this.title, this.description);
Todo.fromJson(Map<String, dynamic> map) :
title = map["title"],
description = map["description"];
Map<String, dynamic> toMap() => {
"title": title,
"description": description
};
}
void main() {
runApp(MaterialApp(
title: 'Passing Data',
home: HomePage(
todos: List.generate(
20, (i) => Todo(
'Todo $i',
'A description of what needs to be done for Todo $i',
),
),
),
));
}
class HomePage extends StatelessWidget {
final List<Todo> todos;
HomePage({this.todos}) {
saveTodos();
}
void saveTodos() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<String> values = todos.map((item) => json.encode(item.toMap())).toList();
prefs.setStringList("todos", values);
}
#override
Widget build(BuildContext context) {
return TodosScreen();
}
}
class TodosScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _StateTodosScreen();
}
}
class _StateTodosScreen extends State<TodosScreen> {
Future<List<Todo>> getTodos() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<String> values = prefs.getStringList("todos");
return values.map((item) => Todo.fromJson(json.decode(item))).toList();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Todos'),
),
body: FutureBuilder(
future: getTodos(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data[index].title),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(todo: snapshot.data[index]),
),
);
},
);
},
);
} else {
return Center(child: CircularProgressIndicator());
}
},
),
);
}
}
class DetailScreen extends StatelessWidget {
final Todo todo;
DetailScreen({Key key, #required this.todo}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(todo.title),
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Text(todo.description),
),
);
}
}
But you can simply encode your complete json and store, there will not be much work then, but if it is a complex json you have to handle that also.

JsonDecode to List<Object> from SharedPreferences

I have a list of strings which is held in an object and is stored in shared preferences.
I have used ScopedModel for my state management and I am trying to get it to read the list from the shared preferences from here.
class Item {
String _weight;
String _name;
String _id;
Item(this._weight, this._name, this._id);
Item.fromJson(Map<String, dynamic> m) {
_weight = m['weight'] as String;
_name = m['name'] as String;
_id = m['id'] as String;
}
String get id => _id;
String get name => _name;
String get weight => _weight;
Map<String, dynamic> toJson() => {
'weight': _weight,
'name': _name,
'id': _id,
};
}
My Model in the ScopedModel folder which is passed down;
mixin ListItem on Model {
String itemKey = 'itemKey';
List<Item> _items = [];
List<Item> get items {
return List.from(_items);
}
Future<Null> readList() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final data = json.decode(prefs.getString(itemKey).toString());
final item = List<Item>.from(data.map((i) => Item.fromJson(i)));
_items = item;
print(jsonDecode(prefs.getString(itemKey)));
notifyListeners();
}
Future<Null> addItem({String id, String name, String weight}) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final Item item = Item(
id,
name,
weight,
);
_items.add(item);
prefs.setString(itemKey, jsonEncode(_items));
notifyListeners();
}
Future<Null> deleteProduct() async {
notifyListeners();
}
}
Part of my stateful widget which runs the initState to call the list from sharedPreferences
class _ListItemsState extends State<ListItems> {
final MainModel _model = MainModel();
final TextEditingController controller = TextEditingController();
#override
void initState() {
_model.readList();
super.initState();
}
#override
Widget build(BuildContext context) {
return ScopedModelDescendant<MainModel>(
builder: (BuildContext context, Widget child, MainModel model) {
return Scaffold(
appBar: AppBar(),
body: CustomScrollView(
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate([
TextField(
controller: controller,
),
FlatButton(
child: Text('Submit'),
onPressed: () {
model.addItem(
id: controller.text,
name: controller.text,
weight: controller.text,
);
},
),
]),
),
model.items.length > 0
? SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Dismissible(
key: Key(model.items[index].id),
background: Container(
color: Colors.redAccent,
),
onDismissed: (DismissDirection direction) {
model.deleteProduct();
},
child: ListTile(
leading: Text(model.items[index].name),
trailing: Text(model.items[index].weight),
onTap: () {},
),
);
},
childCount: model.items.length,
),
)
: SliverFillRemaining(),
],
));
},
);
}
}
My issue is that on the initState the list doesn't appear from the readList() - I'm not sure what I am doing wrong as when I run the print(json.decode(prefs.getString(itemKey)); it calls the list of items that are held from the sharedPreferences as [{weight: HelloWorld, name: HelloWorld, id: HelloWorld}] which looks like it should be fine to decode.
Can anyone help point out what I'm doing wrong? Thanks in advance.
In your code, you have 2 different models:
final MainModel _model = MainModel();
and another model from builder
builder: (BuildContext context, Widget child, MainModel model)
It looks like that there is no connection/link/map between _model and model. You have to link them together like in this discussion.

Flutter JSON duplicate index

I have a list of users that I am reading from JSON.
This is the JSON file:
{
"Dependents": [
{
"Name": "Kim",
"Relationship": "Parent"
},
{
"Name": "Tim",
"Relationship": "Spouse"
}
]
}
This is the model class:
new_fifth_model.dart
class NewFifthModel {
String name;
String relationship;
NewFifthModel(this.name, this.relationship);
}
And this is the class to bring out the users in a list.
NewFifth.dart
import 'package:flutter/material.dart';
import 'package:emas_app/model/new_fifth_model.dart';
import 'dart:convert';
import 'dart:async' show Future;
import 'package:http/http.dart' as http;
final String url = "http://crm.emastpa.com.my/MemberInfo.json";
final int page = 5;
//Future to get list of dependent names
Future<List<NewFifthModel>> fetchUserInfo() async{
var response = await http.get(url, headers: {"Accept": "application/json"});
List data = json.decode(response.body)["Dependents"];
var fifthmodel = <NewFifthModel>[];
data.forEach((f) => fifthmodel.add(new NewFifthModel(f["Name"], f["Relationship"])));
print(fifthmodel);
return fifthmodel;
}
class NewFifth extends StatefulWidget {
#override
_FifthState createState() => _FifthState();
}
class _FifthState extends State<NewFifth> {
List<NewFifthModel> fifthList;
#override
void initState() {
super.initState();
if (fifthList == null) {
fetchUserInfo().then((data) {
this.setState(() {
fifthList = data;
});
});
}
}
#override
Widget build(BuildContext context) {
//body widget
Widget _createBody() {
if(fifthList == null){
return new Center(
child: new CircularProgressIndicator(),
);
}
else{
return new ListView.builder(
shrinkWrap: true,
itemCount: fifthList.length,
itemBuilder: (context, index){
return new Column(
children: fifthList.map((f){
return new Card(
child: new ListTile(
title: new Text(f.name),
subtitle: new Text(f.relationship),
trailing: new Text(index.toString()),
onTap: (){
makeDialog(index.toString());
},
),
);
}).toList(),
);
});
}
}
return new Scaffold(
body: _createBody(),
);
}
}
This is the output on the screen.
The problem I am having (as you can see in the picture) is that the index number I put in the trailing part of the ListTile is duplicating and I really need the index number in order to proceed.
How do I rectify this problem?
Any help is very much appreciated.
you are creating 2 list here, you are recreating a Column with the entire list inside the item build, the ListView.builder is already taking care of iterating on your list using the itemCount.
itemBuilder: (context, index) {
final f = fifthList[index];
return Card(
child: new ListTile(
title: new Text(f.name),
subtitle: new Text(f.relationship),
trailing: new Text(index.toString()),
onTap: (){
makeDialog(index.toString());
},
),
);
}
Looks like you have only 2 items in the JSON object but you are showing 4.
I think you meant to only show 2? If so, in your itemBuilder function, you should do this:
return new ListView.builder(
shrinkWrap: true,
itemCount: fifthList.length,
itemBuilder: (context, index) {
var f = fifthList[index];
return new Card(
child: new ListTile(
title: new Text(f.name),
subtitle: new Text(f.relationship),
trailing: new Text(index.toString()),
onTap: () {
makeDialog(index.toString());
},
),
);
});
You were using .map() which looped through the list again on each item. You had 2 items, so you ended up with 4. If you had 3, it would show 6 items, and so on.