Reputation: 4316
I have the code below that changes the fab opacity on user scroll down or up. but for it, I call the setState()
method so many times. Is there any way to detect the last direction user scrolled so that I can call the setState()
at that moment only Once? Or I appreciate if you can define a new enhanced function for that.
@override
void initState() {
super.initState();
_controller = ScrollController()
..addListener(() {
upDirection = _controller.position.userScrollDirection == ScrollDirection.forward;
upDirection = _controller.position.userScrollDirection == ScrollDirection.reverse;
if (upDirection)
setState(() {
_scrolled = 0.5;
flag = false;
});
else if (!upDirection) {
setState(() {
_scrolled = 1.0;
});
flag = false;
}
});
}
And here is the list
return Scaffold(
body:
SingleChildScrollView(
controller: _controller,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(),
Container(),
Container(),
Container(),
Container(),
Container(),
],
),
),
),
floatingActionButton: AnimatedOpacity(
opacity: _scrolled,
duration: Duration(milliseconds: 100),
child: FloatingActionButton(
onPressed: (){},
child: Icon(Icons.settings),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
);
Upvotes: 2
Views: 1516
Reputation: 4316
Thanks to Omer Celik the full code can be something like below and you can customize it:
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(MaterialApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
ScrollController _controller;
double _scrolled = 1.0;
@override
void initState() {
ScrollDirection _lastScrollDirection;
_controller = ScrollController();
_controller.addListener(() {
if (_lastScrollDirection != _controller.position.userScrollDirection) {
_lastScrollDirection = _controller.position.userScrollDirection;
if (_lastScrollDirection == ScrollDirection.forward) {
setState(() {
_scrolled = 1.0;
});
}
else if (_lastScrollDirection == ScrollDirection.reverse) {
setState(() {
_scrolled = 0.5;
});
}
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body:
ListView.builder(
controller: _controller,
physics: BouncingScrollPhysics(),
padding: const EdgeInsets.all(16.0),
itemCount: 50,
itemBuilder: _buildItem,
),
floatingActionButton: AnimatedOpacity(
opacity: _scrolled,
duration: Duration(milliseconds: 700),
child: FloatingActionButton(
onPressed: (){},
child: Icon(Icons.settings),
),
),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
);
}
Widget _buildItem(BuildContext context, int index) {
return Container(
child: Text(index.toString()),
);
}
}
Upvotes: 0
Reputation: 319
The problem is that the listener you've added will get called every time there's a new event on the controller. That's why your setState
is getting called repeatedly.
You can use flags to prevent calling setState
repeatedly.
Working code:
void initState() {
ScrollDirection _lastScrollDirection; // <---- Notice the new variable here.
_controller = ScrollController();
_controller.addListener(() {
if (_lastScrollDirection != _controller.position.userScrollDirection) {
_lastScrollDirection = _controller.position.userScrollDirection;
setState(() {
//You can set whatever you want here.
//It will only get called once when user changes direction.
});
print('Scroll direction changed --> $_lastScrollDirection');
}
});
super.initState();
}
DEMO:
Upvotes: 6