Flutter: FutureBuilder issue - flutter

Generated list is sending null future
class _HomePageState extends State<HomePage> {
AppStateViewModal appStateViewModal = AppStateViewModal();
late Future<List<PackageDataModal>> packageList;
#override
void initState() {
super.initState();
packageList = _getPackageList();
}
Future<List<PackageDataModal>> _getPackageList() async {
return await appStateViewModal.getPackages();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: packageList,
builder: (context,
AsyncSnapshot<List<PackageDataModal>> packageSnapshot) =>
packageSnapshot.hasData
? GridView.builder(
itemCount: packageSnapshot.data!.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1),
itemBuilder: (context, index) => CardView(
packageData: PackageDataModal(
title: packageSnapshot.data![index].title,
description: packageSnapshot.data![index].description,
packageId: packageSnapshot.data![index].packageId),
imageList: const <ImageDataModal>[
// Generate a "Imagelist" for every "packageId"
// packageId is "$index"
// I want to return "List<ImageDataModal>""
// But I am getting "Future<List<ImageDataModal>>""
// to fix it, i just need to "await" but build cant do "Async"
// What i am doing wrong ?
// ApiUrl is like /api/packages
// and after this
// ApiUrl is like /api/packages/$index/images as List
// BackEnd Services is sending back the data to widget
// I have seen with doing print statments
// First FutureBuilder is running fine but when it comes to next
// Data is not showing.
// Dummy ImageDataModal is working and shwoing the expected behaviour
],
),
)
: const Center(
child: CircularProgressIndicator(),
),
);
}
}
I was trying to Pass the Future to next Widget and then trying to use FutureBuilder but no success. Data is not reaching there.
class CardView extends StatefulWidget {
const CardView({Key? key, required this.imageList, required this.packageData})
: super(key: key);
final PackageDataModal packageData;
final Future<List<ImageDataModal>> imageList;
#override
State<CardView> createState() => _CardViewState();
}
class _CardViewState extends State<CardView> {
#override
Widget build(BuildContext context) {
return FutureBuilder<List<ImageDataModal>>(
future: widget.imageList,
builder: (context, imageSnapshot) => Card(
margin: const EdgeInsets.all(8.0),
child: Column(
children: [
CarouselSlider.builder(
itemCount: imageSnapshot.data!.length,
itemBuilder: (context, imageIndex, pageIndex) =>
CachedNetworkImage(
imageUrl: imageSnapshot.data![imageIndex].image,
placeholder: (context, text) => const Placeholder(),
),
options:
CarouselOptions(aspectRatio: 16 / 9, autoPlay: true),
),
Text(widget.packageData.title),
const SizedBox(height: 2),
Text(widget.packageData.description),
],
),
));
}
}
I checked the Service which is calling the api to fetch the data is working fine and i checked it that it is reaching the card_view_widget.dart file properly

