如何在向下滚动ListView时以动画的方式显示Container,并在向上滚动时隐藏它。
我附加的视频并不是我想要的精确实现,但它只是为了给你一个想法。
示例视频
编辑:
每次我向下滚动时,需要显示Container
,每次我向上滚动时,希望隐藏它。它不应该依赖于ListView
的索引。
如何在向下滚动ListView时以动画的方式显示Container,并在向上滚动时隐藏它。
我附加的视频并不是我想要的精确实现,但它只是为了给你一个想法。
示例视频
编辑:
每次我向下滚动时,需要显示Container
,每次我向上滚动时,希望隐藏它。它不应该依赖于ListView
的索引。
不确定我是否正确理解了您的问题,这是您想要实现的吗?
截图:
代码:
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// Height of your Container
static final _containerHeight = 100.0;
// You don't need to change any of these variables
var _fromTop = -_containerHeight;
var _controller = ScrollController();
var _allowReverse = true, _allowForward = true;
var _prevOffset = 0.0;
var _prevForwardOffset = -_containerHeight;
var _prevReverseOffset = 0.0;
@override
void initState() {
super.initState();
_controller.addListener(_listener);
}
// entire logic is inside this listener for ListView
void _listener() {
double offset = _controller.offset;
var direction = _controller.position.userScrollDirection;
if (direction == ScrollDirection.reverse) {
_allowForward = true;
if (_allowReverse) {
_allowReverse = false;
_prevOffset = offset;
_prevForwardOffset = _fromTop;
}
var difference = offset - _prevOffset;
_fromTop = _prevForwardOffset + difference;
if (_fromTop > 0) _fromTop = 0;
} else if (direction == ScrollDirection.forward) {
_allowReverse = true;
if (_allowForward) {
_allowForward = false;
_prevOffset = offset;
_prevReverseOffset = _fromTop;
}
var difference = offset - _prevOffset;
_fromTop = _prevReverseOffset + difference;
if (_fromTop < -_containerHeight) _fromTop = -_containerHeight;
}
setState(() {}); // for simplicity I'm calling setState here, you can put bool values to only call setState when there is a genuine change in _fromTop
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("ListView")),
body: Stack(
children: <Widget>[
_yourListView(),
Positioned(
top: _fromTop,
left: 0,
right: 0,
child: _yourContainer(),
)
],
),
);
}
Widget _yourListView() {
return ListView.builder(
itemCount: 100,
controller: _controller,
itemBuilder: (_, index) => ListTile(title: Text("Item $index")),
);
}
Widget _yourContainer() {
return Opacity(
opacity: 1 - (-_fromTop / _containerHeight),
child: Container(
height: _containerHeight,
color: Colors.red,
alignment: Alignment.center,
child: Text("Your Container", style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold, color: Colors.white)),
),
);
}
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
const double HEIGHT = 96;
final ValueNotifier<double> notifier = ValueNotifier(0);
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(title: const Text('Test')),
body: Stack(
children: [
NotificationListener<ScrollNotification>(
onNotification: (n) {
if (n.metrics.pixels <= HEIGHT) {
notifier.value = n.metrics.pixels;
}
return false;
},
child: ListView.builder(
itemCount: 42,
itemBuilder: (context, index) {
return Container(
height: 64,
padding: const EdgeInsets.all(16),
alignment: Alignment.centerLeft,
child: Text('Item $index'),
);
},
),
),
HideableWidget(height: HEIGHT, notifier: notifier),
],
),
),
);
}
}
class HideableWidget extends StatelessWidget {
final double height;
final ValueNotifier<double> notifier;
HideableWidget({@required this.height, @required this.notifier});
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<double>(
valueListenable: notifier,
builder: (context, value, child) {
return Transform.translate(
offset: Offset(0, value - height),
child: Container(
height: 80,
color: Colors.red,
),
);
},
);
}
}
class _DemoState extends State<WidgetDemo> {
ScrollController scrollController = new ScrollController();
bool isVisible = true;
@override
initState() {
super.initState();
scrollController.addListener(() {
if (scrollController.position.userScrollDirection ==
ScrollDirection.reverse) {
if (isVisible)
setState(() {
isVisible = false;
});
}
if (scrollController.position.userScrollDirection ==
ScrollDirection.forward) {
if (!isVisible)
setState(() {
isVisible = true;
});
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
new CustomScrollView(
controller: scrollController,
shrinkWrap: true,
slivers: <Widget>[
new SliverPadding(
padding: const EdgeInsets.all(20.0),
sliver: new SliverList(
delegate: new SliverChildListDelegate(
<Widget>[
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
const Text('My temp data'),
const Text('Wow its working'),
],
),
),
),
],
),
AnimatedContainer(
duration: Duration(milliseconds: 400),
height: isVisible ? 60.0 : 0.0,
child: new Container(
color: Colors.green,
width: MediaQuery.of(context).size.width,
child: Center(child: Text("Container")),
)),
],
),
);
}
}