How to put the json response in widget flutter - flutter

I want to use my response data show in the widget. I post the data to my API then I get the return result.
My response.body is like this:
{
"responses": [
{
"Contents": {
"Images": [
{"url":"https:URL"},
{"url":"https:URL"},
],
"Ages": [
{"age":"33"},
{"age":"34"},
],
"Labels":[
{"label":"game"}
]
}
}
]
}
My question is how to get the "Images", "Ages", and "Labels" details? I want to use these detail and put in my widget. Can I know how to do it?

It's not mandatory to make full models in Dart for decoding JSON.
You can simply do:
// Requires import
import 'dart:convert';
// Do like this
var data = jsonDecode(jsonString);
Now data will automatically act as a map.
For example, if you want to access the label in the JSON example you provided, you can simply do:
data['responses'][0]['Contents']['Labels'][0]['label'].toString();
Coming to the actual part,
You need to shape your widgets according to your needs.
Create a Stateless or StatefulWidget whatever suits your needs and start laying out the design.
According to the JSON example that you posted, you need to show a list of images. So, you would probably generate a list of the Widgets based on the URLs and provide the list to perhaps a GridView in your build method.
Check this example I made: DartPad - JSON Widget example
Edit:
I used the exact JSON response you posted. It doesn't give me any errors.
class _MyWidgetState extends State<MyWidget> {
final String mJson = '''
{
"responses": [
{
"Contents": {
"Images": [
{"url":"https:URL"},
{"url":"https:URL"}
],
"Ages": [
{"age":"33"},
{"age":"34"}
],
"Labels":[
{"label":"game"}
]
}
}
]
}
''';
bool _loading = false;
List<String> _infos = [];
Widget _loadingBar() {
return SizedBox.fromSize(
size: Size.fromRadius(30),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
Colors.white,
),
),
));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('MY APPS'), actions: <Widget>[
_loading
? _loadingBar()
: IconButton(
icon: Icon(Icons.send),
onPressed: () => {
submit(context)
.then((res) => setState(() => _loading = false))
}),
]),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: List.generate(_infos.length, (i) => Text(_infos[i]))),
));
}
Future<void> submit(BuildContext context) async {
setState(() => _loading = true);
await Future.delayed(const Duration(seconds: 3));
var data = jsonDecode(mJson);
print(data);
_infos.add(data['responses'][0].toString());
_infos.add(data['responses'][0]['Contents'].toString());
_infos.add(data['responses'][0]['Contents']['Images'][0]['url'].toString());
_infos.add(data['responses'][0]['Contents']['Ages'][0]['age'].toString());
}
}
Note: The code you posted later shouldn't work as you have declared non-final variables in a StatelessWidget class and also using setState. Also, in your Future submit(BuildContext context) you were supposed to jsonDecode(res) not demoJson. demoJson was not assigned. See the example I posted. I changed it to a stateful widget. I am having no trouble accessing any of the fields in the JSON example you gave. Your JSON example is a bit faulty though, it has extra ',' that might give error while decoding.