First correct your card_view_widget to be null aware(Handle null list)
class CardView extends StatefulWidget {
const CardView({Key? key, this.imageList, required this.packageData})
: super(key: key);
final PackageDataModal packageData;
final Future<List<ImageDataModal>>? imageList;
#override
State<CardView> createState() => _CardViewState();
}
class _CardViewState extends State<CardView> {
#override
Widget build(BuildContext context) {
return FutureBuilder<List<ImageDataModal>>(
future: widget.imageList,
builder: (context, imageSnapshot) {
if (!imageSnapshot.hasData &&
imageSnapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (!imageSnapshot.hasData) {
return Text(
'No image found',
style: Theme.of(context).textTheme.subtitle1,
);
}
return Card(
margin: const EdgeInsets.all(8.0),
child: Column(
children: [
CarouselSlider.builder(
itemCount: imageSnapshot.data!.length,
itemBuilder: (context, imageIndex, pageIndex) =>
CachedNetworkImage(
imageUrl: imageSnapshot.data![imageIndex].image,
placeholder: (context, text) => const Placeholder(),
),
options: CarouselOptions(aspectRatio: 16 / 9, autoPlay: true),
),
Text(widget.packageData.title),
const SizedBox(height: 2),
Text(widget.packageData.description),
],
),
);
},
);
}
}
let me know of other errors

I found the solution. My data model, which is Model.fromjson, was mapping the wrong key. I was getting the HTTP response properly, but it was sending the null data to the Future<List>.
It was String itemId, but I changed it previously and forgot to do the fix to String packageId as I was getting the JSON string.

Related

How to get id of element when user select the item from ListView

To go to the album menu, I need to determine which artist the user has chosen. But I can't figure out how to read the element id from the Firestore after the user clicked on the element. For example, there is artist_1 and artist_2, the user clicks on artist_1 and gets his id, after which this id can be used to get information about albums. With my code, the id of the last element is written to the document_id variable, even if i've chosen the first artist. Please help me figure it out.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_application_1/screens/songspage.dart';
import 'package:flutter_application_1/screens/albums.dart';
class Home extends StatefulWidget {
const Home({super.key});
#override
State<Home> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<Home> {
CollectionReference _collectionRef = FirebaseFirestore.instance.collection('artist');
var artist_id, album_id, song_id;
Future getArtists() async {
QuerySnapshot on = await FirebaseFirestore.instance.collection("artist").get();
var documentID;
for (var snapshot in on.docs) {
documentID = snapshot.id; // <-- Document ID
}
print(documentID);
return on.docs;
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getArtists(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return InkWell(
onTap: () => Navigator.push(context,
MaterialPageRoute(builder: (context) => AlbumWidget(
))),
child: Card(
child: Padding(
padding: EdgeInsets.all(10.0),
child: Text(
snapshot.data[index]["name"],
style: TextStyle(fontSize: 15.0),
),
),
elevation: 10.0,
),
);
});
}
},
);
}
}
Try passing the id in the arguments property which is available with Navigator. You can use Navigator.pushNamed like so.
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return InkWell(
onTap: () => Navigator.pushNamed(context, '/albums',
arguments: snapshot.data[index]["id"],
);
child: Card(
child: Padding(
padding: EdgeInsets.all(10.0),
child: Text(
snapshot.data[index]["name"],
style: TextStyle(fontSize: 15.0),
),
),
elevation: 10.0,
),
);
Now in the AlbumWidget , you can create a final variable id and access it using widget. property.
class AlbumWidget extends StatefulWidget {
final String? id;
const AlbumWidget(this.id, {super.key});
#override
State<AlbumWidget> createState() => _AlbumWidgetState();
}
class _AlbumWidgetState extends State<AlbumWidget> {
String? id;
#override
void initState() {
super.initState();
id=widget.id;
}
#override
// rest of the code here
Similarly, if you want the whole album data, just pass the index like so:
arguments: snapshot.data[index]
Depending upon the argument's data type, create a variable in the AlbumWidget and access it using widget.

Make a list of Flutter Firebase field

Hi, I want to make a list inside the Flutter Firebase field. I'm creating an id for followers in the Field. In Firebase, there is a collection, user ID and followers id in the field. My encodings are as follows. But I'm not making a list. What are the changes I will make?
Followers_card
class FollowersCard extends StatefulWidget {
final snap;
const FollowersCard({
Key? key,
required this.snap,
}) : super(key: key);
#override
State<FollowersCard> createState() => _FollowersCardState();
}
class _FollowersCardState extends State<FollowersCard> {
List<dynamic> followersList = []; // shouldn't use dynamic
getdata() async {
await FirebaseFirestore.instance
.collection("users")
.doc(FirebaseAuth.instance.currentUser!.uid)
.get()
.then((value) async {
// get followerIds
List<String> follwerIds = List.from(value.data()!['followers']);
// loop through all ids and get associated user object by userID/followerID
for (int i = 0; i < follwerIds.length; i++) {
var followerId = follwerIds[i];
var data = await FirebaseFirestore.instance
.collection("users")
.doc(followerId)
.get();
// push that data into followersList variable as we are going
// to use that in listViewBuilder
followersList.add(data);
}
setState(() {});
});
#override
void initState() {
super.initState();
getdata();
}
}
#override
Widget build(BuildContext context) {
// use the listView builder to render the list of followers card
return SingleChildScrollView(
physics: NeverScrollableScrollPhysics(),
child: ListView.builder(
shrinkWrap: true,
itemCount: followersList.length,
itemBuilder: (context, index) {
var followerItem = followersList[index];
print('photoUrl');
return _buildFollowersCard(
followerItem['photoUrl'], followerItem['username']);
}),
);
}
Widget _buildFollowersCard(String photoUrl, String username) {
return Container(
height: 70,
width: double.infinity,
color: mobileBackgroundColor,
child: Card(
child: Column(children: [
//Header
Container(
height: 40,
width: double.infinity,
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 16,
).copyWith(right: 0),
child: Row(
children: [
CircleAvatar(
radius: 16,
backgroundImage: NetworkImage(
photoUrl,
),
),
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
username,
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
),
],
),
)
]),
),
);
}
}
followers_screen
class FollowersScreen extends StatefulWidget {
const FollowersScreen({Key? key}) : super(key: key);
#override
State<FollowersScreen> createState() => _FollowersScreenState();
}
class _FollowersScreenState extends State<FollowersScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: mobileBackgroundColor,
centerTitle: true,
title: Image.asset(
'Resim/logo.png',
height: 50,
),
),
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection('users').snapshots(),
builder: (context,
AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) => FollowersCard(
snap: snapshot.data!.docs[index].data(),
),
);
},
),
);
}
}
The problem is that _FollowersScreenState.initState is in the wrong place. It's inside the function getdata that it is trying to call. The initState is never called. That's why there is no list being built.
Also, setState is the one that assigns State values. So first, populate a temporary list of followers and then assign it to the State one inside the setState callback.
Below is the fixed snippet code for _FollowersScreenState:
class _FollowersCardState extends State<FollowersCard> {
List<dynamic> followersList = []; // shouldn't use dynamic
getdata() async {
List<dynamic> followers = [];
final currentUserSnapshot = await FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.get();
// get followerIds
List<String> follwerIds =
List.from(currentUserSnapshot.data()!['followers']);
// loop through all ids and get associated user object by userID/followerID
for (int i = 0; i < follwerIds.length; i++) {
var followerId = follwerIds[i];
var data = await FirebaseFirestore.instance
.collection('users')
.doc(followerId)
.get();
// push that data into the temp list variable as we are going
// to use that in to setState
followers.add(data);
}
setState(() => followersList = followers);
}
#override
void initState() {
super.initState();
getdata();
}
...

LateInitializationError: Field '_' has not been initialized

So, I'm trying to display an Item on my app using the bloc pattern but I'm getting this error:
The following LateError was thrown building ItemDetailsScreen(state: _ItemDetailsScreenState#55c51):
LateInitializationError: Field 'item' has not been initialized.
The relevant error-causing widget was:
ItemDetailsScreen
ItemDetailsScreen:file:/mobile/lib/src/autoroute/app_router.gr.dart:70:40
Below the app_router.dart
AutoRoute(
path: '/item/:itemId',
page: ItemDetailsScreen,
name: 'ItemDetailsRoute',
meta: {'hideBottomNav': true},
guards: [AuthGuard],
),
and the app_router.gr.dart
ItemDetailsRoute.name: (routeData) {
final pathParams = routeData.inheritedPathParams;
final args = routeData.argsAs<ItemDetailsRouteArgs>(
orElse: () =>
ItemDetailsRouteArgs(itemId: pathParams.getInt('itemId')));
return MaterialPageX<dynamic>(
routeData: routeData, child: ItemDetailsScreen(args.itemId));
},
And view code in itemDetails.dart file as bellow
class ItemDetailsScreen extends StatefulWidget {
final int itemId;
const ItemDetailsScreen(#pathParam this.itemId, {Key? key}) : super(key: key);
#override
_ItemDetailsScreenState createState() => _ItemDetailsScreenState();
}
class _ItemDetailsScreenState extends State<ItemDetailsScreen> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
//final auth = Provider.of<KeycloakAuth>(context);
return BlocBuilder<ItemBloc, ItemState>(builder: (context, state) {
if (state is ItemLoadingState) {
return const MyCircularProgressIndicator();
}
if (state is ItemLoadedState) {
Item showItem = state.item;
return Scaffold(
appBar: AppBar(
title: Text(showItem.name),
),
body: ListView(
children: [
CarouselSlider(
options: CarouselOptions(
height: 300.0,
enableInfiniteScroll: false,
aspectRatio: 16 / 10,
viewportFraction: 1.0,
),
items: showItem.pictures.whereNotNull().map((String e) {
return CachedNetworkImage(
imageUrl: "${e}_SMALL.jpg",
placeholder: (context, url) =>
const CircularProgressIndicator(),
errorWidget: (context, url, error) =>
const Icon(Icons.error),
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
);
}).toList()),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: Column(
children: [
const SizedBox(
height: 15,
),
Text(
showItem.name,
style: MyTheme.bigBlack,
),
const SizedBox(
height: 15,
),
],
),
)
],
),
);
}
if (state is ItemLoadingErrorState) {
return Center(
child: Text(state.error.toString()),
);
}
return Container();
});
}
}
Any idea how I can fix it ? thank you in advance for your answers.
in file itemDetails.dart you declared final int itemId; inside class ItemDetailsScreen
when you called ItemDetailsScreen in AutoRoute you did not pass a value for final int itemId;
When you write your code in null safety you should declare something to the variable's .So declare some values to final int itemid

