How I can show last 5 elements in widget with listview.builder?
Thanks!
if (snapshot.hasData) {
return ListView.builder(
controller: controller,
// shrinkWrap: true,
// reverse: true,
itemCount: snapshot.data!.length ,
itemBuilder: (BuildContext context, int index) {
// controller.jumpTo(controller.position.maxScrollExtent);
if (snapshot.data![index].operatorName == 'barz1' ||
snapshot.data![index].operatorName == 'barz2') {
return Container(
Try this:
if (snapshot.hasData) {
return ListView.builder(
controller: controller,
itemCount: snapshot.data!.length ,
itemBuilder: (BuildContext context, int index) {
if(index > snapshot.data!.length - 5){ /// <--- add this condition
if (snapshot.data![index].operatorName == 'barz1' ||
snapshot.data![index].operatorName == 'barz2') {
return Container(...);
}
}
}
);
}
You can set reverse property true in Listview.builder and set itemcount to 5 to get last 5 elements in widget with ListView.Builder.
ListView.builder(
reverse: true,
itemCount: 5,
itemBuilder: (ctx, index) {
return Container();
},
);
you can use this method
if (snapshot.hasData) {
final originalList=snapshot.data;
final newList=originalList.sublist(originalList.length-5, originalList.length);
return ListView.builder(
controller: controller,
// shrinkWrap: true,
// reverse: true,
itemCount: newList.length ,
itemBuilder: (BuildContext context, int index) {
// controller.jumpTo(controller.position.maxScrollExtent);
if (newList[index].operatorName == 'barz1' ||
newList[index].operatorName == 'barz2') {
return Container(
You can create sublist from original data.
class Fas4 extends StatelessWidget {
const Fas4({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<List<int>>(
future: Future.value(List.generate(333, (index) => index)),
builder: (context, snapshot) {
//handle other state like error...
if (snapshot.hasData) {
final data = snapshot.data ?? [];
List<int> subList =
data.length <= 5 ? data : data.sublist(data.length - 6);
return ListView.builder(
itemCount: subList.length,
itemBuilder: (context, index) => Text("${subList[index]}"),
);
}
return Text("Loading");
},
),
);
}
}
Related
i'm trying to migrate to null safety.
I'm currently stuck with StreamBuilder and ListView.builder.
I'm unsure how to adjust the code, that the itemCount and an element from the snapshot data can be safely accessed.
Is there a way to cast Object to int?
Errors
The argument type 'Object?' can't be assigned to the parameter type 'int?'.
The method '[]' can't be unconditionally invoked because the receiver can be 'null'.
The code is a minimal version, where I want to display List elements
class _ListState extends State<List> {
#override
Widget build(BuildContext context) {
final database = Provider.of<AppDatabase>(context);
return StreamBuilder(
stream: database.XYZ(),
builder: (context, snapshot) {
if (snapshot.data != null) {
return ListView.builder(
itemCount: snapshot.data.length,
shrinkWrap: true,
itemBuilder: (_, index) {
return ListItem(snapshot.data[index]);
}
);
} else {
return Text("No data");
}
},
);
}
}
Possible Solution - Specify StreamBuilder with Class
class _ListState extends State<List> {
#override
Widget build(BuildContext context) {
final database = Provider.of<AppDatabase>(context);
return StreamBuilder<List<Element>>(
stream: database.XYZ(),
builder: (context, snapshot) {
if (snapshot.data != null) {
return ListView.builder(
itemCount: snapshot.data.length,
shrinkWrap: true,
itemBuilder: (_, index) {
return ListItem(snapshot.data[index]);
}
);
} else {
return Text("No data");
}
},
);
}
}
You can solve it in two ways:
Provide a specific type to your StreamBuilder
StreamBuilder<List> (...)
Use as to downcast.
builder: (context, snapshot) {
if (snapshot.data != null) {
final list = snapshot.data as List; // <-- Downcasting
return ListView.builder(
itemCount: list.length, // Works now
);
} else {
return Text("No data");
}
}
I have a problem about using FutureBuilder in Flutter.
With FutureBuilder, the page is continuously rebuilt.
I've omitted the detailed code to write the question. If you want to see additional code, please leave a comment.
To stop this, What can I do?
Future<bool> initLikes() async {
var list = await ApiProvider().post('/RoomSalesInfo/Select/Like', jsonEncode(
{
"userID" : GlobalProfile.loggedInUser.userID,
}
));
return true;
} else {
return false;
}
}
//This is the Code that I use in Widget build
FutureBuilder(
future: initLikes(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
//해당 부분은 data를 아직 받아 오지 못했을때 실행되는 부분을 의미한다.
if (snapshot.hasData == false) {
return SizedBox();
}
//error가 발생하게 될 경우 반환하게 되는 부분
// 데이터를 정상적으로 받아오게 되면 다음 부분을 실행하게 되는 것이다.
else {
return Expanded(
child: ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.vertical,
controller: _scrollController,
itemCount: GlobalProfile.listForMe.length +1,
itemBuilder: (BuildContext context, int index) {
if(index == GlobalProfile.listForMe.length){
return CupertinoActivityIndicator();
}
else
return Column();
}
),
);
}
})
future: initLikes(),
Don't recomputing this. The new invocation will overwrite the old one. Instead use an initState() to compute it just once into a variable that you reference from "future:..".
I'm trying to load data from Firestore to my flutter app , but I'm stuck in ' loading... ' text , I feel I'm missing something !
Here is the code :
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: kMainColor,
body: StreamBuilder<QuerySnapshot>(
stream:FirebaseFirestore.instance.collection(kProductsCollection).snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
List<Product> products = [];
for (var doc in snapshot.data.docs) {
var data = doc.data();
products.add(Product(
pPrice: data[kProductName],
pName: data[kProductPrice],
pDescription: data[kProductDescription],
pImage: data[kProductImage],
pCategory: data[kProductCategory]));
}
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: .8,
),
itemBuilder: (context, index) => Text(products[index].pName),
itemCount: products.length,
);
}
else {
// I'm stuck in here
return Center(child: Text('Loading...'));
}
},
),
);
}
I want to build cards on the basis of data received from the future which returns a map. Since cardDetails is being fetched from the backend, it requires some time but while building the cards using ListView.builder, it reaches to itemCount before the data is fetched which makes cardDetails to be null. If I hardcode the value of itemCount then, the error disappears and I get cards as required. Any clues on how to solve this issue would be helpful.
Update: It is going into the snapshot.hasError condition but I'm not able to figure out which error is it
In UI
if (_localStorageService.getStringFromLocalStorage() != 'testFalse')
FutureBuilder(
future: _localStorageService.getMapFromLocalStorage(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
cardDetails = snapshot.data;
return ListView.builder(
itemBuilder: (context, index) {
print("Shared Pref hasData");
return cardDetails == null
? CircularProgressIndicator()
: HomepageCards(
user: widget.user,
cardDetails: cardDetails[
cardDetails.keys.toList()[index]],
);
},
// verify if cardDetails is null to prevent app crash
itemCount:
(cardDetails == null ? 0 : cardDetails.keys.length),
scrollDirection: Axis.vertical,
controller: _controller,
shrinkWrap: true,
);
} else if (snapshot.hasError) {
// TODO: Shimmer skeleton
}
return CircularProgressIndicator();
},
)
else
StreamBuilder<DocumentSnapshot>(
stream: Firestore()
.collection('homepage')
.document(widget.user.uid)
.collection('h')
.document('28032020')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.data != null) {
cardDetails = {};
snapshot.data.data.forEach((index, individualDetail) {
cardDetails[index] = individualDetail;
});
_localStorageService
.storeCardInSharedPreference(cardDetails);
cardDetailKeys = snapshot.data.data.keys;
} else if (snapshot.hasError) {
// TODO: Show skeletal shimmer
} else {
// TODO: Convert it to Shimmer with card skeletal layout
CircularProgressIndicator();
}
return cardDetails == null
? CircularProgressIndicator()
: ListView.builder(
itemBuilder: (context, index) {
return HomepageCards(
user: widget.user,
cardDetails:
cardDetails[cardDetails.keys.toList()[index]],
);
},
itemCount: (cardDetailKeys == null
? 0
: cardDetailKeys.length),
scrollDirection: Axis.vertical,
controller: _controller,
shrinkWrap: true,
);
},
)
LocalStorage Service for Shared Preferences
class LocalStorageService {
static SharedPreferences _sharedPreferences;
final String screenkey;
String value;
String _initialSharedValue;
LocalStorageService({#required this.screenkey});
initialiseLocalStorage() async {
_sharedPreferences = await SharedPreferences.getInstance();
persist(screenkey);
}
Future<void> persist(String key) async {
_initialSharedValue = _sharedPreferences?.getString(key);
// will be null if never previously saved
if (_initialSharedValue == null) {
_initialSharedValue = 'testFalse';
}
await _sharedPreferences?.setString(screenkey, _initialSharedValue);
print("share = ${_sharedPreferences?.getString(screenkey)}");
}
storeCardInSharedPreference(Map cardDetails) async {
await _sharedPreferences?.setString(screenkey, json.encode(cardDetails));
}
getMapFromLocalStorage() async {
return await json.decode(_sharedPreferences?.getString(screenkey));
}
String getStringFromLocalStorage() {
return _sharedPreferences?.getString(screenkey);
}
}
This is because regardless of the status of your futurebuilder, Listview is being returned.
If you want to control the status of your futurebuilder, you must put the return inside your if/else/case.
Thus:
FutureBuilder(
future: _localStorageService.getStringFromLocalStorage(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
cardDetails = snapshot.data;
print("number of cards = ${cardDetails.keys.length}");
return ListView.builder(
itemBuilder: (context, index) {
print("card details in futute : ${snapshot.data}");
return cardDetails == null
? CircularProgressIndicator()
: HomepageCards(
user: widget.user,
cardDetails:
cardDetails[cardDetails.keys.toList()[index]],
);
},
// verify if cardDetails is null to prevent app crash
itemCount: (cardDetails == null? 0: cardDetails.keys.length),
scrollDirection: Axis.vertical,
controller: _controller,
shrinkWrap: true,
);
} else if (snapshot.hasError) {
print("Error here in snapshot");
return Center(child:Text("An error has occurred"));
} else {
return CircularProgressIndicator();
}
},
)
I'm trying to make a ListView that auto-scrolls to the newest data point.
I tired to do this by making a _scrollToBottom function that uses the .jumpTo method.
But i get a blank screen in the app, and
'child.parentData != null': is not true. in the debug console.
Any suggestions on how i can implement auto-scrolling?
Here is the relevant portions of my current code:
ScrollController _scrollController = ScrollController();
_scrollToBottom(){ _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: DataShareWidget.of(context).stream,
builder: (BuildContext context, AsyncSnapshot snapshot){
if(snapshot.hasError){ return Text(snapshot.error);}
if(snapshot.hasData){
_dataFormat(snapshot.data);
return ListView.builder(
itemCount: _listViewData.length,
controller: _scrollController,
reverse: true,
shrinkWrap: true,
itemBuilder: (context, index) {
_scrollToBottom();
return ListTile(
title: AutoSizeText(_listViewData[index], maxLines: 2),
dense: true,
);
},
);
}
}
);
}
What you need is to call _scrollToBottom() method once the list is built fully.
Modification is your code (without StreamBuilder):
ScrollController _scrollController = ScrollController();
_scrollToBottom() {
_scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToBottom());
return Scaffold(
body: ListView.builder(
itemCount: 50,
// itemCount: _listViewData.length,
controller: _scrollController,
reverse: true,
shrinkWrap: true,
itemBuilder: (context, index) {
return ListTile(
title: Text('Yo Dummy Text $index'),
// title: AutoSizeText(_listViewData[index], maxLines: 2),
dense: true,
);
},
),
);
}
You need to do this and work perfectly....
ScrollController _scrollController = ScrollController();
#override
Widget build(BuildContext context) {
_scrollController.animateTo(_scrollController.position.maxScrollExtent, duration: Duration(milliseconds: 200), curve: Curves.easeOut);
return StreamBuilder(
stream: stream = Firestore.instance
.collection('your collaction')
.document('your document')
.snapshots(),
builder: (context, snapshot) {
return snapshot.hasData
? ListView.builder(
controller: _scrollController,
shrinkWrap: true,
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) =>
msgTile(snapshot.data.documents[index], user1),
)
: Text('Loading...');
},
);
}
The problem is in your StreamBuilder code. If the snapshot isn't ready you need to return something.
Try this code:
ScrollController _scrollController = ScrollController();
_scrollToBottom(){ _scrollController.jumpTo(_scrollController.position.maxScrollExtent);
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: DataShareWidget.of(context).stream,
builder: (BuildContext context, AsyncSnapshot snapshot){
if(snapshot.hasError){ return Text(snapshot.error);}
if(snapshot.hasData){
_dataFormat(snapshot.data);
return ListView.builder(
itemCount: _listViewData.length,
controller: _scrollController,
reverse: true,
shrinkWrap: true,
itemBuilder: (context, index) {
_scrollToBottom();
return ListTile(
title: AutoSizeText(_listViewData[index], maxLines: 2),
dense: true,
);
},
);
}
//Something waiting the snapshot
return CircularProgressIndicator();
}
);
}