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();
}
);
}
Related
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");
},
),
);
}
}
I've successfully displayed my Firestore Data as a ListView() unfortunately I can't get it to display as a GridView()
I've tried many different methods but can't seem to find the right fit - I'd like a two columns GridView
Here is my ListView code :
#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(
shrinkWrap: true,
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return ListTile(
title: Text(data['num']),
subtitle: Text(data['num']),
);
}).toList(),
);
},
);
Turns out it was easier than I thought :
I just had to change ListView to GridView.count for it to display as a GridView & set crossAxisCount: 2 for two columns
class UserGrid extends StatefulWidget {
#override
_UserGridState createState() => _UserGridState();
}
class _UserGridState extends State<UserGrid> {
final Stream<QuerySnapshot> _usersStream =
FirebaseFirestore.instance.collection('User Data').snapshots();
#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 GridView.count(
shrinkWrap: true,
crossAxisCount: 2,
children: [
...snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return ListTile(
title: Text(data['num']),
subtitle: Text(data['num']),
);
}).toList(),
ElevatedButton(onPressed: () {}, child: Text("Hello")),
],
);
});
}
}
SliverGrid must also be a part of CustomScrollView
class SliverGridWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
///no.of items in the horizontal axis
crossAxisCount: 4,
),
///Lazy building of list
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
/// To convert this infinite list to a list with "n" no of items,
/// uncomment the following line:
/// if (index > n) return null;
return listItem(Utils.getRandomColor(), "Sliver Grid item:\n$index");
},
/// Set childCount to limit no.of items
/// childCount: 100,
),
)
],
),
);
}
I have an app that gets some data from firebase and than calls a class to display a widget based on the data from firebase. I tried adding a swipe up refresh but i have no idea where to put it and what to to call on refresh. I was trying it using the RefreshIndicator.
Here i will put my code in which it calls the database(firebase) and than creates an widget for each event in the database.
If you need any more information, please feel free to comment. Thank you so much for the help!
FutureBuilder(
future: databaseReference.once(),
builder: (context, AsyncSnapshot<DataSnapshot> snapshot) {
List lists = [];
if (snapshot.hasData) {
lists.clear();
Map<dynamic, dynamic> values = snapshot.data.value;
values.forEach((key, values) {
lists.add(values);
});
return new ListView.builder(
primary: false,
padding: EdgeInsets.only(left:12.0,right:12,bottom: 15,),
shrinkWrap: true,
itemCount: lists.length,
itemBuilder: (BuildContext context, int index) {
if(lists[index]["Status"]== "Active"){;
return Container(
child:EvendWidget(lists[index]["EventImage"],
Text(lists[index]["EventName"]).data,
Text(lists[index]["EventLocation"]+ ", "+lists[index]["EventCounty"] ).data,
Text(lists[index]["Date"]+ ", "+lists[index]["Time"]+ " - "+lists[index]["Time"]).data,
Text(lists[index]["Duration"]+ " H").data,
Text(lists[index]["Genre"]).data,
Text(lists[index]["Price"]).data,false));}else{return SizedBox.shrink(); };
});
}
return Container(
margin: const EdgeInsets.only(top: 300),
child:CircularProgressIndicator());
}),
Do something like this..
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: RefreshIndicator(
onRefresh: () async {
//write your code here to update the list*********
},
child: ListView.builder(
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return Text('Line $index');
}
)
),
);
}
}
You can try with below lines may be it will work for you
return RefreshIndicator(
color: Colors.blue,
onRefresh: () {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (_) => HomePage()));
},
child: ListView.builder(
....
));
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...'));
}
},
),
);
}
How to attach flutter swiper to any scroll views?I have horizontal swiper,i scroll down my ui and then i scroll back(up), as a rezult i get Exception 'ScrollController not attached to any scroll views'.
What i should do to fix it.Could you help me?
class SwiperTop extends StatelessWidget {
MBloc _Bloc = BlocProvider.getBloc<MBloc>();
#override
Widget build(BuildContext context) {
return Container(
height: 284.0,
child: StreamBuilder(
initialData: List<MEntity>(),
stream: _mBloc.listMoviesFlux,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) return Container(height: 1, width: 1);
return Swiper(
loop: true,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return _itemM(snapshot.data[index], context, index);
},
viewportFraction: 0.8,
scale: 0.8,
autoplay: true,
duration: 300,
autoplayDelay: 3000,
index: 0,
);
}),
);
}
}