RefreshIndicator on horizontal ListView.builder - flutter

I have a horizontal ListView.builder widget that I want to refresh with a RefreshIndicator when pulling it to the left.
FutureBuilder(
future: _initGetTopX(),
builder: (context, wikiSnapshot) {
if (ConnectionState.active != null &&
!wikiSnapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
if (ConnectionState.done != null &&
wikiSnapshot.hasError) {
return Center(child: Text(wikiSnapshot.error));
}
return Container(
height: 220,
child: RefreshIndicator(
onRefresh: _refreshInitGetTopX,
child: ListView.builder(
scrollDirection: Axis.horizontal,
physics: const AlwaysScrollableScrollPhysics(),
itemCount: _getTopXList.length,
itemBuilder: (context, index) {
return Row(
children: [
MainImageThumb(
myTitle: _getTopXList[index].title +
" (" +
The List is loading initally but the RefreshIndicator doesn't show up and it doesn't reload neither...
How can I reload this list?

You can use ScrollController to listen for scrollExtents
ScrollController _controller;
We instantiate it within our initState method, in the following way:
#override
void initState() {
_controller = ScrollController();
super.initState();
}
Then we assign this _controller to our ListView.
controller: _controller,
With this we have our ListView connected with our ScrollController, we just have to listen to the events to determine what we want.
Listening to events
#override
void initState() {
_controller = ScrollController();
_controller.addListener(_scrollListener);
super.initState();
}
Now we are listening to the Scroll events, but how we can know if the scroll reach the top or bottom.
We just have to add these validations:
_scrollListener() {
if (_controller.offset >= _controller.position.maxScrollExtent &&
!_controller.position.outOfRange) {
setState(() {
message = "reach the end right";
});
}
if (_controller.offset <= _controller.position.minScrollExtent &&
!_controller.position.outOfRange) {
setState(() {
message = "reach the end left";
});
}
}
for indepth you can read more here

Related

Flutter: I wants to show message to the user only if the user reaches end of the ListView

Below is my code but it is showing the SnackBar frequently when I reach the bottom of ListView. It also shows the SnackBar on the pages also but I wants to show it only one time how to do that.
final snackBar = SnackBar(content: const Text('Yay! A SnackBar!'));
Expanded(
child: ListView.builder(
controller: _scrollController,
itemCount: docs.length,
itemBuilder: (context, index) {
final doc = docs[index];
print(doc);
//_checkController();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
ScaffoldMessenger.of(context).showSnackBar(snackBar);
} else {
if (_scrollController.position.pixels !=
_scrollController.position.maxScrollExtent) {
return null;
}
}
});
return builddoc(doc);
},
Because you are assigning new listeners every time item builder calls.
put this code in ititState so it just called once.
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
ScaffoldMessenger.of(context).showSnackBar(snackBar);
} else {
if (_scrollController.position.pixels !=
_scrollController.position.maxScrollExtent) {
return null;
}
}
});
Remove the listener from the itembuilder, instead use it in initState(),as everytime item is builded on listview, it will call this listener, so it is going on everytime item get builded.
You can use Listener on ScrollController, your issue is that you assign Listener to controller in build method which is wrong, you should do it once in initState. This is a full example of what you want:
class ScrollPageTest extends StatefulWidget {
const ScrollPageTest({Key? key}) : super(key: key);
#override
State<ScrollPageTest> createState() => _ScrollPageTest();
}
class _ScrollPageTest extends State<ScrollPageTest> {
ScrollController controller = ScrollController();
#override
void initState() {
// TODO: implement initState
super.initState();
controller.addListener(() {
if (controller.position.atEdge) {
if (controller.position.pixels != 0) {
final snackBar = SnackBar(content: const Text('Yay! A SnackBar!'));
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
controller: controller,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Text('data = $index'),
);
},
itemCount: 100,
),
);
}
}
Try this:
bool isSnackBarShown = false;
...
Expanded(
child: ListView.builder(
controller: _scrollController,
itemCount: docs.length,
itemBuilder: (context, index) {
final doc = docs[index];
print(doc);
//_checkController();
_scrollController.addListener(() {
if ((_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent)
&& !isSnackBarShown) {
isSnackBarShown = true;
ScaffoldMessenger.of(context).showSnackBar(snackBar);
} else {
if (_scrollController.position.pixels !=
_scrollController.position.maxScrollExtent) {
return null;
}
}
});
return builddoc(doc);
},

Scroll Controller not firing inside List View builder

I am using a ScrollController in the ListView.builder widget.
But when I try to print something after reaching end of the screen it doesn't seem to work.
My main goal id to use it for pagination.
class RestaurantList extends StatefulWidget {
#override
_RestaurantListState createState() => _RestaurantListState();
}
class _RestaurantListState extends State<RestaurantList> {
ScrollController _controller;
void _scrollListener() {
if (_controller.offset >= _controller.position.maxScrollExtent &&
!_controller.position.outOfRange) {
print('new rests');
}
}
#override
void initState() {
_controller = ScrollController();
_controller.addListener(_scrollListener);
super.initState();
}
#override
Widget build(BuildContext context) {
// print('restaurant list');
return FutureBuilder<List<Restaurant>>(
future: Provider.of<Restaurants>(context, listen: false)
.fetchAndSetRestaurants(),
builder: (context, snapshot) {
// Items are not available and you need to handle this situation, simple solution is to show a progress indicator
if (!snapshot.hasData) {
//Navigator.of(context).pushNamed('loading');
return Center(child: LoadingList());
}
final List<Restaurant> restaurants = snapshot.data;
print('List: ${restaurants[0].name}');
return ListView.builder(
controller: _controller,
physics: ScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (ctx, i) =>
restaurants[i].isAvailable || restaurants[i].totalQuantity != 0
? Column(
children: [
ChangeNotifierProvider.value(
value: restaurants[i], child: RestaurantInfo()),
Container(
height: 10,
)
],
)
: Container(),
itemCount: restaurants.length,
);
},
);
}
}
Try changing like below when checking reaching end of screen.
void _scrollListener() {
if (_controller.position.atEdge) {
if (_controller.position.pixels == 0) {
// Reaching Top
} else {
// Reaching end of screen
}
}
}

Scroll Controller is not listening to scroll in Flutter

Im trying to listen to scroll for lazy loading but im not getting any Listen values,I have used ListView.builder widget and have attached a scroll contoller (_controller) and have instantiated controller in initState() method, need help with the issue
class _FeedsState extends State<Feeds> {
ScrollController _controller;
int pageNumber = 1;
#override
void initState() {
super.initState();
_controller = new ScrollController();
_controller.addListener(scrollListener);
SchedulerBinding.instance.addPostFrameCallback((_) {
_controller.animateTo(
0.0,
duration: const Duration(milliseconds: 10),
curve: Curves.easeOut,
);
});
}
#override
Widget build(BuildContext context) {
print(widget.feedDetails);
return widget.feedDetails.length == 0
? PostSomething(
isAdmin: widget.isAdmin,
)
: ListView.builder(
shrinkWrap: true,
controller: _controller,
itemBuilder: (context, index) {
return Column(
children: [
index == 0
? PostSomething(
isAdmin: widget.isAdmin,
profilePic: widget.feedDetails[0]["profile_pic"])
: Container(),
(Posts(
index: index,
feedDetails: widget.feedDetails[index],
displayProfileNavigation: widget.displayProfileNavigation,
)),
],
);
},
itemCount: widget.feedDetails.length,
);
}
void scrollListener() {
print("Scrolling");
if (_controller.position.pixels == _controller.position.maxScrollExtent) {
print("Coooool");
}
}
}
make: shrinkWrap: false,, this will enable your scrolling, if this show unbounded height exception, then try
return Scaffold(
body: Expanded(
ListView.builder(....your code..

Flutter: ListView not scrolling

Problem:
Initially I have disabled ListView scrolling, and want to enable it after 3 seconds. The moment app launches and you keep scrolling it for like 5 seconds (without lifting your finger off the screen), the ListView doesn't scroll.
However it should have scrolled because I am enabling scrolling at 3rd second, the console confirms ListView enabled but still I am not able to scroll it.
Code:
bool _enabled = false; // scrolling disabled initially
#override
void initState() {
super.initState();
Timer(Duration(seconds: 3), () {
print("Scrolling enabled");
setState(() => _enabled = true); // scrolling enabled after 3 seconds
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
physics: _enabled ? ClampingScrollPhysics() : NeverScrollableScrollPhysics(),
itemBuilder: (_, i) => ListTile(title: Text("Item $i")),
),
);
}
Here is a workaround for this:
final _scrollController = ScrollController();
var _firstScroll = true;
bool _enabled = false;
#override
void initState() {
super.initState();
Timer(Duration(seconds: 3), () {
setState(() => _enabled = true);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onVerticalDragUpdate: (details) {
if (_enabled && _firstScroll) {
_scrollController
.jumpTo(_scrollController.position.pixels - details.delta.dy);
}
},
onVerticalDragEnd: (_) {
if (_enabled) _firstScroll = false;
},
child: AbsorbPointer(
absorbing: !_enabled,
child: ListView.builder(
controller: _scrollController,
physics: ClampingScrollPhysics(),
itemBuilder: (_, i) => ListTile(title: Text("Item $i")),
),
),
),
);
}
try this out...
class _blabla extends State<blabla> {
Timer _timer;
}
#override
void initState() {
super.initState();
bool _enabled = false;
);
}
_blablaState() {
_timer = new Timer(const Duration(seconds: 3), () {
setState(() => _enabled = true);
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
physics: _enabled ? ClampingScrollPhysics() : NeverScrollableScrollPhysics(),
itemBuilder: (_, i) => ListTile(title: Text("Item $i")),
),
);
}
#override
void dispose() {
super.dispose();
_timer.cancel();
}
I would also try with physics disabled to see if it makes a difference. There may be a conflict.

how to do pagination in GridView (Flutter)

I want to implement pagination in GridView I use GridView.builder I want to download 10 by 10 items when the user reaches the last row
You can do this using a NotificationListener. As a simple demonstration it will increase the length of your GridView whenever it reaches end of page :
var items_number = 10 ;
return NotificationListener<ScrollNotification>(
onNotification: (scrollNotification){
if(scrollNotification.metrics.pixels == scrollNotification.metrics.maxScrollExtent){
setState(() {
items_number += 10 ;
});
}
},
child: GridView.builder(
itemCount: items_number,
itemBuilder: (context, index) {
//.... the reminder of your code
}
),
);
I also needed this but couldn't find any widget for the gridview pagination, so I tried to make a component based on #Mazin Ibrahim's answer below. It seems to be working but not sure if it is the right way to do this.
typedef Future<bool> OnNextPage(int nextPage);
class GridViewPagination extends StatefulWidget {
final int itemCount;
final double childAspectRatio;
final OnNextPage onNextPage;
final Function(BuildContext context, int position) itemBuilder;
final Widget Function(BuildContext context) progressBuilder;
GridViewPagination({
this.itemCount,
this.childAspectRatio,
this.itemBuilder,
this.onNextPage,
this.progressBuilder,
});
#override
_GridViewPaginationState createState() => _GridViewPaginationState();
}
class _GridViewPaginationState extends State<GridViewPagination> {
int currentPage = 1;
bool isLoading = false;
#override
Widget build(BuildContext context) {
return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification sn) {
if (!isLoading && sn is ScrollUpdateNotification && sn.metrics.pixels == sn.metrics.maxScrollExtent) {
setState(() {
this.isLoading = true;
});
widget.onNextPage?.call(currentPage++)?.then((bool isLoaded) {
setState(() {
this.isLoading = false;
});
});
}
return true;
},
child: CustomScrollView(
slivers: <Widget>[
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisSpacing: 1,
mainAxisSpacing: 1,
crossAxisCount: 2,
childAspectRatio: widget.childAspectRatio,
),
delegate: SliverChildBuilderDelegate(
widget.itemBuilder,
childCount: widget.itemCount,
addAutomaticKeepAlives: true,
addRepaintBoundaries: true,
addSemanticIndexes: true,
),
),
if (isLoading)
SliverToBoxAdapter(
child: widget.progressBuilder?.call(context) ?? _defaultLoading(),
),
],
),
);
}
Widget _defaultLoading() {
return Container(
padding: EdgeInsets.all(15),
alignment: Alignment.center,
child: CircularProgressIndicator(),
);
}
}
Example -
GridViewPagination(
itemCount: 10,
childAspectRatio: 1,
itemBuilder: _buildGridItem,
onNextPage: (int nextPage) {
return fetchData();
},
)
create a Scroll controller
ScrollController _scrollController = new ScrollController();
add a scroll event listener
#override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
// Bottom poistion
}
});
}
Now just need to set the controller in your GridView, ListViewand ...
GridView.builder(
controller: _scrollController,
));
You can use this plugin here: Paging. Wrap your GridView inside of it and tell me if this works!