Flutter - Read json from local file and search and update UI - json

I am reading a local json file in a provider
Future<List<ProductModel>> readJsonData() async {
final jsondata = await rootBundle.loadString('jsonData/products.json');
final list = json.decode(jsondata) as List<dynamic>;
var _listOfProducts = list.map((e) => ProductModel.fromJson(e)).toList();
return _listOfProducts;
}
and the search function
void searchProduct(String searchText) {
if (searchText == '') {
return;
}
final products = listProducts.where((product) {
final nameLowerCase = product.name!.toLowerCase();
final searchTextLowerCase = searchText.toLowerCase();
return nameLowerCase.contains(searchTextLowerCase);
}).toList();
_searchString = searchText;
listProducts = products;
notifyListeners();
}
and then rendering it like so:
Consumer<ProductsProvider>(builder: (context, productData, child) {
//print(productData.listProducts.toString());
return FutureBuilder(
future: _productsProvider.readJsonData(),
builder: (context, data) {
/// I BELIEVE THIS IS THE CAUSE WHY THE UI WON'T UPDATE
productData.listProducts = data.data as List<ProductModel>;
return Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: productData.listProducts.length,
itemBuilder: (context, index) => Card(
elevation: 3,
child: ListTile(
title: Text(productData.listProducts[index].name!),
),
),
),
);
});
})
search textfield onChange:
onChanged: (value) {
_productsProvider.searchProduct(value);
},
The search works but the UI doesn't change, the list doesn't get filtered because of the line after the comment (above). How do I deal with this so that I get all the items before search (or when the searchText is empty) and the filtered items(list) when there is a searchText?

Related

How to convert a json data into a List<widgets> in flutter?

a sample of what i have in mind
var url = 'https://www.googleapis.com/books/v1/volumes?q=egg';
Future<BookResponse> getData() async {
var response = await http.get(url);
var responseBody = jsonDecode(response.body);
// print(responseBody);
return BookResponse.fromJson(responseBody);
}
-----
FutureBuilder<BookResponse>(
future: getData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError) print(snapshot.error);
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.items.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title:
Text(snapshot.data.items[index].volumeInfo.title),
));
});
} else
return Center(child: CircularProgressIndicator());
},
)
im trying to create a method that will convert json map into a List<widgets> so i can use it to display items.
i can't find a way to display items as rows and columns.
is it possible to have multiple widgets using FutureBuilder? (one row of books and another row to show authors for example)
if so i will avoid converting it to List<widgets>
Convert JSON data to objects and:
Row(
children: entities.map((entity) => Text(entity.text)).toList();
),
It creates a list with widgets.

Flutter Dart http Type Response is not a subtype of type String error