VIdeo streams keep on loading in flutter vlc player

I'm trying to build a video stream app that fetches the stream from an api, the problem is that the streams fetched aren't displaying instead my CircularProgressIndicator keeps on loading . I can't really figure out what is the problem .
output image
Here is my code
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
Future<List<dynamic>> fetchMedia() async {
final result = await Future.wait([
http.get(Uri.parse('https://iptv-org.github.io/api/streams.json')), // all
streams and details
http.get(Uri.parse('https://iptv-org.github.io/api/channels.json')), // all
channels and details
]);
final result1 = json.decode(result[0].body) as List<dynamic>;
final result2 = json.decode(result[1].body) as List<dynamic>;
result1.addAll(result2);
return result1;
}
String _name(dynamic media) {
return media['channel'];
}
String _location(dynamic media) {
return media['url'];
}
String _stat(dynamic media) {
return media['status'];
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: FutureBuilder<List<dynamic>>(
future: fetchMedia(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
padding: EdgeInsets.all(8),
itemCount: 5,
itemBuilder: (BuildContext context, int index) {
String status = _stat(snapshot.data[index]);
String myUrl = _location(snapshot.data[index]);
String name =_name(snapshot.data[index]);
if ( status == 'online') {
// ignore: unnecessary_new
VlcPlayerController _vlcViewController =
new VlcPlayerController.network(
myUrl,
autoPlay:true,
);
return Card(
child: Column(
children: <Widget>[
InkWell(
child: ListTile(
title: Text(name),
subtitle: SizedBox(
height: 200,
width: 300,
child: VlcPlayer(
aspectRatio: 16 / 9,
controller: _vlcViewController,
placeholder:
Center(child: CircularProgressIndicator()),
),
),
),)
],
),
) ;
} else{ return Container();} });
} else {
return Center(
child: CircularProgressIndicator(
color: Colors.orange,
));
}
},
),
),
);
Can somebody help please
You might not have internet permission enabled on android. To enable it locate AndroidManifest.xml file and add the following line:
<manifest xlmns:android...>
...
<uses-permission android:name="android.permission.INTERNET" />
<application ...
</manifest>

