how can i get length of list in widget - flutter

I can't get the length of the list in the widget although I wrote everything correct
this is my cubit
class AppCubit extends Cubit<NewsStates> {
AppCubit() : super(NewsInit());
static AppCubit get(context) => BlocProvider.of(context);
MianTeams? mianTeams;
void getTimes() {
emit(NewsLoding());
DioHelper.getData(
Query: {"action": "get_teams", "league_id": "141", "APIkey": api})
.then((value) {
mianTeams = MianTeams.fromJson(value.data);
////////////////+++++ i can get the length here in cubit
print(mianTeams!.team.length);
emit(NewsSucsess());
}).catchError((onError) {
print(onError.toString());
emit(NewsErorr(onError.toString()));
});
}
this is my model
class MianTeams {
List<TeamsModel> team = [];
MianTeams.fromJson(List<dynamic> json) {
json.forEach((e) {
team.add(TeamsModel.fromJson(e));
});
}
}
class TeamsModel {
String? teamKey;
String? teamName;
String? teamBadge;
List<Players> players = [];
TeamsModel.fromJson(Map<String, dynamic> json) {
teamKey = json['team_key'];
teamName = json['team_name'];
teamBadge = json['team_badge'];
json['players'].forEach((e) {
players.add(Players.fromJson(e));
});
}
}
but in Widget I can't get the length to set the itemCount of List View separated
this is my statelessWidget
class Teams extends StatelessWidget {
const Teams({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocConsumer<AppCubit, NewsStates>(
listener: (context, state) {},
builder: (context, state) {
// print(AppCubit.get(context).mianTeams!.team.length);
return Scaffold(
appBar: AppBar(),
body: ListView.separated(
itemBuilder: (context, index) => Divider(),
separatorBuilder: (context, index) => Divider(),
itemCount: AppCubit.get(context).mianTeams!.team.length),
);
});
}
}
I don't know what's wrong and I think I wrote everything correctly

The problem is that mianTeams is null until after getTimes() is called and the response is received from your API. So when the ListView tries to build initially, it's trying to get the length of a null list.
Instead of storing mianTeams directly as a property of the Cubit, you should make it a property of one of your State classes. Probably NewsSuccess - then the list will always be available in that state. And if the Cubit is in a different state, you can display a loading indicator or something.
Your Cubit might then look something like this:
class AppCubit extends Cubit<NewsStates> {
AppCubit() : super(NewsInit());
static AppCubit get(context) => BlocProvider.of(context);
void getTimes() {
emit(NewsLoding());
DioHelper.getData(
Query: {"action": "get_teams", "league_id": "141", "APIkey": api})
.then((value) {
final mianTeams = MianTeams.fromJson(value.data);
emit(NewsSucsess(mianTeams: mianTeams));
}).catchError((onError) {
print(onError.toString());
emit(NewsErorr(onError.toString()));
});
}
}
And your widget might be something like:
class Teams extends StatelessWidget {
const Teams({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocConsumer<AppCubit, NewsStates>(
listener: (context, state) {},
builder: (context, state) {
return Scaffold(
appBar: AppBar(),
body: _buildBody(state),
});
}
Widget _buildBody(NewsStates state) {
if (state is NewsError) {
// For example
return Text("Error: ${state.message}");
} else if (state is NewsLoding) {
// For example
return const CircularProgressIndicator();
} else if (state is NewsSuccess) {
return ListView.separated(
itemBuilder: (context, index) => Divider(),
separatorBuilder: (context, index) => Divider(),
itemCount: state.mianTeams.team.length),
);
} else {
// Handle any other possible states you have
}
}
}

You can use the state from builder
itemCount: state.mianTeams?.team.length,

Related

How to use querySnapshot in a listview builder? (flutter)

I'm trying to fetch documents from my firebase DB and use them to create a social media feed. Here I'm trying to get the length of the fetched collection but I cannot manage to call the variable. Any help would be appreciated. Example code
class LoadDataFromFirestore extends StatefulWidget {
#override
_LoadDataFromFirestoreState createState() => _LoadDataFromFirestoreState();
}
class _LoadDataFromFirestoreState extends State<LoadDataFromFirestore> {
#override
void initState() {
super.initState();
CollectionReference _collectionRef =
FirebaseFirestore.instance.collection('fish');
Future<void> getData() async {
// Get docs from collection reference
QuerySnapshot querySnapshot = await _collectionRef.get();
// Get data from docs and convert map to List
final allData = querySnapshot.docs.map((doc) => doc.data()).toList();
print(allData);
}
}
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: querySnapshot.docs.length,
itemBuilder: (BuildContext context, int index) {
return _postView();
},
),
);
}
}
First of all it is not ok to call future function in initstate, you need to use FutureBuilder like this:
class LoadDataFromFirestore extends StatefulWidget {
#override
_LoadDataFromFirestoreState createState() => _LoadDataFromFirestoreState();
}
class _LoadDataFromFirestoreState extends State<LoadDataFromFirestore> {
late CollectionReference _collectionRef;
#override
void initState() {
super.initState();
_collectionRef = FirebaseFirestore.instance.collection('fish');
}
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<QuerySnapshot>(
future: _collectionRef.get(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
QuerySnapshot? querySnapshot = snapshot.data;
return ListView.builder(
itemCount: querySnapshot?.docs?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
var data = querySnapshot?.docs?[index].data();
print("data = $data");
return _postView();
},
);
}
}
},
),
);
}
}
inside listview's builder you can use data to parse your data and use it.
You can use FutureBuilder like this:
class LoadDataFromFirestore extends StatefulWidget {
const LoadDataFromFirestore({super.key});
#override
State<LoadDataFromFirestore> createState() => _LoadDataFromFirestoreState();
}
class _LoadDataFromFirestoreState extends State<LoadDataFromFirestore> {
//TODO change Map<String, dynamic> with your data type with fromJson for example
Future<List<Map<String, dynamic>>> _getData() async {
final querySnapshot = await FirebaseFirestore.instance.collection('fish').get();
return querySnapshot.docs.map((doc) => doc.data()).toList();
}
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<List<Map<String, dynamic>>>(
future: _getData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return _postView(/* Ithink you have to pass here your item like snapshot.data[index]*/);
},
);
} else {
return const Center(child: CircularProgressIndicator());
}
},
),
);
}
}

