I have a two cards in a Column and I would like to hide one of them when a List that's under it is scrolled down and show it when scrolling back up.
Code looks like this:
final ScrollController scrollController = ScrollController();
#override
void initState() {
super.initState();
scrollController.addListener(() {
if (scrollController.position.pixels > 0 ||
scrollController.position.pixels <
scrollController.position.maxScrollExtent) {
scrollVisibility = false;
} else {
scrollVisibility = true;
}
setState(() {});
});
}
#override
void dispose() {
scrollController.dispose();
super.dispose();
}
body: Column(
children: [
stats(),
ActivityWidget(controller: scrollController),
],
),
stats() {
return StreamBuilder ...
return Column(children: [
Card(),
Visibility(
visible: scrollVisibility,
child: Card(... // this is the one that I want hidden
])
}
The ActivityWidget returns this:
return Expanded(
child: Scrollbar(
controller: widget.scrollController,
child: ListView.builder(
controller: widget.scrollController,
itemCount: activityContent!.length,
itemBuilder: (context, i) { ...
In this case an exception is thrown:
The following assertion was thrown while notifying status listeners for AnimationController: The Scrollbar's ScrollController has no ScrollPosition attached. A Scrollbar cannot be painted without a ScrollPosition. The Scrollbar attempted to use the provided ScrollController. This ScrollController should be associated with the ScrollView that the Scrollbar is being applied to.When providing your own ScrollController, ensure both the Scrollbar and the Scrollable widget use the same one.
Another exception was thrown: The Scrollbar's ScrollController has no ScrollPosition attached.
I tried adding the controller to the ListView too, but then it wouldn't scroll.
Also tried adding the controller to the Scrollbar and the ListView but still didn't work.
Related
i've been trying to make a listview in flutter and get how many pixels the view had scrolled.
Unfortunately, i used NotificationListener
Like this:
NotificationListener<ScrollUpdateNotification>(
child: ListView(
children: [
//content here
],
),
onNotification: (notification) {
//How many pixels scrolled from pervious frame
// print("scrollDelta: ${notification.scrollDelta}");
setState(() {
this.scrollValue = notification.metrics.pixels;
});
//List scroll position
// print("metrics.pixels: ${notification.metrics.pixels}");
return true;
},
),
Issue is that it gave some very poor performance with my application.
So i noticed that you could use a controller directly with the ListView
and i made the following code:
ScrollController controller;
controller.addListener(() {
print("i'm here");
});
var scaffold = Scaffold(
backgroundColor: Color(0xFFFFFFFF),
resizeToAvoidBottomPadding: false,
body: Stack(
children: [
ListView(
controller: controller,
children: [
//content here
],
),
],
),
);
But then i get the following error :
NoSuchMethodError: invalid member on null: 'addListener'
I currently have no idea how to get how many pixel were scrolled in a listview without hindering greatly the performances.
Your ScrollController is initialized as null.
Just give it a value:
ScrollController controller = ScrollController();
Make sure you dispose of it using the dispose method of your StatefulWidget.
#override
void dispose(){
controller.dispose();
}
ScrollController controller;
#override
void initState() {
// TODO: implement initState
controller = new ScrollController()..addListener(_scrollListener);
super.initState();
}
void _scrollListener() {
print(controller.position.extentAfter);
if (controller.position.extentAfter < 500) {
// you reached at page bottom
}
}
//////////////////////////
ListView(
controller: controller,
children: [
//content here
],
),
Sorry i forgot to simply initialise it!
Now it works but there is still my problem with lag whenever i add a listener.
class Search extends StatefulWidget {
String name;
String location;
Search({
this.name,
this.location,
Key key,
}) : super(key: key);
ScrollController controller = ScrollController();
dispose() {
controller.dispose();
}
#override
_Search createState() => _Search();
}
class _Search extends State<Search> {
widget.controller.addListener(() {
setState(() {
scrollValue = widget.controller.position.pixels;
});
});
/// some unrelated code here
ListView(
controller: widget.controller,
children: [
/// some unrelated code here again
}
whenever i add the addListener the performances drop very harshly
So basically I've been trying for a couple of days to allow users to close a modal bottom sheet when they get to the top of the ListView, when swiping on the ListView. However, when they swipe on the list view the widgets register as if I'm just trying to scroll up on the ListView. Is there a physics type for the ListView to close a modal bottom sheet when at the top or a different way to set up an ignore pointer for this?
This is what I've come up with and I hope I explained myself well enough that this problem is understood.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class ModalSheet extends StatefulWidget {
#override
_ModalSheetState createState() => _ModalSheetState();
}
ScrollController _scrollController = ScrollController();
bool close = false;
class _ModalSheetState extends State<ModalSheet> {
#override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.pixels < 1) {
if (_scrollController.position.userScrollDirection ==
ScrollDirection.forward) {
setState(() {
close = true;
});
} else {
setState(() {
close = false;
});
}
} else {
setState(() {
close = false;
});
}
print(_scrollController.position.pixels);
print(_scrollController.position.userScrollDirection);
print(close);
// print(close);
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
Flexible(
child: IgnorePointer(
ignoring: close,
child: ListView.builder(
controller: _scrollController,
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
return ListTile(
title: Text('tile: ${index + 1}'),
);
},
),
),
),
],
),
);
}
}
EDIT: Thanks to #LearningJS888 for the package suggestion below to use this package! much appreciated
Can you elaborate what exactly you're trying to do?
A normal way of closing ModalBottomSheet is by Navigator.pop(context).
I have a listview with some TextFields inside, I think that it will be cool to add the textfield once a user scrolls down,
For example:
We have 5 Elements in ListView after a user reaches the fifth element and continued scrolling down a new element is created.
I couldn't find a solution for this task, I tried to check scrollposition for elements but unfortunately, I got an error
flutter: Another exception was thrown: NoSuchMethodError: The getter
'position' was called on null.
Here is the code..
ScrollController _controller;
#override
void initState() {
super.initState();
_controller = ScrollController();
}
FlatButton(
onPressed: () {
print(_controller.position.minScrollExtent);
},
child: Text('ds'),
),
ListView
- controller: _controller
Here is the one solution if you want to add the new element on the end of the list view
ScrollController _controller;
ListView.builder(
controller: _controller,
itemCount: _items.length,
itemBuilder: (context, index) {
return ListTile(title: Text("Index : $index"));
},
)
#override
void initState() {
_controller = ScrollController();
_controller.addListener(_scrollListener);
super.initState();
}
_scrollListener() {
if (_controller.offset >= _controller.position.maxScrollExtent &&
!_controller.position.outOfRange) {
setState(() {
message = "reach the bottom";
});
}
if (_controller.offset <= _controller.position.minScrollExtent &&
!_controller.position.outOfRange) {
setState(() {
message = "reach the top";
});
}
}
in the maxScrollExtent you need to add the element into the listview. I hope it will helps you.
For more information visit here
For creating a ListView with a dynamic number of children, you might want to check out the ListView.builder factory constructor.
ListView.builder(
itemBuilder: (context, i) => Text('$i'),
)
Check out the "Working with long lists" tutorial for more information.
I'm trying to get which direction(left or right) user swiped by using PageView.
I was able to get direction like so.
Code:
PageController _controller;
#override
void initState() {
_controller = new PageController()..addListener(_listener);
super.initState();
}
_listener() {
if (_controller.position.userScrollDirection == ScrollDirection.reverse) {
print('swiped to right');
} else {
print('swiped to left');
}
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(),
body: PageView.builder(
itemCount: 10,
controller: _controller,
itemBuilder: (context, index) {
return new Center(child: Text('item ${++index}'));
}),
);
}
However since it's not getting end of scrolling, print method
return this many times.
Is there way I can get this after current page switched to next page completely?
flutter: swiped to right
flutter: swiped to right
flutter: swiped to right
flutter: swiped to right
flutter: swiped to right
flutter: swiped to right
flutter: swiped to right
flutter: swiped to right
flutter: swiped to right
flutter: swiped to right
flutter: swiped to right
A better solution is as follows, using the PageView widget's built in function.
PageView(
onPageChanged: (int page) {
// this page variable is the new page and will change before the pageController fully reaches the full, rounded int value
var swipingRight = page > pageController.page;
print(swipingRight);
},
Compare the current _controller.page.round() with the value from the previous listener invocation (store the previous value in the State).
If the current value is greater than the previous value, the user swiped to the right.
If the current value is lower than the previous value, the user swiped to the left.
None of the solutions here worked for me, figuring this out was a huge headache.
I ended up using a gesture detector and disabling the PageView gestures entirely.
The NeverScrollableScrollPhysics() is how you disable the PageView's built-in gestures.
class MyPageView extends StatefulWidget {
#override
_MyPageViewState createState() => _MyPageViewState();
}
class _MyPageViewState extends State<MyPageView> {
PageController _pageController;
Duration pageTurnDuration = Duration(milliseconds: 500);
Curve pageTurnCurve = Curves.ease;
#override
void initState() {
super.initState();
// The PageController allows us to instruct the PageView to change pages.
_pageController = PageController();
}
void _goForward() {
_pageController.nextPage(duration: pageTurnDuration, curve: pageTurnCurve);
}
void _goBack() {
_pageController.previousPage(
duration: pageTurnDuration, curve: pageTurnCurve);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
// Using the DragEndDetails allows us to only fire once per swipe.
onHorizontalDragEnd: (dragEndDetails) {
if (dragEndDetails.primaryVelocity < 0) {
// Page forwards
print('Move page forwards');
_goForward();
} else if (dragEndDetails.primaryVelocity > 0) {
// Page backwards
print('Move page backwards');
_goBack();
}
},
child: PageView.builder(
itemCount: 10,
controller: _pageController,
// NeverScrollableScrollPhysics disables PageView built-in gestures.
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return new Center(child: Text('item ${++index}'));
}),
),
);
}
}
Your code is okay just add a setState
like this
PageController _controller;
#override
void initState() {
_controller = new PageController()..addListener(_listener);
super.initState();
}
_listener() {
setState(() {
if (_controller.position.userScrollDirection == ScrollDirection.reverse) {
print('swiped to right');
} else {
print('swiped to left');
}
});
}
How do I get the current scroll offset inside a Flutter ListView, GridView, SliverList`, etc?
If you're inside the scroll view use Scrollable.of(context).position.pixels.
If you're outside, you probably want to hand a ScrollController in as the controller argument of the scroll view, then you can read controller.offset.
Alternatively, use scroll notifications with NotificationListener.
This was asked on Flutter Gitter, and answered:
https://gitter.im/flutter/flutter?at=591243f18a05641b1167be0e
For someone else, looking for code implementation, you can use ScrollController like this:
Using NotificationListener:
NotificationListener<ScrollNotification>(
onNotification: (scrollNotification) {
print(scrollNotification.metrics.pixels); // <-- This is it.
return false;
},
child: ListView.builder(
itemCount: 200,
itemBuilder: (c, i) => Text('Item $i'),
),
)
Using ScrollController.offset
final ScrollController _controller = ScrollController();
#override
void initState() {
super.initState();
_controller.addListener(() {
print(_controller.offset); // <-- This is it.
});
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
controller: _controller,
itemCount: 200,
itemBuilder: (c, i) => Text('Item $i'),
),
);
}
Another approach is with NotificationListener
NotificationListener(
child: ListView(
controller: _scrollController,
children: ...
),
onNotification: (notification) {
if (notification is ScrollEndNotification) {
print(_scrollController.position.pixels);
}
return false;
},
)