Just check the below example that i have made :
Follwing is the json file that you provided i have parsed locally.
{
"responses": [{
"Contents": {
"Images": [{
"url": "https:URL"
},
{
"url": "https:URL"
}
],
"Ages": [{
"age": "33"
},
{
"age": "34"
}
],
"Labels": [{
"age": "33"
}
]
}
}]
}
Model class created for the json file:
// To parse this JSON data, do
//
// final response = responseFromJson(jsonString);
import 'dart:convert';
Response responseFromJson(String str) => Response.fromJson(json.decode(str));
String responseToJson(Response data) => json.encode(data.toJson());
class Response {
List<ResponseElement> responses;
Response({
this.responses,
});
factory Response.fromJson(Map<String, dynamic> json) => Response(
responses: List<ResponseElement>.from(json["responses"].map((x) => ResponseElement.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"responses": List<dynamic>.from(responses.map((x) => x.toJson())),
};
}
class ResponseElement {
Contents contents;
ResponseElement({
this.contents,
});
factory ResponseElement.fromJson(Map<String, dynamic> json) => ResponseElement(
contents: Contents.fromJson(json["Contents"]),
);
Map<String, dynamic> toJson() => {
"Contents": contents.toJson(),
};
}
class Contents {
List<Image1> images;
List<Age> ages;
List<Age> labels;
Contents({
this.images,
this.ages,
this.labels,
});
factory Contents.fromJson(Map<String, dynamic> json) => Contents(
images: List<Image1>.from(json["Images"].map((x) => Image1.fromJson(x))),
ages: List<Age>.from(json["Ages"].map((x) => Age.fromJson(x))),
labels: List<Age>.from(json["Labels"].map((x) => Age.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"Images": List<dynamic>.from(images.map((x) => x.toJson())),
"Ages": List<dynamic>.from(ages.map((x) => x.toJson())),
"Labels": List<dynamic>.from(labels.map((x) => x.toJson())),
};
}
class Age {
String age;
Age({
this.age,
});
factory Age.fromJson(Map<String, dynamic> json) => Age(
age: json["age"],
);
Map<String, dynamic> toJson() => {
"age": age,
};
}
class Image1 {
String url;
Image1({
this.url,
});
factory Image1.fromJson(Map<String, dynamic> json) => Image1(
url: json["url"],
);
Map<String, dynamic> toJson() => {
"url": url,
};
}
This is the main UI file where you fetch and show the data, I have show your data in the list view.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:sample_project_for_api/model.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool isLoading = false;
List<Age> ages = List();
List<Age> labels = List();
List<Image1> image = List();
// here i have taken the json locally which you posted on stack
Future<String> loadFromAssets() async {
return await rootBundle.loadString('json/parse.json');
}
#override
void initState() {
super.initState();
givenFunction();
}
Future givenFunction() async {
setState(() {
isLoading = true;
});
//http.Response response = await http.post(url, body: json.encode(data));
// you can make the http call above just uncomment is and comment the below line
String jsonString = await loadFromAssets();
// Here you can just replace down your response.body with jsonString
final response = responseFromJson(jsonString);
print(response.responses[0].contents.ages[0].age);
//ages =response.responses[0].contents.
for (int i = 0; i < response.responses[0].contents.ages.length; i++) {
ages.add(response.responses[0].contents.ages[i]);
}
for (int i = 0; i < response.responses[0].contents.images.length; i++) {
image.add(response.responses[0].contents.images[i]);
}
for (int i = 0; i < response.responses[0].contents.labels.length; i++) {
labels.add(response.responses[0].contents.labels[i]);
}
setState(() {
isLoading = false;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: isLoading
? CircularProgressIndicator()
: SafeArea(
child: Container(
child: Column(
children: <Widget>[
Container(
width: 400,
height: 100,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: ages.length,
itemBuilder: (BuildContext context, int index) {
return Container(
width: 100,
height: 100,
child: Card(
elevation: 10,
child: Center(child: Text(ages[index].age)),
),
);
},
),
),
Container(
width: 500,
height: 200,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: image.length,
itemBuilder: (BuildContext context, int index) {
return Container(
width: 100,
height: 100,
child: Card(
elevation: 10,
child: Center(child: Text(image[index].url)),
),
);
},
),
),
Container(
width: 500,
height: 200,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: labels.length,
itemBuilder: (BuildContext context, int index) {
return Container(
width: 100,
height: 100,
child: Card(
elevation: 10,
child: Center(child: Text(labels[index].age)),
),
);
},
),
)
],
),
),
)),
);
}
}
void main() {
runApp(HomePage());
}
just taken a screen shot:
Just check it and let me know.

Related

The improper use of a GetX has been detected when using RxList