how to access snapshot.data inside Listview.builder inside futurebuilder

I am getting error The method '[]' can't be unconditionally invoked because the receiver can be 'null'.
Mydata inside _categoriesList future
[
{
"business_category_id": 1,
"business_category": "Manufacturer",
"business_category_image": "164423936662011a06c450d.png"
},
{
"business_category_id": 2,
"business_category": "Distributor",
"business_category_image": "164423937762011a11033aa.png"
},
{
"business_category_id": 3,
"business_category": "Wholesaler",
"business_category_image": "164423938762011a1bb2e3c.png"
},
{
"business_category_id": 4,
"business_category": "Retailer",
"business_category_image": "164423940062011a28189e5.png"
},
{
"business_category_id": 5,
"business_category": "Reseller",
"business_category_image": "164423941362011a3554148.png"
},
{
"business_category_id": 6,
"business_category": "Service Provider",
"business_category_image": "164423942462011a4096996.png"
}
]
my code inside futurebuilder. I am having problem accessing snapshot.data[index]
FutureBuilder(
future: _categoriesList,
builder: (_, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: 6,
itemBuilder: (_, index) {
return Text(snapshot.data[index]); // error The method '[]' can't be unconditionally invoked because the receiver can be 'null'
});
}
},
)
Here is the example for ListView.builder:
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future <List<Data>> fetchData() async {
final response =
await http.get('https://jsonplaceholder.typicode.com/albums');
if (response.statusCode == 200) {
List jsonResponse = json.decode(response.body);
return jsonResponse.map((data) => new Data.fromJson(data)).toList();
} else {
throw Exception('Unexpected error occured!');
}
}
class Data {
final int userId;
final int id;
final String title;
Data({this.userId, this.id, this.title});
factory Data.fromJson(Map<String, dynamic> json) {
return Data(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future <List<Data>> futureData;
#override
void initState() {
super.initState();
futureData = fetchData();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter API and ListView Example',
home: Scaffold(
appBar: AppBar(
title: Text('Flutter ListView'),
),
body: Center(
child: FutureBuilder <List<Data>>(
future: futureData,
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Data> data = snapshot.data;
return
ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 75,
color: Colors.white,
child: Center(child: Text(data[index].title),
),);
}
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default show a loading spinner.
return CircularProgressIndicator();
},
),
),
),
);
}
}
Specify your data-type for list in FutureBuilder first.
FutureBuilder<List<String>>()
//
Check if you are getting data. print the data using snapshot.data.
FutureBuilder<List<String>>(
future: _categoriesList,
builder: (_, snapshot) {
if (snapshot.hasData) {
print("snapshot:: data >>>> ${snapshot.data}");
return ListView.builder(
itemCount: 6,
itemBuilder: (_, index) {
return Text(snapshot.data[index]); // error The method '[]' can't be unconditionally invoked because the receiver can be 'null'
});
}
},
)
Assuming you have a Category class, with variables for the CategoryId,CategoryName,CategoryImage with all the needed methods as shown in https://stackoverflow.com/a/71029334/12371668
//create an instance of the category class before the build() method
final category = Category();
#override
Widget build(BuildContext context) {
// other code
FutureBuilder(
future: category.fetchData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Category> categories = snapshot.data();
return ListView.builder(
itemCount: categories.length,
itemBuilder: (context, index) {
var category = categories[index];
return Text(category.CategoryName);
});
}
},
)
}

