I want to scroll infinitely as much my product has. I have total 412 pages. When I scroll to the end I want to show more item of next pages. that means infinite scrolling. how to do this? i have implemented the ScrollController.
this is my Api:
https://www.moharaj.com.bd/api/new/collection/products?page=1
this is my model class:
// To parse this JSON data, do
//
// final newCollectionProductModel = newCollectionProductModelFromJson(jsonString);
import 'dart:convert';
NewCollectionProductModel newCollectionProductModelFromJson(String str) =>
NewCollectionProductModel.fromJson(json.decode(str));
String newCollectionProductModelToJson(NewCollectionProductModel data) =>
json.encode(data.toJson());
class NewCollectionProductModel {
NewCollectionProductModel({
required this.currentPage,
required this.data,
required this.firstPageUrl,
required this.from,
required this.lastPage,
required this.lastPageUrl,
required this.nextPageUrl,
required this.path,
required this.perPage,
required this.prevPageUrl,
required this.to,
required this.total,
});
final int currentPage;
final List<Datum> data;
final String firstPageUrl;
final int from;
final int lastPage;
final String lastPageUrl;
final String nextPageUrl;
final String path;
final int perPage;
final dynamic prevPageUrl;
final int to;
final int total;
factory NewCollectionProductModel.fromJson(Map<String, dynamic> json) =>
NewCollectionProductModel(
currentPage: json["current_page"],
data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
firstPageUrl: json["first_page_url"],
from: json["from"],
lastPage: json["last_page"],
lastPageUrl: json["last_page_url"],
nextPageUrl: json["next_page_url"],
path: json["path"],
perPage: json["per_page"],
prevPageUrl: json["prev_page_url"],
to: json["to"],
total: json["total"],
);
Map<String, dynamic> toJson() => {
"current_page": currentPage,
"data": List<dynamic>.from(data.map((x) => x.toJson())),
"first_page_url": firstPageUrl,
"from": from,
"last_page": lastPage,
"last_page_url": lastPageUrl,
"next_page_url": nextPageUrl,
"path": path,
"per_page": perPage,
"prev_page_url": prevPageUrl,
"to": to,
"total": total,
};
}
class Datum {
Datum({
required this.id,
required this.name,
required this.price,
required this.salePrice,
required this.slug,
required this.discount,
required this.thumnail,
required this.productImage,
});
final int id;
final String name;
final String price;
final String salePrice;
final String slug;
final int discount;
final String thumnail;
final List<ProductImage> productImage;
factory Datum.fromJson(Map<String, dynamic> json) => Datum(
id: json["id"],
name: json["name"],
price: json["price"],
salePrice: json["sale_price"],
slug: json["slug"],
discount: json["discount"],
thumnail: json["thumnail"],
productImage: List<ProductImage>.from(
json["product_image"].map((x) => ProductImage.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
"price": price,
"sale_price": salePrice,
"slug": slug,
"discount": discount,
"thumnail": thumnail,
"product_image":
List<dynamic>.from(productImage.map((x) => x.toJson())),
};
}
class ProductImage {
ProductImage({
required this.id,
required this.productId,
required this.productImage,
required this.createdAt,
required this.prefixUrl,
required this.updatedAt,
});
final int id;
final int productId;
final String productImage;
final DateTime createdAt;
final String prefixUrl;
final DateTime updatedAt;
factory ProductImage.fromJson(Map<String, dynamic> json) => ProductImage(
id: json["id"],
productId: json["product_id"],
productImage: json["product_image"],
createdAt: DateTime.parse(json["created_at"]),
prefixUrl: json["prefix_url"],
updatedAt: DateTime.parse(json["updated_at"]),
);
Map<String, dynamic> toJson() => {
"id": id,
"product_id": productId,
"product_image": productImage,
"created_at": createdAt.toIso8601String(),
"prefix_url": prefixUrl,
"updated_at": updatedAt.toIso8601String(),
};
}
this is my Service class Code:
import 'dart:convert';
import 'package:infinite_pagination/NewArrival.dart';
import 'package:infinite_pagination/NewCollectionProductModel.dart';
import 'package:http/http.dart' as http;
/**
* This is a Service Class.
* This Service Class is used for New COllection Product.
*
*/
class NewCollectionProductService {
static var product;
static Future<NewCollectionProductModel>
getNewCollectionProductService() async {
try {
final response = await http.get(Uri.parse(
"https://www.moharaj.com.bd/api/new/collection/products?page=$pageNumber"));
//print(response);
if (response.statusCode == 200 || response.statusCode == 201) {
final decode = jsonDecode(response.body);
product = NewCollectionProductModel.fromJson(decode);
return product;
} else {
return product;
}
} catch (error) {
throw Exception();
}
}
}
this is product Class:
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
var size = 180.0;
var iconSize = 10.0;
Widget Products(String ImgLocation, name, price, discountPrice, discountPercent,
reviews, BuildContext context) {
return Container(
height: 300,
child: Card(
child: Padding(
padding: EdgeInsets.all(5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.network(
'$ImgLocation',
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
return loadingProgress == null
? child
: Center(
child: LinearProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
loadingProgress.expectedTotalBytes!
: null,
));
// : LinearProgressIndicator();
},
width: MediaQuery.of(context).size.width,
height: size,
),
),
),
Text(
'$name',
textAlign: TextAlign.start,
),
Text(
'৳ $price',
style: TextStyle(
fontSize: 15,
color: Colors.red,
fontWeight: FontWeight.bold),
textAlign: TextAlign.left,
),
Text.rich(
TextSpan(
children: <TextSpan>[
// ignore: unnecessary_new
TextSpan(
text: '৳ $discountPercent',
style: const TextStyle(
color: Colors.grey,
decoration: TextDecoration.lineThrough,
),
),
TextSpan(
text: ' -$discountPrice%',
),
],
),
),
Row(
children: [
Icon(
Icons.star,
color: Color(0xFFFfebf50),
size: iconSize,
),
Icon(
Icons.star,
color: Color(0xFFFfebf50),
size: iconSize,
),
Icon(
Icons.star,
color: Color(0xFFFfebf50),
size: iconSize,
),
Icon(
Icons.star,
color: Color(0xFFFfebf50),
size: iconSize,
),
Icon(
Icons.star,
color: Color(0xFFFfee9c3),
size: iconSize,
),
SizedBox(
width: 5,
),
Text(
'($reviews)',
style: TextStyle(fontSize: iconSize),
)
],
)
],
)),
),
);
}
this is my home page:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:infinite_pagination/NewArrivalController.dart';
import 'package:infinite_pagination/NewCollectionProductModel.dart';
import 'package:infinite_pagination/NewCollectionProductService.dart';
import 'package:infinite_pagination/Products.dart';
//------------------------------------------------------------
// this widget is for Upcoming categories
//------------------------------------------------------------
class NewArrival extends StatefulWidget {
#override
State<NewArrival> createState() => _NewArrivalState();
}
var pageNumber = 1;
class _NewArrivalState extends State<NewArrival> {
NewArrivalController newArrivalController = Get.put(NewArrivalController());
late Future<NewCollectionProductModel> getData;
ScrollController scrollController = ScrollController();
#override
void initState() {
getData = NewCollectionProductService.getNewCollectionProductService();
// TODO: implement initState
super.initState();
scrollController.addListener(() {
print(scrollController.position.pixels);
if (scrollController.position.pixels ==
scrollController.position.maxScrollExtent) {
pageNumber++;
print(pageNumber);
}
});
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
scrollController.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Padding(
padding: EdgeInsets.only(left: 10),
child: Text('New Arrival',
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold,
)),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: MaterialButton(
color: Colors.red,
child: Text("View All"),
onPressed: () {}),
)
],
),
Container(
// height: 200,
child: collectionOfData())
],
),
),
),
);
}
collectionOfData() {
return FutureBuilder<NewCollectionProductModel>(
future: getData,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
controller: scrollController,
physics: NeverScrollableScrollPhysics(),
// scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: snapshot.data!.data.length,
// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
// crossAxisCount: 2),
itemBuilder: (context, int index) {
var product = snapshot.data!.data[index];
//slug = product.slug;
String image = product.productImage[0].prefixUrl.toString() +
product.productImage[0].productImage.toString();
return GestureDetector(
onTap: () {},
child: Container(
height: 300,
width: 200,
child: Products(
image,
product.name,
product.price,
product.discount,
product.salePrice,
product.id,
context,
),
),
);
});
} else {
return Center(child: CircularProgressIndicator());
}
});
}
}
you can manage in 2 ways.
without package: manage scrolling point & total page, current page & per page items.
using pagination_view and other packages.
you can also check the below links.
raywenderlich-pagination
mobikul-pageination
stack-overfllow-query
Simple Example without packages:
Declare the below code initially.
ScrollController _scrollController = new ScrollController();
var pageNumber = 1;// update in API CALL
total_page = 0; // update in API CALL
current_page = 0; // update in API CALL
List<ModelClass> arrList = [];
Add below code init method
_scrollController.addListener(() async {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
if (currentPage != total) { // on bottom scroll API Call until last page
currentPage += 1;
apiCall(page: currentPage);
}
}
});
If No data is found & add Pull to refresh widget.
Widget noDataFound() {
return RefreshIndicator(
onRefresh: apiCall(),
child: Center(
child: Text('No Data Found!'),
),
);
}
in Build Widget
arrList.isNotEmpty ? ListView.separated(
padding: const EdgeInsets.all(16.0),
separatorBuilder: (c, index) {
return SizedBox(
height: 20.0,
);
},
physics: AlwaysScrollableScrollPhysics(),
controller: _scrollController,
itemCount: arrList.size + 1,
itemBuilder: (_, index) {
if (index == arrList.length) { // Always one widget 'Loading...' added end of list it will Hide/Show based on condtion.
return Visibility(
visible:
current_page != totalPage ? false:true,
child: Center(
child: Text('Loading...',),
);
} else {
return ... listViewItemWidget;
}
}) : noDataFound()
API Call Function
apiCall(int page = 0){
// update all pagenation variables After API get & map data sucessfully
currentPage , Total page , list value
// Add one condition to store data
if (page == 0) { // If Pull to refresh.
arrList.clear();
arrList = [mapData list from API]; // New data load.
} else {
arrList.add([mapData list from API]); // Append new data in old list
}
}
You can look at the infinite_scroll_pagination package
You can use custom ScrollController to listen when to load more items:
import 'package:flutter/widgets.dart';
class InfiniteListenerController extends ScrollController {
final VoidCallback? onLoadMore;
InfiniteListenerController({this.onLoadMore}) {
if(onLoadMore != null) addListener(_endListener);
}
void _endListener() {
if (position.pixels == position.maxScrollExtent) {
onLoadMore?.call();
}
}
#override
void dispose() {
if(onLoadMore != null) removeListener(_endListener);
super.dispose();
}
}
Related
I am a learner and new to Flutter.
I want help with the followings
I need to get a list of items with checkboxes from and API
I should be able to select a single or multiple checkboxes
If I select a checkbox its should display the total amount selected
if I select two checkboxes, its should sum the total of the two for me and display it.
Below is what I have done so far.
I will greatly appreciate your solutions
here is my code
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:liquid_pull_to_refresh/liquid_pull_to_refresh.dart';
import 'dart:convert';
import 'dart:ffi';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:stachup/models/TeamModel.dart';
import 'package:stachup/models/newStaches.dart';
import 'package:stachup/models/TeamModel.dart';
class Stash extends StatefulWidget {
String first_name;
String token;
String referalcode;
String last_name;
Stash(
{Key? key,
required this.first_name,
required this.token,
required this.last_name,
required this.referalcode})
: super(key: key);
#override
State<Stash> createState() => _StashState();
}
class _StashState extends State<Stash> {
bool isChecked = false;
String? first_name;
String? token;
String? last_name;
String? referalcode;
bool setup = false;
String? setupError;
bool _isChecked = false;
late SharedPreferences logindata;
userData() async {
logindata = await SharedPreferences.getInstance();
first_name = logindata.getString('first_name')!;
last_name = logindata.getString('last_name')!;
referalcode = logindata.getString('referalcode')!;
token = logindata.getString('token')!;
setState(() {});
}
List<Team> teams = [];
Future getData() async {
var response = await http.get(Uri.https(
'incoming.isplitpay.com', 'api/durationlist', {'token': token}));
var jsonData = jsonDecode(response.body);
print(token);
for (var eachTeam in jsonData) {
var datetime = DateTime.parse(eachTeam["paid_date"]);
final team = Team(
id_pes: eachTeam['id_pes'],
amount: eachTeam['amount'],
paid_date: datetime,
);
teams.add(team);
}
// print(teams.length);
}
#override
void initState() {
super.initState();
userData();
}
#override
Widget build(BuildContext context) {
//getData();
return Scaffold(
backgroundColor: Colors.blue.shade50,
body: FutureBuilder(
future: getData(),
builder: ((context, snapshot) {
//if done loading show data
if (snapshot.connectionState == ConnectionState.done) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
),
child: ListView.builder(
itemCount: teams.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Container(
height: 80,
decoration: BoxDecoration(
border: Border.all(
color: Colors.white,
),
color: Colors.grey[50],
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Checkbox(
checkColor: Colors.white,
value: _isChecked,
onChanged: (bool? value) {
setState(() {
_isChecked = value!;
});
print(value);
},
),
Text(
'GHS ' + teams[index].amount,
style: GoogleFonts.lato(
fontSize: 16,
),
),
Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 20.0),
child: Text(
'Savings for',
style: GoogleFonts.lato(
fontSize: 16,
),
),
),
Text(
"${teams[index].paid_date.day.toString().padLeft(2, '0')}-${teams[index].paid_date.month.toString().padLeft(2, '0')}-${teams[index].paid_date.year}",
style: GoogleFonts.lato(
fontSize: 16,
),
),
],
)
],
),
),
);
}),
);
//else show circule progress bar
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}),
),
);
}
}
My Model
import 'package:flutter/material.dart';
class Team {
final String id_pes;
final String amount;
final DateTime paid_date;
Team({
required this.id_pes,
required this.amount,
required this.paid_date,
});
}
class UserProgress {
final int goaldays;
final double progress;
UserProgress({
required this.goaldays,
required this.progress,
});
}
You can add a variable inside the Class Team to control the CheckBox.
The checked list can be filtered out through the List.where() function.
I added the Tooltip() Widget to show the expected effect.
import 'package:flutter/material.dart';
class MyWidget extends StatefulWidget {
const MyWidget({super.key});
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
List<Team> dataList = [
Team(id_pes: '1', amount: '10', paid_date: DateTime.now()),
Team(id_pes: '2', amount: '20', paid_date: DateTime.now()),
Team(id_pes: '3', amount: '30', paid_date: DateTime.now()),
Team(id_pes: '4', amount: '40', paid_date: DateTime.now()),
Team(id_pes: '5', amount: '50', paid_date: DateTime.now()),
Team(id_pes: '6', amount: '60', paid_date: DateTime.now()),
];
Widget _buildListItem(int index) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Container(
height: 80,
decoration: BoxDecoration(
border: Border.all(
color: Colors.white,
),
color: Colors.grey[50],
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Checkbox(
checkColor: Colors.white,
value: dataList[index].isChecked ?? false,
onChanged: (bool? value) {
setState(() {
dataList[index].isChecked = value;
});
print(value);
},
),
Text(
'GHS ' + dataList[index].amount,
),
],
),
),
);
}
#override
Widget build(BuildContext context) {
var checkedList = dataList.where((data) => data.isChecked ?? false);
var totalAmount = checkedList.isNotEmpty
? 'TotalAmount:${checkedList.reduce((before, after) => Team(amount: '${double.parse(before.amount) + double.parse(after.amount)}', id_pes: '_', paid_date: DateTime.now())).amount} '
: '';
return Scaffold(
body: ListView.builder(
shrinkWrap: true,
itemCount: dataList.length,
itemBuilder: (context, index) {
return Tooltip(
message: totalAmount, child: _buildListItem(index));
}));
}
}
class Team {
final String id_pes;
final String amount;
final DateTime paid_date;
bool? isChecked;
Team({
required this.id_pes,
required this.amount,
required this.paid_date,
this.isChecked,
});
}
This might be a bit of broad question.
I have an JSON Map Array which i can parse normally. But i would like to MAP multiple Values to a Single key. I am stuck and unsure how to proceed.
My JSON output. I cannot change this.
{
"pos":"1",
"element":"37542",
"title":"Easy On Me",
"inter":"Adele",
"isnew":"0"
},
{
"pos":"66",
"element":"37183",
"title":"Send My Love",
"inter":"Adele",
"isnew":"0"
}
this is just a snippet of the actual JSON objects
what I would like to do is something like Map<String,List<String,String>>
Adele
Easy On Me, 37542
Send My Love, 37183
Searching online I find a lot of examples how to parse JSON normally, but never found a version where you can iterate through a JSON version like above and pull parts of the map out and add it to another.
any help would be appreciated. Thank you in advance
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'dart:async';
class SongList {
final List<Song> songs;
SongList({
required this.songs,
});
factory SongList.fromJson(List<dynamic> parsedJson) {
List<Song> songs = <Song>[];
songs = parsedJson.map((i) => Song.fromJson(i)).toList();
return SongList(songs: songs);
}
}
class Song {
final String? pos;
final String? element;
final String? title;
final String? inter;
final String? isnew;
const Song(
{required this.pos,
required this.element,
required this.title,
required this.inter,
required this.isnew});
factory Song.fromJson(Map<String, dynamic> data) {
return Song(
pos: data['pos'] as String?,
element: data['element'] as String?,
title: data['title'] as String?,
inter: data['inter'] as String?,
isnew: data['isnew'] as String?,
);
}
}
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
class Songs extends StatefulWidget {
final String pos;
final String element;
final String title;
final String inter;
final String isnew;
const Songs(
{super.key,
required this.pos,
required this.element,
required this.title,
required this.inter,
required this.isnew});
#override
State<Songs> createState() => _SongsState();
}
class _SongsState extends State<Songs> {
late AudioPlayer player;
#override
void initState() {
super.initState();
player = AudioPlayer();
}
#override
void dispose() {
player.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
var titles = Row(
mainAxisSize: MainAxisSize.min,
children: [
Column(children: [
SizedBox(
height: 25,
width: 200,
child: Text(
widget.title,
style: const TextStyle(fontSize: 14),
textAlign: TextAlign.center,
),
),
SizedBox(
height: 25,
width: 200,
child: Text(
widget.inter,
style: const TextStyle(fontSize: 10),
textAlign: TextAlign.center,
),
),
]),
],
);
return FittedBox(
fit: BoxFit.contain,
child: Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
Center(
child: SizedBox(
height: 50,
width: 50,
child: Align(
alignment: Alignment.center,
child: widget.isnew != "1"
? Text(
widget.pos.toString(),
textAlign: TextAlign.center,
)
: const Text(
"new",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.red),
)))),
],
),
Column(
children: [
SizedBox(
height: 50,
width: 50,
child: Image.network(
'${'http://www.example.com/${widget.element}'}.jpg'),
),
],
),
Column(
children: [
titles,
],
),
Column(
children: [
SizedBox(
height: 50,
width: 50,
child: TextButton(
onPressed: () async {
final url =
'${'http://www.example.com/${widget.element}'}.mp3';
await player.setUrl(url);
player.play();
},
child: Image.asset('assets/images/btn_play.png'),
),
),
],
),
Column(
children: [
SizedBox(
height: 50,
width: 50,
child: TextButton(
// ignore: avoid_print
onPressed: () =>
print('pressed button download!! ${widget.element}'),
child: Image.asset('assets/images/btn_download.png'),
),
),
],
),
],
)));
}
}
Future<SongList> fetchSong() async {
final response = await http
.get(Uri.parse('http://example.com'));
if (response.statusCode == 200) {
return SongList.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to Load Songs');
}
}
//snippet from class that instatiates
late Future<SongList> futureSong;
#override
void initState() {
super.initState();
futureSong = fetchSong();
}
//snippet from class that builds the list
FutureBuilder<SongList>(
future: futureSong,
builder: (context, AsyncSnapshot<SongList> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.songs.length,
itemBuilder: (context, index) {
if (snapshot.data!.songs[index].element.toString() !=
"null") {
return Songs(
element:
snapshot.data!.songs[index].element.toString(),
inter: snapshot.data!.songs[index].inter.toString(),
isnew: snapshot.data!.songs[index].isnew.toString(),
pos: snapshot.data!.songs[index].pos.toString(),
title: snapshot.data!.songs[index].title.toString());
} else {
return const SizedBox(height: 0);
}
});
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
}
This might work:
void main() {
var data=[
{
"pos":"1",
"element":"37542",
"title":"Easy On Me",
"inter":"Adele",
"isnew":"0"
},
{
"pos":"66",
"element":"37183",
"title":"Send My Love",
"inter":"Adele",
"isnew":"0"
},
{
"pos":"80",
"element":"5000",
"title":"Enjoy the silence",
"inter":"Depeche Mode",
"isnew":"0"
},
{
"pos":"100",
"element":"6000",
"title":"In your room",
"inter":"Depeche Mode",
"isnew":"0"
}
];
var result = <String,List<Map<String,String>>>{};
for (var d in data) {
print(d);
var e={"element":d["element"]!, "title": d["title"]!};
var key=d["inter"]!;
if (result.containsKey(key)) {
result[key]!.add(e);
} else {
result[key]=[e];
}
}
print(result);
}
You can achieve this by creating a subclass.
Interpret interpretFromJson(String st) => Interpret.fromJson(json.decode(st));
class Interpret{
Interpret({
required this.name,
required this.titles,
});
final String name;
final List<Title> titles;
factory Interpret.fromJson(Map<String, dynamic> json) => Title(
name: json["inter"],
titles: List<Title>.from(json.map((x) => Title.fromJson(x))),
);
}
class Title {
Title({
required this.name,
required this.element,
});
final String name;
final double element;
factory Title.fromJson(Map<String, dynamic> json) => Title(
name: json["name"],
element: json["element"],
);
Then you can just call it like.
InterpretFromJson(json);
This is my API code and I'm passing the image path from here into flutter to determine which image to load. However, all of the other data such as price or title are loading but the image is not loading and throwing an error of unable to load asset.
app.post('/products',(req,res)=>{
//console.log(req.body.LanguageID);
//console.log(req.body.CategoryID);
console.log(req.body);
res.status(200).json({
"StatusCode": 200,
"StatusMessage": "success",
"Data": [
{
"Id": "p8",
"title": "Sufiyan",
"ImageAsset": "assets/images/plant3.png",
"price": 80.0
},
{
"Id": "p8",
"title": "Sufiyan",
"ImageAsset": "assets/images/plant1.png",
"price": 90.0
}
],
"Message": null,
// "localId": '8',
// "idToken": '',
// "error": {
// "message": ""
// },
// "expiresIn": "9"
});
});
app.listen(5000);
That's how I am loading data from the provider into the widget and unable to comprehend why this is not working as I am a newbie to flutter and unable to understand it.
#override
void initState() {
// TODO: implement initState
super.initState();
//gettingData();
final productData = Provider.of<Products>(context, listen: false);
productData.fetchData(context);
// Create TabController for getting the index of current tab
_controller = TabController(length: list.length, vsync: this);
_containerheight = _totalarrayheight(8) * 140;
}
#override
Widget build(BuildContext context) {
final productData = Provider.of<Products>(context);
final products = productData.items;
final featuredProducts = productData.featuredItems;
_controller!.addListener(() {
setState(() {
_selectedIndex = _controller!.index;
if (_controller!.index == 0) {
_containerheight =
140 * _totalarrayheight(products.length.toDouble());
} else {
_containerheight =
140 * _totalarrayheight(featuredProducts.length.toDouble());
}
});
if (kDebugMode) {
print("Selected Index: " + _controller!.index.toString());
}
});
That is my provider code:
class Products with ChangeNotifier {
late List<ProductList> _items = [];
fetchData(context) async {
_items = await getData();
notifyListeners();
}
Widget to create the grid for product
class NewProductGrid extends StatelessWidget {
const NewProductGrid({
Key? key,
required this.products,
}) : super(key: key);
final List<ProductList> products;
#override
Widget build(BuildContext context) {
return StaggeredGridView.countBuilder(
padding: EdgeInsets.only(right: 30, left: 10, top: 10),
physics:
NeverScrollableScrollPhysics(), // to disable GridView's scrolling
shrinkWrap: false,
crossAxisCount: 4,
itemCount: products.length,
itemBuilder: (BuildContext context, int index) => GestureDetector(
onTap: () {
Navigator.of(context).pushNamed(ProductDetail.routeName,
arguments: {'id': products[index].id});
},
child: Container(
//padding: EdgeInsets.only(left:40.0),
child: Column(
children: <Widget>[
Stack(
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 35.0),
child: Container(
height: 150,
width: 150,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: primary_background_six,
),
),
),
Container(
child: Image.asset(
"${products[index].imageAsset}",
fit: BoxFit.cover,
)),
Container(),
],
),
Padding(
padding: const EdgeInsets.only(left: 20.0),
child: Text(
"\$${products[index].price.toInt()}",
style: TextStyle(fontFamily: 'Morganite Light', fontSize: 50),
),
),
Padding(
padding: const EdgeInsets.only(left: 20.0),
child: Text(
"${products[index].title}",
style: TextStyle(fontFamily: 'PlayFairDisplay', fontSize: 18),
),
),
],
),
),
),
staggeredTileBuilder: (int index) =>
StaggeredTile.count(2, index.isEven ? 3 : 3),
mainAxisSpacing: 4.0,
crossAxisSpacing: 4.0,
);
}
}
screen code where the widget is called.
Container(
height: _containerheight,
child: TabBarView(
controller: _controller,
children: <Widget>[
Container(
//height: 140.0 * _totalarrayheight(tagObjs.length.toDouble()),
child: NewProductGrid(
products: products),
)
Product list model code:
import 'package:flutter/material.dart';
class ProductList with ChangeNotifier {
final String id;
final String title;
final String imageAsset;
final num price;
bool isFavorite;
ProductList({
required this.id,
required this.title,
required this.price,
required this.imageAsset,
this.isFavorite = false,
});
factory ProductList.fromMap(Map<String, dynamic> json) {
return ProductList(
id: json['id'],
title: json['title'],
imageAsset: json['imageAsset'],
price: json['price']);
}
factory ProductList.fromJson(Map<String, dynamic> json) {
print('Json:');
print(json);
return ProductList(
id: json['id'] ?? '',
title: json['title'] ?? '',
imageAsset: json['imageAsset'] ?? '',
price: json['price'] ?? 0);
}
void toggleFavoriteStatus() {
isFavorite = !isFavorite;
notifyListeners();
}
}
The image asset in response has an upper case i and the model of product list has image asset with lower case i
Change the model to this
import 'package:flutter/material.dart';
class ProductList with ChangeNotifier {
final String id;
final String title;
final String imageAsset;
final num price;
bool isFavorite;
ProductList({
required this.id,
required this.title,
required this.price,
required this.imageAsset,
this.isFavorite = false,
});
factory ProductList.fromMap(Map<String, dynamic> json) {
return ProductList(
id: json['id'],
title: json['title'],
imageAsset: json['ImageAsset'],
price: json['price']);
}
factory ProductList.fromJson(Map<String, dynamic> json) {
print('Json:');
print(json);
return ProductList(
id: json['id'] ?? '',
title: json['title'] ?? '',
imageAsset: json['ImageAsset'] ?? '',
price: json['price'] ?? 0);
}
void toggleFavoriteStatus() {
isFavorite = !isFavorite;
notifyListeners();
}
}
In this code data successfully adding to my ''items'' list.
But In the second code, when I call my ''items'' list, It looks empty. What should I do for displaying data?
import 'dart:convert';
import 'package:http/http.dart' as http;
class IceCream {
final String id;
final String? title;
final String? shortTitle;
final String? description;
final String? longDescription;
final double price;
final String imageUrl;
final double? kilos;
final double? review;
final int? reviewCount;
IceCream(
{required this.id,
this.title,
this.shortTitle,
this.description,
this.longDescription,
required this.price,
required this.imageUrl,
this.kilos,
this.review,
this.reviewCount});
}
class Products {
List<IceCream> _items = [];
List<IceCream> get items {
return [..._items];
}
Future<void> fetchProducts() async {
final url = Uri.parse(
'https://ice-cream-app-31dce-default-rtdb.europe-west1.firebasedatabase.app/icecreams.json');
try {
final response = await http.get(url);
// print(response.body);
final extractedData = json.decode(response.body) as Map<String, dynamic>;
if (extractedData.length == null) {
return;
}
final List<IceCream> loadedProduct = [];
extractedData.forEach((prodId, prodData) {
loadedProduct.add(IceCream(
id: prodId,
title: prodData['title'],
description: prodData['description'],
price: prodData['price'],
imageUrl: prodData['imageUrl'],
shortTitle: prodData['shortTitle'],
longDescription: prodData['longDescription'],
kilos: prodData['kilos'],
review: prodData['review'],
reviewCount: prodData['reviewCount']));
});
_items = loadedProduct;
//print(items);
} catch (error) {
print(error);
throw (error);
}
}
}
Second code:
import 'package:flutter/material.dart';
import 'package:ice_cream_ui_clone/model/ice_cream.dart';
import 'package:ice_cream_ui_clone/screens/detail_screen.dart';
import '../utils/constants.dart';
import 'add_item_button.dart';
class TopFlavoursList extends StatelessWidget {
#override
Widget build(BuildContext context) {
final cart = Products().items;
return Expanded(
flex: 2,
child: ListView.builder(
itemCount: cart.length,
scrollDirection: Axis.horizontal,
itemBuilder: (ctx, i) => GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (ctx) => DetailScreen(
item: cart[i],
)));
},
child: Card(
color: myLightPinkColor,
child: Padding(
padding: EdgeInsets.all(lowPadding),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.network(
cart[i].imageUrl,
height: midImageHeight,
width: midImageWidht,
),
SingleChildScrollView(
child: Column(
children: [
Text(cart[i].title!,
style:
Theme.of(context).textTheme.headline5),
SizedBox(
height: lowPadding,
),
Row(
children: [
Text("${cart[i].kilos.toString()} KG",
style: Theme.of(context)
.textTheme
.headline6),
SizedBox(
width: lowImageWidht,
),
Icon(
Icons.star,
color: myYellowColor,
),
Text(cart[i].review.toString(),
style: Theme.of(context)
.textTheme
.headline6)
],
),
Row(
children: [
Text("\$${cart[i].price.toString()}",
style: Theme.of(context)
.textTheme
.headline5),
SizedBox(
width: lowImageWidht,
),
AddItemButton(
buttonShape: const CircleBorder(),
)
],
)
],
),
)
],
)),
),
)),
);
}
}
I think the issue is with the "forEach" statment. Should be something more like
extractedData.forEach((data) {
loadedProduct.add(IceCream())
});
The function in the forEach should take one argument not 2
I have been trying to run a flutter app and it keeps giving me Failed assertion error. below is my code. I just want to show the list the second code
class Post extends StatefulWidget {
final String postId;
final String ownerId;
final String username;
final String location;
final String description;
final String mediaUrl;
final dynamic likes;
Post({
this.postId,
this.ownerId,
this.username,
this.location,
this.description,
this.mediaUrl,
this.likes,
});
factory Post.fromDocument(DocumentSnapshot doc) {
return Post(
postId: doc['postId'],
ownerId: doc['ownerId'],
username: doc['username'],
location: doc['location'],
description: doc['description'],
mediaUrl: doc['mediaUrl'],
likes: doc['likes'],
);
}
int getLikeCount(likes) {
// if no likes, return 0
if (likes == null) {
return 0;
}
int count = 0;
// if the key is explicitly set to true, add a like
likes.values.forEach((val) {
if (val == true) {
count += 1;
}
});
return count;
}
#override
_PostState createState() => _PostState(
postId: this.postId,
ownerId: this.ownerId,
username: this.username,
location: this.location,
description: this.description,
mediaUrl: this.mediaUrl,
likes: this.likes,
likeCount: getLikeCount(this.likes),
);
}
class _PostState extends State<Post> {
final String postId;
final String ownerId;
final String username;
final String location;
final String description;
final String mediaUrl;
Int likeCount;
Map likes;
_PostState({
this.postId,
this.ownerId,
this.username,
this.location,
this.description,
this.mediaUrl,
this.likes,
this.likeCount,
});
Widget postingHeading() {
return FutureBuilder(
future:
FirebaseFirestore.instance.collection('User').doc(ownerId).get(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
return ListTile(
onTap: () => print('go check the url'),
leading: CircleAvatar(
backgroundImage:
NetworkImage("${snapshot.data.data()['photoUrl']}"),
),
title: Text('${snapshot.data.data()['username']}'),
subtitle: Text(location),
trailing: IconButton(
onPressed: () => print('deleting post'),
icon: Icon(Icons.more_vert),
),
);
});
}
Widget postImagePicture() {
return Stack(
alignment: Alignment.center,
children: [
Image.network(mediaUrl),
],
);
}
Widget postDetailsComment() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
IconButton(icon: Icon(Icons.favorite_border), onPressed: () {}),
IconButton(icon: Icon(Icons.favorite_border), onPressed: () {})
],
),
Row(
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 20.0),
child: Text(
"$likeCount likes",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
],
),
Row(
children: [
Text(username),
SizedBox(
width: 1,
),
Flexible(child: Text(description))
],
)
],
);
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
postingHeading(),
postImagePicture(),
postDetailsComment(),
],
);
}
}
here is where I convert it to List... I don't even no what is wrong.... please help.... thanks in advance
class Profile extends StatefulWidget {
final String currentUser;
Profile({this.currentUser});
#override
_ProfileState createState() => _ProfileState();
}
class _ProfileState extends State<Profile> {
final _firestore = FirebaseFirestore.instance;
int postLenght;
List<Post> post;
bool pleaseWait;
#override
void initState() {
super.initState();
getUsersPicsDetails();
}
getUsersPicsDetails() async {
setState(() {
pleaseWait = false;
});
QuerySnapshot _getPost = await _firestore
.collection('post')
.doc(widget.currentUser)
.collection('userPost')
.orderBy('timeStamp', descending: true)
.get();
setState(() {
pleaseWait = true;
postLenght = _getPost.docs.length;
post = _getPost.docs.map((e) => Post.fromDocument(e)).toList();
print(postLenght);
});
}
fellowEditButtton({String test, Function press}) {
return TextButton(
onPressed: press,
child: Container(
alignment: Alignment.center,
height: 25,
width: 250,
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
border: Border.all(),
borderRadius: BorderRadius.circular(8.0)),
child: Text(
test,
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
)),
);
}
gettingUserprofile() {
return StreamBuilder(
stream: _firestore.collection('User').doc(widget.currentUser).snapshots(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.connectionState != ConnectionState.active) {
return Text('this stuff en');
}
return ListView.builder(
itemCount: 1,
itemBuilder: (context, index) => Container(
padding: EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
FullScreenWidget(
child: Hero(
tag: 'smallImage',
child: CircleAvatar(
backgroundImage: NetworkImage(
'${snapshot.data.data()['photoUrl']}'),
radius: 50.0,
),
),
),
Expanded(
flex: 1,
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
topColum(buttom: 'Post', number: 1),
topColum(buttom: 'Fellowers', number: 2),
topColum(buttom: 'Following', number: 0),
],
),
Column(
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20.0,
),
child: widget.currentUser ==
_auth.currentUser.uid
? fellowEditButtton(
test: 'Edit Profile',
press: profileEditingButton)
: fellowEditButtton(
test: 'Fellow', press: () {})),
],
),
],
),
),
],
),
Divider(),
//! this is i call the list
//getUserPicture(),
getUserPicture(),
],
),
));
},
);
}
profileEditingButton() {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => EditProfile(
userProfile: widget.currentUser,
)));
}
Widget topColum({int number, String buttom}) {
return Column(
children: [
Text(
'$number',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
Text(
buttom,
style: TextStyle(
fontSize: 20,
),
),
],
);
}
//! this is the where i return the list
Widget getUserPicture() {
if (pleaseWait = false) {
return CircularProgressIndicator();
}
return Column(
children: post,
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: header(context, titleString: "Profile"),
body:
//Text('${widget.currentUser}')
gettingUserprofile());
// userPictursPost(),
}
}
I want to the get the post at the bottom... thanks in advance
below is the error image
enter image description here
When you get an error, please post it along with your question. When you are getting an error, it means that something is wrong with your code,and most likely not the flutter engine. Both are important for debugging, the error+your code.
Try changing this
QuerySnapshot _getPost = await _firestore
.collection('post')
.doc(widget.currentUser)
.collection('userPost')
.orderBy('timeStamp', descending: true)
.get();
setState(() {
pleaseWait = true;
postLenght = _getPost.docs.length;
post = _getPost.docs.map((e) => Post.fromDocument(e)).toList();
print(postLenght);
});
into this:
QuerySnapshot _getPost = await _firestore
.collection('post')
.doc(widget.currentUser)
.collection('userPost')
.orderBy('timeStamp', descending: true)
.get();
if(_getPost.docs.isNotEmpty){
List<Post> tempPost = _getPost.docs.map((e) => Post.fromDocument(e)).toList();
setState(() {
pleaseWait = true;
post =tempPost
print(postLenght);
});
}else{
print('The List is empty);}
You are not checking if the Query result has data or not. If it's empty, you will pass an empty List post down your tree, and you will get the error you are having.
For people facing similar issues, let me tell what I found in my code:
The error says that the children is null, not empty !
So if you are getting the children for the parent widget like Row or Column from a separate method, just check if you are returning the constructed child widget from the method.
Row(
children: getMyRowChildren()
)
.
.
.
getMyRowChildren(){
Widget childWidget = ... //constructed
return childWidget; //don't forget to return!
}
Else it would return null, which results in the children being null and we get the mentioned above error!