I have created an observable list and then storing data in the list from the api as below
class FeedsController extends GetxController {
final Rx<List<Stories>> _stories = Rx<List<Stories>>([]);
#override
void onInit() {
super.onInit();
getActiveStories();
}
List<Stories> get getStories {
return _stories.value;
}
Future<List<Stories>> getActiveStories() async {
var url = Uri.parse(storiesURL);
Map<String, Object> params = {'apikey': apiKey, 'userid': "8"};
await http.post(url, body: params).then((value) {
StoriesResponse storiesResponse = storiesResponseFromJson(value.body);
_stories.value = storiesResponse.stories;
}).onError((error, stackTrace) {
debugPrint('Error occurred while fetching stories response: ${error.toString()}');
});
return _stories.value;
}
}
Here is the code for displaying the Stories List
class _ActiveStoriesListState extends State<ActiveStoriesList> {
List<Story> _currentUserStories = [];
final FeedsController _feedsController = Get.find();
#override
void initState() {
Future.delayed(Duration.zero, fetchUserStories);
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Active Stories',
style: titleLargeTextStyle.copyWith(fontSize: 22, fontWeight: FontWeight.w600),),
const SizedBox(height: 10),
SizedBox(
height: 100,
child: Obx(
() => ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemBuilder: (ctx, index) =>
ActiveStoriesWidget(story: _currentUserStories[index]),
itemCount: _currentUserStories.length,
),
)),
],
);
}
void fetchUserStories() async {
List<Stories> stories = _feedsController.getStories;
stories = stories.where((story) => story.userId == '8').toList();
_currentUserStories = stories[0].story;
debugPrint('Active Stories Page: ${_currentUserStories.length}');
}
}
Solutions I have tried is that make only ListView observable (that didn't work), making ListView parent child Observable that also didn't work. I'm unable to understand where I'm missing. Below is the exception
Exception Screenshot
sample json data
{ "status": 200, "success": [ { "user_id": "4", "first_name": "Abu", "profileImage": "jayspur.com/RallyApp/public/uploads/userImages/…", "story": [ { "story": "jayspur.com/RallyApp/public/uploads/userStories/…", "addeddate": "2023-02-08 09:58:11" } ] } ] }
You are getting this error because you are not using any observable list inside your ListView.builder.
But before that you should convert your StatefullWidget to a StatelessWidget because in GetX, we don't need any StatefullWidget.
You can try the following code.
Controller
class FeedsController extends GetxController {
final Rx<List<Stories>> _stories = Rx<List<Stories>>([]);
List<Stories> currUserstories = [];
RxBool isLoading = false.obs;
#override
void onInit() {
super.onInit();
getActiveStories();
}
List<Stories> get getStories {
return _stories.value;
}
Future<List<Stories>> getActiveStories() async {
isLoading.value = true;
var url = Uri.parse("storiesURL");
Map<String, Object> params = {'apikey': apiKey, 'userid': "8"};
await http.post(url, body: params).then((value) {
StoriesResponse storiesResponse = storiesResponseFromJson(value.body);
_stories.value = storiesResponse.stories;
_stories.value =
_stories.value.where((story) => story.userId == '8').toList();
currUserstories = _stories.value[0];
}).onError((error, stackTrace) {
debugPrint(
'Error occurred while fetching stories response: ${error.toString()}');
});
isLoading.value = false;
return _stories.value;
}
}
View file:
class ActiveStoriesList extends StatelessWidget {
ActiveStoriesList({super.key});
final FeedsController _feedsController = Get.find();
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Active Stories',
style: titleLargeTextStyle.copyWith(
fontSize: 22, fontWeight: FontWeight.w600),
),
const SizedBox(height: 10),
SizedBox(
height: 100,
child: Obx(
() => _feedsController.isLoading.value
? const Center(
child: CircularProgressIndicator(),
)
: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemBuilder: (ctx, index) => ActiveStoriesWidget(
story: _feedsController.currUserstories[index]),
itemCount: _feedsController.currUserstories.length,
),
)),
],
);
}
}
You might have to tweak the code a bit but the core concept it you should do all your work inside the controller and only fetch the data in view file.
Also, you should only use the lifecycle which controller provides. Eg. onInit instead of initState.
If this dosen't work, try to modify your controller file such that you get the value in the list as you want in the controller file itself and you the list which was preseneted in controller in your view file.
Hope it helps.

The argument type 'RxList<Streams>' can't be assigned to the parameter type 'Stream<Streams>?'