future.wait can not return any data

class AdView extends StatefulWidget {
const AdView({Key? key, required String id}) : super(key: key);
final id = '2';
#override
_AdViewState createState() => _AdViewState();
}
class _AdViewState extends State<AdView> {
final _adService = NewsService();
late Future<Categories> _futureCategories;
late Future<AdBanner> _futureBanners;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
getData() async {
_futureCategories = _adService.getAllCategories();
_futureBanners = _adService.getAds('2');
AdBanner foos;
Categories bars;
await Future.wait<void>([
_futureBanners.then((result) => foos = result),
_futureCategories.then((result) => bars = result),
]);
}
return FutureBuilder(
future: getData(),
builder: (BuildContext context, AsyncSnapshot<dynamic> shot) {
// (BuildContext context, AsyncSnapshot<List<dynamic>> shot) {
if (shot.hasData) {
return ListView.builder(
itemCount: 2,
itemBuilder: (BuildContext context, int index) {
// return bannerListTile(advertisements, index, context);
return const Text('index');
});
} else if (shot.hasError) {
return NewsError(
errorMessage: '${shot.hasError}',
);
} else {
return const NewsLoading(
text: 'loading...',
);
}
});
}
}
I am trying to combine two different API and fetch the results but in this structure I cannot get any data and run only ProgressBarIndicator.
If I am use regular FutureBuilder the JSON calls works without any problem. My goal is get data from two different API's like shot.data[0].value and shot.data[1].value
you made mistake in defining the getData() function.
remove getData from build method and put outside build method because the build is itself a method, you cant define a method inside a method in Dart.
class AdView extends StatefulWidget {
const AdView({Key? key, required String id}) : super(key: key);
final id = '2';
#override
_AdViewState createState() => _AdViewState();
}
class _AdViewState extends State<AdView> {
final _adService = NewsService();
late Future<Categories> _futureCategories;
late Future<AdBanner> _futureBanners;
#override
void initState() {
super.initState();
}
Future getData() async {
_futureCategories = _adService.getAllCategories();
_futureBanners = _adService.getAds('2');
AdBanner foos;
Categories bars;
await Future.wait<void>([
_futureBanners.then((result) => foos = result),
_futureCategories.then((result) => bars = result),
]);
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getData(),
builder: (BuildContext context, AsyncSnapshot<dynamic> shot) {
// (BuildContext context, AsyncSnapshot<List<dynamic>> shot) {
if (shot.hasData) {
return ListView.builder(
itemCount: 2,
itemBuilder: (BuildContext context, int index) {
// return bannerListTile(advertisements, index, context);
return const Text('index');
});
} else if (shot.hasError) {
return NewsError(
errorMessage: '${shot.hasError}',
);
} else {
return const NewsLoading(
text: 'loading...',
);
}
});
}
}