Flutter Listview.Builder inside bottom sheet widget not loading data on load

The below code does not display any data when the bottomsheet loads. Once the bottomsheet is loaded if I do a save operation on the code editor it loads the data. What am I missing here?
I have a bottomsheet widget which is invoked using a button.
_showBottomSheet() {
showModalBottomSheet(
context: context,
builder: (context) {
return const Contacts();
},
);
}
The above code loads up the Contacts widget that has a Listview.builder in it which is below.
class Contacts extends StatefulWidget {
const Contacts({Key? key}) : super(key: key);
#override
_ContactsState createState() => _ContactsState();
}
class _ContactsState extends State<Contacts> {
List<PhoneBookContact> phoneBookContacts1 = [];
List<PhoneBookContact> phoneBookContacts2 = [];
#override
void initState() {
loadContacts();
super.initState();
}
Future loadContacts() async {
///somecode to gather data for the listview builder
///populates the phoneBookContacts1 & phoneBookContacts2 lists
}
#override
Widget build(BuildContext context) {
return Column(children: [
const Text('Contacts Set 1'),
displayPhoneBookContacts(phoneBookContacts1),
const Text('Contacts Set 2'),
displayPhoneBookContacts(phoneBookContacts2),
]);
}
Widget displayPhoneBookContacts(phoneBookContacts) {
return Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: phoneBookContacts.length,
itemBuilder: (BuildContext context, int index) {
return Card(
margin: const EdgeInsets.all(10),
child: ListTile(
contentPadding: const EdgeInsets.all(10),
title: Column(
children: [
Text(phoneBookContacts[index].phoneBookContact.toString()),
const SizedBox(
height: 20,
),
ListView.separated(
physics: const ClampingScrollPhysics(),
shrinkWrap: true,
itemCount: phoneBookContacts[index].contactNumbers!.length,
separatorBuilder: (BuildContext context, int index) =>
const Divider(),
itemBuilder: (BuildContext context, int phoneIndex) {
return InkWell(
onTap: () {},
child: Row(
children: [
Text(phoneBookContacts[index]
.contactNumbers![phoneIndex]
.phone),
],
),
);
},
),
],
),
),
);
},
),
);
}
}
I don't prefer using FutureBuilder inside StatefulWidget., it will recall the API(future) on every setState. As for comment it is missing setState after initializing the data.
#override
void initState() {
super.initState();
loadContacts();
}
Future loadContacts() async {
///somecode to gather data for the listview builder
///populates the phoneBookContacts1 & phoneBookContacts2
if(mounted){
// if widget build then setState call.if not we don't need to call setState
// for every initstate data loading, we have to ensure it if widget is build or not. most of the case user close screen when data loading, then error happens
setState(() {});// make sure to call setState
}
}
Because function initState() don't await your loadContacts(), data loaded after function build().
You need use FutureBuilder class to rebuild ListView widget after load data
Example:
FutureBuilder(
future: loadContacts(),
builder:(context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
} else {
return Container(
child: ListView.builder(
itemCount: _faouriteList.length,
scrollDirection: Axis.horizontal,
itemBuilder: (BuildContext context, int index) {
return Text('${_faouriteList[index].title}');
}
)
);
}
}
)