I am using streambuilder to fetch data but I keep getting its err
The argument type 'RxList' can't be assigned to the parameter type 'Stream?'
Here is my StreamBuilder
StreamBuilder<Streams>(
stream: _streamsController.streamList,
builder: (
BuildContext context,
AsyncSnapshot<Streams> snapshot,
) {
if (ConnectionState.waiting == snapshot.connectionState) {
return Center(
child: CircularProgressIndicator(
color: ProjectColors.primary,
),
);
} else if (snapshot.hasData) {
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: _streamsController.streamList.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(right: width * 0.02),
child: GestureDetector(
onTap: () => Get.to(
() => DetailsScreen(
stream:
_streamsController.streamList[index],
),
arguments: [
_streamByBooks.books[index].title,
_streamsController
.streamList[index].description,
],
),
child: Column(
children: [
Image.asset(
_streamByBooks.books
.map((e) => e.image)
.toList()[index],
height: height * 0.25,
width: width * 0.3,
fit: BoxFit.cover,
),
SizedBox(height: height * 0.007),
Text(
_streamsController.streamList
.map((element) => element.stream)
.toList()[index]
.toString(),
style: TextStyle(
color: ProjectColors.black,
),
),
],
),
),
);
},
);
} else {
return Text('State: ${snapshot.connectionState}');
}
},
)
Here is my Stream Controller class
class StreamsController extends GetxController {
RxList<Streams> streamList = <Streams>[].obs;
void getStreams() async {
List<Streams> streamVariable = await RetrieveStreams.fetchStreams();
streamList.value = streamVariable;
}
#override
void onInit() {
getStreams();
super.onInit();
}
}
The Stream is a model class and here is that class
List<Streams> streamsListFromJson(String str) =>
(json.decode(str) as List<dynamic>)
.map((s) => Streams.fromJson(s))
.toList();
String streamsListToJson(List<Streams> data) =>
json.encode(data.map((s) => s.toJson()).toList());
class Streams {
Streams({
required this.title,
required this.stream,
});
String title;
String stream;
factory Streams.fromJson(Map<String, dynamic> json) => Streams(
title: json["title"],
stream: json["stream"],
);
Map<String, dynamic> toJson() => {
"title": title,
"stream": stream,
};
}
your stream is
stream: _streamsController.streamList,
and your streamList datatype is
RxList<Streams>
and StreamBuldir received type of
<Streams>
so its not match
//////////////////////////////////////////////////////////////////////////
try this
class StreamsController extends GetxController {
RxList<Streams> _streamList =[].obs;
get streamList async*{
yield _streamList.value;
//it will return List<Streams> as a stream}
void getStreams() async {
List<Streams> streamVariable = await RetrieveStreams.fetchStreams();
streamList.value = streamVariable;
}
#override
void onInit() {
getStreams();
super.onInit();
}
}
try this Example to know more about stream:
import 'dart:math';
void main() {
getRandomValues().listen((value) {
print(value);
});
}
Stream<String> getRandomValues() async* {
var random = Random(2);
while (true) {
await Future.delayed(Duration(seconds: 1));
yield "1st: ${random.nextDouble()}";
}
}

Flutter - Argument type 'List<List<SubCategoriesFolder>> Function(String)' can't be assigned to the parameter type

I'm trying to apply the function compute to get the data from responseJSON.
I used quicktype.io to generate the function for this purpose.
This is what i have done so far:
final subCategoriesFolder = await compute(subCategoriesFolderFromJson, responseJSON);
Error 1:
error: The argument type 'List<List<SubCategoriesFolder>> Function(String)' can't be assigned to the parameter type 'FutureOr<List<List<SubCategoriesFolder>>> Function(dynamic)'. (argument_type_not_assignable at lib\src\ui\pages\subcategories.dart:84)
Error 2:
error: Couldn't infer type parameter 'Q'.
Tried to infer 'dynamic' for 'Q' which doesn't work:
Parameter 'callback' declared as 'FutureOr<R> Function(Q)'
but argument is 'List<List<SubCategoriesFolder>> Function(String)'.
The type 'dynamic' was inferred from:
Parameter 'message' declared as 'Q'
but argument is 'dynamic'.
Consider passing explicit type argument(s) to the generic.
(could_not_infer at lib\src\ui\pages\subcategories.dart:84)
Source:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter/foundation.dart';
// import 'dart:isolate';
// import 'package:flutter/foundation.dart';
// import 'catwidget.dart';
// To parse this JSON data, do
//
// final subCategoriesFolder = subCategoriesFolderFromJson(jsonString);
List<List<SubCategoriesFolder>> subCategoriesFolderFromJson(String str) => List<List<SubCategoriesFolder>>.from(json.decode(str).map((x) => List<SubCategoriesFolder>.from(x.map((x) => SubCategoriesFolder.fromJson(x)))));
String subCategoriesFolderToJson(List<List<SubCategoriesFolder>> data) => json.encode(List<dynamic>.from(data.map((x) => List<dynamic>.from(x.map((x) => x.toJson())))));
class SubCategoriesFolder {
SubCategoriesFolder({
this.children,
this.title,
});
List<Child> children;
String title;
factory SubCategoriesFolder.fromJson(Map<String, dynamic> json) => SubCategoriesFolder(
children: List<Child>.from(json["children"].map((x) => Child.fromJson(x))),
title: json["title"],
);
Map<String, dynamic> toJson() => {
"children": List<dynamic>.from(children.map((x) => x.toJson())),
"title": title,
};
}
class Child {
Child({
this.title,
this.uri,
this.iconuri,
this.charset,
});
String title;
String uri;
String iconuri;
String charset;
factory Child.fromJson(Map<String, dynamic> json) => Child(
title: json["title"],
uri: json["uri"],
iconuri: json["iconuri"] == null ? null : json["iconuri"],
charset: json["charset"] == null ? null : json["charset"],
);
Map<String, dynamic> toJson() => {
"title": title,
"uri": uri,
"iconuri": iconuri == null ? null : iconuri,
"charset": charset == null ? null : charset,
};
}
class Subfolder extends StatefulWidget {
#override
SubFolder createState() => SubFolder();
}
class SubFolder extends State<Subfolder> {
Future _fetchJSON() async {
var response = await http.get(
"http://10.0.2.2:5000/subcategories",
headers: {"Accept": "application/json"},
);
if (response.statusCode == 200) {
String responseBody = response.body;
//var responseJSON = json.decode(responseBody);
var responseJSON = await compute(jsonDecode, responseBody);
//final subCategoriesFolder = subCategoriesFolderFromJson(responseJSON);
final subCategoriesFolder = await compute(subCategoriesFolderFromJson, responseJSON);
print(subCategoriesFolder.runtimeType);//THis is not being set, error here
print(subCategoriesFolder);
// name = responseJSON['name'];
// avatar = responseJSON['avatar_url'];
}
return true;
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: CupertinoColors.darkBackgroundGray,
body: FutureBuilder(
future: _fetchJSON(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data == null) {
return Center(child: Text('No data'));
} else
return Container(
child: Text(snapshot.data),
);
/*return GridView(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
children: <Widget>[
for(var item in snapshot.data ) singleCategoryTemp(item)
],
); */
} else if (snapshot.connectionState == snapshot.error) {
return Center(
child: Text('Error: Please try again later')); // error
} else {
return Center(child: CircularProgressIndicator()); // loading
}
}),
);
}
}
You can copy paste run full code below
You can use compute(subCategoriesFolderFromJson, responseBody)
I use nested ListView to show data, you check full code
code snippet
Future<List<List<SubCategoriesFolder>>> _fetchJSON() async {
...
if (response.statusCode == 200) {
String responseBody = response.body;
return compute(subCategoriesFolderFromJson, responseBody);
}
working demo
full code
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
List<List<SubCategoriesFolder>> subCategoriesFolderFromJson(String str) =>
List<List<SubCategoriesFolder>>.from(json.decode(str).map((x) =>
List<SubCategoriesFolder>.from(
x.map((x) => SubCategoriesFolder.fromJson(x)))));
String subCategoriesFolderToJson(List<List<SubCategoriesFolder>> data) =>
json.encode(List<dynamic>.from(
data.map((x) => List<dynamic>.from(x.map((x) => x.toJson())))));
class SubCategoriesFolder {
SubCategoriesFolder({
this.children,
this.title,
});
List<Child> children;
String title;
factory SubCategoriesFolder.fromJson(Map<String, dynamic> json) =>
SubCategoriesFolder(
children:
List<Child>.from(json["children"].map((x) => Child.fromJson(x))),
title: json["title"],
);
Map<String, dynamic> toJson() => {
"children": List<dynamic>.from(children.map((x) => x.toJson())),
"title": title,
};
}
class Child {
Child({
this.title,
this.uri,
this.iconuri,
this.charset,
});
String title;
String uri;
String iconuri;
String charset;
factory Child.fromJson(Map<String, dynamic> json) => Child(
title: json["title"],
uri: json["uri"],
iconuri: json["iconuri"] == null ? null : json["iconuri"],
charset: json["charset"] == null ? null : json["charset"],
);
Map<String, dynamic> toJson() => {
"title": title,
"uri": uri,
"iconuri": iconuri == null ? null : iconuri,
"charset": charset == null ? null : charset,
};
}
class Subfolder extends StatefulWidget {
#override
SubFolder createState() => SubFolder();
}
class SubFolder extends State<Subfolder> {
Future<List<List<SubCategoriesFolder>>> _fetchJSON() async {
/*var response = await http.get(
"http://10.0.2.2:5000/subcategories",
headers: {"Accept": "application/json"},
);*/
String jsonString = '''
[[
{
"title" : "1",
"children" : [{
"title":"abc1",
"uri" : "def1",
"iconuri" : "123",
"charset" : "456"
},
{
"title":"abc2",
"uri" : "def1",
"iconuri" : "123",
"charset" : "456"
}]
},
{
"title" : "2",
"children" : [{
"title":"abc2",
"uri" : "def2",
"iconuri" : "789",
"charset" : "321"
}]
}
]]
''';
http.Response response = http.Response(jsonString, 200);
if (response.statusCode == 200) {
String responseBody = response.body;
return compute(subCategoriesFolderFromJson, responseBody);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
//backgroundColor: CupertinoColors.darkBackgroundGray,
body: FutureBuilder(
future: _fetchJSON(),
builder: (BuildContext context,
AsyncSnapshot<List<List<SubCategoriesFolder>>> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snapshot.hasError) {
return Text(
'${snapshot.error}',
style: TextStyle(color: Colors.red),
);
} else {
return ListView.separated(
separatorBuilder: (BuildContext context, int index) {
return SizedBox(
height: 10,
);
},
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
/* Expanded(
flex: 1, child: Text(snapshot.data[index].toString())),*/
Expanded(
flex: 2,
child: Container(
height: 50,
child: ListView.separated(
separatorBuilder:
(BuildContext context,
int index) {
return SizedBox(
width: 10,
);
},
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount:
snapshot.data[index].length,
itemBuilder: (context, index1) {
return Row(
mainAxisAlignment:
MainAxisAlignment.center,
crossAxisAlignment:
CrossAxisAlignment.center,
children: <Widget>[
Container(
width: 120,
child: Column(
children: <Widget>[
Text(
snapshot
.data[index]
[index1]
.title,
style: TextStyle(
color:
Colors.red),
),
Expanded(
child: ListView
.separated(
separatorBuilder:
(BuildContext
context,
int
index) {
return SizedBox(
width:
2,
);
},
shrinkWrap:
true,
scrollDirection:
Axis
.horizontal,
itemCount: snapshot
.data[
index]
[
index1]
.children
.length,
itemBuilder:
(context,
index2) {
return Row(
mainAxisAlignment: MainAxisAlignment
.spaceEvenly,
children: <
Widget>[
Text(snapshot.data[index][index1].children[index2].title,
style: TextStyle(color: Colors.red)),
]);
}),
),
],
),
)
]);
}),
),
)
])
],
);
});
}
}
}));
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Subfolder(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

How to add list view builder inside another list view builder?

This is my list view widget. There are two list view builders, one inside another. I added shrinkWrap property and physics property. Nothing is rendered.I have another doubt when to use list view, single child scroll view and custom scroll view.
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("Listviews"),
backgroundColor: Colors.blue,
),
body: ListView.builder(
shrinkWrap: true,
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, int index) {
if (data[index]["type"] == "single") {
var innerData = data[index]["data"];
return Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: innerData == null ? 0 : innerData.length,
itemBuilder: (BuildContext context, int index) {
String title = innerData[index]["title"];
return Text("$title");
},
),
);
}
},
),
);
}
This is the output screen
This is my json response:
[
{
"type": "single",
"data": [
{
"title": "Fresh Vegetables"
},
{
"title": "Fresh Fruits"
},
{
"title": "Cuts and Sprouts"
},
{
"title": "Exotic Center"
}
]
}
]
I want to do like the flipkart home page. I want to build widgets based on the response. What is the widgets should I use?
Use physics property inside listViewBuilder
shrinkWrap: true,
physics: ClampingScrollPhysics(), /// listView scrolls
I some how copy pasted your code and made some modifications and it worked for me just check the code i have modified :
I have loaded your json locally mentioned below:
[
{
"type": "single",
"data": [
{
"title": "Fresh Vegetables"
},
{
"title": "Fresh Fruits"
},
{
"title": "Cuts and Sprouts"
},
{
"title": "Exotic Center"
}
]
}
]
According to you json class i have created a model class where you can access the specific object from the listview using this model class :
// To parse this JSON data, do
//
// final data = dataFromJson(jsonString);
import 'dart:convert';
List<Data> dataFromJson(String str) => List<Data>.from(json.decode(str).map((x) => Data.fromJson(x)));
String dataToJson(List<Data> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Data {
String type;
List<Datum> data;
Data({
this.type,
this.data,
});
factory Data.fromJson(Map<String, dynamic> json) => Data(
type: json["type"],
data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"type": type,
"data": List<dynamic>.from(data.map((x) => x.toJson())),
};
}
class Datum {
String title;
Datum({
this.title,
});
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
title: json["title"],
);
Map<String, dynamic> toJson() => {
"title": title,
};
}
And just check the main file where i have made the changes :
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:sample_testing_project/models.dart';
main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<Data> data = List();
bool _isLoading = false;
#override
void initState() {
// TODO: implement initState
super.initState();
loadYourData();
}
Future<String> loadFromAssets() async {
return await rootBundle.loadString('json/parse.json');
}
loadYourData() async {
setState(() {
_isLoading = true;
});
// Loading your json locally you can make an api call, when you get the response just pass it to the productListFromJson method
String jsonString = await loadFromAssets();
final datamodel = dataFromJson(jsonString);
data = datamodel;
setState(() {
_isLoading = false;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: new Scaffold(
appBar: AppBar(
title: Text("Listviews"),
backgroundColor: Colors.blue,
),
body: ListView.builder(
shrinkWrap: true,
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, int index) {
if (data[index].type == "single") {
var innerData = data[index].data;
return Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: innerData == null ? 0 : innerData.length,
itemBuilder: (BuildContext context, int index) {
String title = innerData[index].title;
return Container(
width: MediaQuery.of(context).size.width,
child: Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("$title"),
),
),
);
},
),
);
}
},
),
),
);
}
}