Future.wait returning null

class AdView extends StatefulWidget {
const AdView({Key? key, required String id}) : super(key: key);
final id = '2';
#override
_AdViewState createState() => _AdViewState();
}
class _AdViewState extends State<AdView> {
final _adService = NewsService();
Future<AdBanner?> futureAdd() async {
_adService.getAds('2');
}
Future<Categories?> futureCatergoriess() async {
_adService.getAllCategories();
}
#override
void initState() {
futureAdd();
futureCatergoriess();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.grey[200],
body: FutureBuilder(
future: Future.wait([futureCatergoriess(), futureAdd()]),
builder: (context, AsyncSnapshot<List<dynamic>> snapshot) {
if (snapshot.hasData) {
final advertisements = snapshot.data![0];
return ListView.builder(
itemCount: advertisements!.length,
itemBuilder: (BuildContext context, int index) {
//return bannerListTile(advertisements, index, context);
return const Text('index');
});
} else {
if (snapshot.hasError) {
return NewsError(
errorMessage: '${snapshot.hasError}',
);
}
return const NewsLoading(
text: 'Loading...',
);
}
},
),
);
}
}
snapshot.data![0]; returning null value. I tried already many versions ([1] or snapshot.data.data but I cannot call the results.
I am using future.wait first time. There is no problem if I use any of API with traditional Future.builder.
any help?
after the advice of #ChristopherMoore I modified the code but the main problem is still continue. This code gives as output:
index
index
modified code
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: Future.wait([futureCatergoriess(), futureAdd()]),
builder: (context, AsyncSnapshot<List<dynamic>> snapshot) {
if (snapshot.hasData) {
final advertisements = snapshot.data!;
return ListView.builder(
itemCount: advertisements.length,
itemBuilder: (BuildContext context, int index) {
//return bannerListTile(advertisements, index, context);
return const Text('index');
});
This original line gives this error:
final advertisements = snapshot.data![0];
The getter 'length' was called on null. Receiver: null Tried calling: length The relevant error-causing widget was FutureBuilder<List<Object?>> lib/view/ad_view.dart:37

Flutter FutureBuilder snapshot returns Instance of 'Object' instead of data

i am new to flutter and trying to display data from a http post
referencing from [1]https://flutter.dev/docs/cookbook/networking/background-parsing and [2]https://flutter.dev/docs/cookbook/networking/fetch-data
i tried to display data on a futurebuilder but it keeps displaying this from the Text('${snapshot.data}')
[Instance of 'DashBoardBanner', Instance of 'DashBoardBanner', Instance of 'DashBoardBanner']
Builder
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late Future<List<DashBoardBanner>> futureBanner;
#override
void initState() {
super.initState();
futureBanner = getBannerDataFromServer();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ListView(
children: [
Card(
child: FutureBuilder(
future: getBannerDataFromServer(),
builder: (context,snapshot){
if(snapshot.connectionState == ConnectionState.done){
if (snapshot.hasData) {
return Text('${snapshot.data}');
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
}
return const CircularProgressIndicator();
},
),
)
],
)),
);
}
}
Class and postreq
class DashBoardBanner {
final String MsgId;
final String MsgKey;
final String MsgPic;
const DashBoardBanner(
{required this.MsgId, required this.MsgKey, required this.MsgPic});
factory DashBoardBanner.fromJson(Map<String, dynamic> json) {
return DashBoardBanner(
MsgId: json['MsgId'] as String,
MsgKey: json['MsgKey'] as String,
MsgPic: json['MsgPic'] as String,
);
}
}
Future<List<DashBoardBanner>> getBannerDataFromServer() async {
final queryParameters = {
"ApiFunc": 'Banner',
"UserKey": getDeviceKey(),
"Token": getDeviceToken(),
"SubmitContent": json.encode({"MobileNo": getMobileNo1()})
};
final response = await http.post(
Uri.http('somesite.net', '/capi.aspx', queryParameters),
);
if (response.statusCode == 200) {
Map<String, dynamic> data = jsonDecode(response.body);
final splitoff = jsonEncode(data['RespContent']);
return compute(parseBanner, splitoff);
} else {
throw Exception('Failed to load Data');
}
}
List<DashBoardBanner> parseBanner(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed
.map<DashBoardBanner>((json) => DashBoardBanner.fromJson(json))
.toList();
}
Edit : i rebuilt the file replicating reference[1] and it finally displayed the data i needed, it seems the issue stem from not having this 2nd widget which return the obj back , however how do i combine the 2nd build widget into the first without needing the whole widget as having a whole build widget to return 1 line seems pointless?
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body:Container(
child: FutureBuilder<List<DashBoardBanner>>(
future: getBannerDataFromServer(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text('An error has occurred!'),
);
} else if (snapshot.hasData) {
print(snapshot.data!.length);
return DashBoardBannersList(dashboardBanners: snapshot.data!); <--- original issue due to not having this
} else {
return CircularProgressIndicator();
}
},
),
),
);
}
}
class DashBoardBannersList extends StatelessWidget {
const DashBoardBannersList({Key? key, required this.dashboardBanners}) : super(key: key);
final List<DashBoardBanner> dashboardBanners;
#override
Widget build(BuildContext context) {
return Text(dashboardBanners[0].MsgId);
}
}
This error is caused because of the sound null safety
snapshot.data might be null for some requests so you can't access the array at a certain index cause it can be null.
If you know for sure snapshot.data exists you can use the ! operator to tell dart the variable is not null for sure like that:
snapshot.data![index];
You can also check if the data is null before accessing it like that:
if (snapshot.data != null) {
// do something with snapshot.data[index]
}
I recommed to read more about sound null safety here
Check the Firestore docs.
Inside snapshot.data, there's docs (every document of your collection).
The code is from there:
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _usersStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return ListView(
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data = document.data()! as Map<String, dynamic>;
return ListTile(
title: Text(data['full_name']),
subtitle: Text(data['company']),
);
}).toList(),
);
},
);
}
The code above shows how to convert every doc (type DocumentSnapshot) to a JSON format (that can be represented with Map<String, dynamic>). To access to the doc id, you'll access with document.id, because it isn't inside the document.data() method.
You wanna retrieve a list of DashBoardBanner but you forget initialize the futurebuilder by adding a ListView.builder().
Try to use the following code idea :
FutureBuilder(
future: getBannerDataFromServer(http.Client()),
builder: (context, AsyncSnapshot snapshot) {
print(snapshot.hasData);
if (snapshot.hasError) {
return CircularProgressIndicator();
} else if (snapshot.hasData) {
return Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: snapshot.data!.length,
itemBuilder: (BuildContext context, int index) {
var data = snapshot.data![index];
return DashBoardBannersList(dashboardBanners: data);
},),
),},
},)