I'm trying to display pictures I get from a http request but i get this error "Type Response is not a subtype of type String". First i get the recently added albums list then i take the cover art id and put that into the url and send that request to the api. The api sends back an image.
Page:
class RecentlyAddedAlbums extends StatefulWidget {
#override
_RecentlyAddedAlbumsState createState() => _RecentlyAddedAlbumsState();
}
class _RecentlyAddedAlbumsState extends State<RecentlyAddedAlbums> {
Future<List<Album>> albums;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: FutureBuilder(
future: fetchRecentlyAddedAlbums(),
builder: (context, AsyncSnapshot<List<Album>> data) {
switch (data.connectionState) {
case ConnectionState.none:
return Text(
"none",
style: TextStyle(color: Colors.black),
);
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.black),
));
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (data.hasData) {
List<Album> albums = data.data;
return ListView.builder(
itemCount: albums.length,
itemBuilder: (context, index) {
return FutureBuilder(
future: recentAlbumArt(albums[index].coverArt),
builder: (context, AsyncSnapshot data) {
switch (data.connectionState) {
case ConnectionState.none:
return Text(
"none",
style: TextStyle(color: Colors.black),
);
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
Colors.black),
));
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (data.hasData) {
return Image.network(data.data);
/* return GridView.count(
padding: const EdgeInsets.all(20),
crossAxisCount: 2,
children: <Widget>[
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (context, index) {
return ListTile(
title: Image.network(
(data.data)[index]),
);
})
],
); */
}
}
});
},
);
}
}
}),
),
);
}
}
Recently added function:
Future<List<Album>> fetchRecentlyAddedAlbums() async {
try {
var salt = randomToken(6);
var token = makeToken("$password", "$salt");
var uRL =
"$server/rest/getAlbumList?u=$username&t=$token&s=$salt&v=$tapeOutVerison&c=$client$format&type=newest";
var authresponse = await http.get(uRL);
if (authresponse.statusCode == 200) {
var jsondata = jsonDecode(authresponse.body);
var data = apicallFromJson(jsondata);
var aresponse = data.subsonicResponse.albumList.album;
return aresponse;
} else {
return null;
}
} catch (e) {
return null;
}
}
Cover Art Function
Future recentAlbumArt(String coverArtID) async {
try {
var salt = randomToken(6);
var token = makeToken("$password", "$salt");
var uRL =
"$server/rest/getCoverArt/?u=$username&t=$token&s=$salt&v=$tapeOutVerison&c=$client$format&id=$coverArtID";
return await http.get(uRL);
} catch (e) {
print(e);
}
}
Album class:
class Album {
Album({
this.id,
this.parent,
this.isDir,
this.title,
this.album,
this.artist,
this.genre,
this.coverArt,
this.playCount,
this.created,
this.year,
});
String id;
String parent;
bool isDir;
String title;
String album;
String artist;
String genre;
String coverArt;
int playCount;
DateTime created;
int year;
This function returns a http reponse:
return await http.get(uRL);
If you wish to get the content of the response, you need to get the body like so:
var response = await http.get(uRL);
return response.body;

Flutter Futurebuilder snapshot is null

I try to show the results from JSON in a ListView in Flutter with a FutureBuilder.
But the snapshot is null and the message that no data is available shows.
Here I try to fetch the data:
static Future _getBans() async {
Storage.getLoggedToken().then((token) async {
var body = {
"token": token
};
final response = await http.post('${URLS.BASE_URL}/punishments.php', headers: ApiService.header, body: json.encode(body));
if (response.statusCode == 200) {
List<Ban> bans = [];
var jsonData = json.decode(response.body)["bans"];
for(var b in jsonData){
Ban ban = Ban(b["player"], b["reason"], int.parse(b["end"]), b["by"]);
bans.add(ban);
}
print(response.body);
print(bans.length);
return bans;
} else {
return null;
}
});
}
from this JSON response
{"status":1,"msg":"OK","bans":[{"player":"DDOSAttacke","reason":"Hacking","end":"1579275471304","by":"DDOSAttacke"}],"mutes":[]}
My Futurebuilder. Here is snapshot null but the count of the elements is working.
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Aktive Bans'),
),
body: Container(
child: FutureBuilder(
future: _getBans(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(
child: Text('Keine aktiven Ban vorhanden')
),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(snapshot.data[index].player),
);
},
);
}
},
),
),
);
}
Please try this. I think you'll have to use await keyword for the getLoggedToken mothod, the returnwill wait for the post before returning anything. But now you're returning before the getLoggedTokenfinishes his work. That is why you are always receiving null.
static Future _getBans() async {
var token = await Storage.getLoggedToken();
var body = {
"token": token
};
final response = await http.post('${URLS.BASE_URL}/punishments.php', headers: ApiService.header, body: json.encode(body));
if (response.statusCode == 200) {
List<Ban> bans = [];
var jsonData = json.decode(response.body)["bans"];
for(var b in jsonData){
Ban ban = Ban(b["player"], b["reason"], int.parse(b["end"]), b["by"]);
bans.add(ban);
}
print(response.body);
print(bans.length);
return bans;
} else {
return null;
}
}

Why can't I retreive my data? (Flutter-Rest API)

I've been trying to retreive data from my database using REST API. There are not error in my code but there is no data shown in my emulator and it keeps showing circular progress indicator (that means no data)
Is this because of token that I want to get from shared preferences? or something else?
This is my getToken code :
String token;
Future<String> getToken() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String token = prefs.getString("token");
return token;
}
This is my ShowPost code :
Future<List<Post>> showPosts() async {
String token = await getToken();
var data = await http.get(
"https://api-wisapedia.herokuapp.com/posts?sortBy=createdAt:desc",
headers: {HttpHeaders.authorizationHeader: 'Bearer $token'},
);
var dataDecoded = json.decode(data.body);
List<Post> posts = List();
dataDecoded.forEach((post) {
post.add(post["destination"], post["owner"], post["image"]);
});
return posts;
}
And this is my body code :
FutureBuilder(
future: showPosts(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Card(
color: Color(0xFFE1F5FE),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: Image(
image: AssetImage('lib/images/pantai.jpg')),
title: Text(snapshot.data[index].title),
subtitle: Text(snapshot.data[index].destination),
),
ButtonTheme.bar(
// make buttons use the appropriate styles for cards
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('DETAILS'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return DetailPost();
}),
);
},
),
FlatButton(
child: const Text('JOIN'),
onPressed: () {
/* ... */
},
),
],
),
),
],
),
);
});
} else {
return Align(
alignment: FractionalOffset.center,
child: CircularProgressIndicator(),
);
}
})
How can I solve this?
remove the () in the future parameter of the future builder
future : showPosts not future :showPosts().
Edit :
replace FutureBuilder with FutureBuilder<List<Post>> and AsyncSnapshot with AsyncSnapshot<List<Post>> to specify the data type of the incoming data.

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.