How to do scroll control in flutter by passing the data fetched from api as pageno

How to make scroll control by sending the next page parameter as pageno in url. I have url where I am sending the pageno as pageno='', and to get the next page I need to pass the data which is returned from the future fetchpost. For example I have initial url as
"https://example.org/api.php&action=query&gcmtitle=Category:$cname&pilimit=max&generator=categorymembers&format=json&gcmcontinue=$pageno where pageno=''
and second url to get next page content is
https://example.org/api.php&action=query&gcmtitle=Category:$cname&pithumbsize=600&generator=categorymembers&format=json&gcmcontinue=$pageno
where pageno='some string value'.
I have written code but the when I scroll to the end of the page I am passing next page data to url and it wipes out the data of first page and displays next page page content list on it instead continuing with the list below the firstpage.
Below is my code please help me with the issue.
class Herbs extends StatefulWidget {
final String title;
Herbs(this.title);
#override
_HerbsState createState() => new _HerbsState();
}
class _HerbsState extends State<Herbs> {
List<Herbslist> herbslist = [];
var cname;
var gcm;
var pageno = '';
ScrollController _scrollController = new ScrollController();
#override
void initState() {
fetchPost(pageno);
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
setState(() {
pageno = gcm;
fetchPost(pageno);
});
}
});
super.initState();
}
Future<Herbslist> fetchPost(String pageno) async {
final response = await http.get(
'https://example.org/api.php?action=query&gcmtitle=Category:$cname&pilimit=max&prop=pageimages&pithumbsize=600&generator=categorymembers&format=json&gcmcontinue=$pageno'
);
if (response.statusCode == 200) {
if(this.mounted){
var jsonData = json.decode(response.body);
gcm = jsonData["continue"]["gcmcontinue"];
}
} else {
print(Exception);
throw (e) {
print("Exception thrown: $e");
Exception(e);
};
}
return Herbslist.fromJson(json.decode(response.body));
}
#override
void dispose() {
_scrollController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
cname = widget.title;
return new Scaffold(
appBar: AppBar(
title: Align(
alignment: Alignment(-0.2, 0.3),
child: Text(
cname,
),
),
),
body: Center(
child: FutureBuilder<Herbslist>(
future: fetchPost(gcm),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
shrinkWrap: true,
controller: _scrollController,
itemCount: snapshot.data.query.pages.length,
itemBuilder: (BuildContext context, int index) {
var img = snapshot.data.query.pages.values.toList()[index].thumbnail.source;
return Container(
child: Card(
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Detailpage(
snapshot.data.query.pages.values.toList()[index].title,
),
));
},
child: ListTile(
contentPadding: EdgeInsets.symmetric(
horizontal: 8.0, vertical: 8.0),
leading: Container(
padding: EdgeInsets.only(right: 10.0),
decoration: new BoxDecoration(
border: new Border(
right: new BorderSide(
width: 1.5, color: Colors.grey)),
),
child: img == img.isEmpty
? SizedBox(height: 50.0,width: 50.0,child: Image.asset('images/ayurwiki.png'),)
:SizedBox(
height: 50.0,
width: 50.0,
child: FadeInImage.assetNetwork(
placeholder: 'images.png',
image: img,
fit: BoxFit.fill,
),
)
),
title: Text(
snapshot.data.query.pages.values.toList()[index].title),
),
)));
},
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
));
}
}
Herbslist.dart
class Herbslist {
String batchcomplete;
Continue herbslistContinue;
Limits limits;
Query query;
Herbslist({
this.batchcomplete,
this.herbslistContinue,
this.limits,
this.query,
});
factory Herbslist.fromJson(Map<String, dynamic> json) => Herbslist(
batchcomplete: json["batchcomplete"],
herbslistContinue: Continue.fromJson(json["continue"]),
limits: Limits.fromJson(json["limits"]),
query: Query.fromJson(json["query"]),
);
Map<String, dynamic> toJson() => {
"batchcomplete": batchcomplete,
"continue": herbslistContinue.toJson(),
"limits": limits.toJson(),
"query": query.toJson(),
};
}
class Continue {
String gcmcontinue;
String continueContinue;
Continue({
this.gcmcontinue,
this.continueContinue,
});
factory Continue.fromJson(Map<String, dynamic> json) => Continue(
gcmcontinue: json["gcmcontinue"],
continueContinue: json["continue"],
);
Map<String, dynamic> toJson() => {
"gcmcontinue": gcmcontinue,
"continue": continueContinue,
};
}
class Limits {
int categorymembers;
int pageimages;
Limits({
this.categorymembers,
this.pageimages,
});
factory Limits.fromJson(Map<String, dynamic> json) => Limits(
categorymembers: json["categorymembers"],
pageimages: json["pageimages"],
);
Map<String, dynamic> toJson() => {
"categorymembers": categorymembers,
"pageimages": pageimages,
};
}
class Query {
Map<String, Page> pages;
Query({
this.pages,
});
factory Query.fromJson(Map<String, dynamic> json) => Query(
pages: Map.from(json["pages"]).map((k, v) => MapEntry<String, Page>(k, Page.fromJson(v))),
);
Map<String, dynamic> toJson() => {
"pages": Map.from(pages).map((k, v) => MapEntry<String, dynamic>(k, v.toJson())),
};
}
class Page {
int pageid;
int ns;
String title;
Thumbnail thumbnail;
String pageimage;
Page({
this.pageid,
this.ns,
this.title,
this.thumbnail,
this.pageimage,
});
factory Page.fromJson(Map<String, dynamic> json) => Page(
pageid: json["pageid"],
ns: json["ns"],
title: json["title"],
thumbnail: json["thumbnail"] == null ? Thumbnail(source:'',width:0,height:0) : Thumbnail.fromJson(json["thumbnail"]),
pageimage: json["pageimage"] == null ? null : json["pageimage"],
);
Map<String, dynamic> toJson() => {
"pageid": pageid,
"ns": ns,
"title": title,
"thumbnail": thumbnail == null ? null : thumbnail.toJson(),
"pageimage": pageimage == null ? null : pageimage,
};
}
class Thumbnail {
String source;
int width;
int height;
Thumbnail({
this.source,
this.width,
this.height,
});
factory Thumbnail.fromJson(Map<String, dynamic> json) => Thumbnail(
source: json["source"],
width: json["width"],
height: json["height"],
);
Map<String, dynamic> toJson() => {
"source": source,
"width": width,
"height": height,
};
}
You are overriding the list you have. You need to append new data into your list.
Your logic should be like this:
// Declare a list
List<Herbslist> herbslist = [];
// Update the list
herblist.add(Herbslist.fromJson(json.decode(response.body)));
// Return the updated list
return herblist;
Without further information about your Herblist class, I can't guarantee that this would work. You probably need to flat you List of Lists.
Update
Your data structure seems unconvinient for this situation. There are better ways to structure your data and respresent it on the UI. You wrapped iterable data into a class and whenever you get new instance from that class you end up with having two different data stores. Since you pass the newest instance to the UI, only the latest results will show up on the list.
You should have a single data store (eg. a list or map for your API results) and append new data into it. You only have model classes now. They should declare how is your data structured. They shouldn't store any real data.