您可以修改PageScrollPhysics
以创建捕捉效果。
这是我做的,
import 'package:flutter/material.dart';
Future<void> main() async {
runApp(
MaterialApp(
home: new Main(),
),
);
}
class Main extends StatefulWidget {
@override
_MainState createState() => _MainState();
}
class _MainState extends State<Main> {
static const _scrollPhysics =
const ExtentScrollPhysics(itemExtent: 80, separatorSpacing: 10);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Demo"),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
"Recommended for you",
style: const TextStyle(fontWeight: FontWeight.bold),
),
IconButton(
icon: Icon(Icons.arrow_forward),
onPressed: () {},
)
],
),
SizedBox.fromSize(
size: Size.fromHeight(130),
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: 30,
physics: _scrollPhysics,
separatorBuilder: (context, _) =>
SizedBox(width: _scrollPhysics.dividerSpacing),
itemBuilder: (context, index) {
return SizedBox(
width: _scrollPhysics.itemExtent, // set height for vertical
child: CardItem(),
);
},
),
),
],
),
),
);
}
}
class CardItem extends StatefulWidget {
@override
_CardItemState createState() => _CardItemState();
}
class _CardItemState extends State<CardItem> {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AspectRatio(
aspectRatio: 1,
child: Container(
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(20.0),
),
),
),
const SizedBox(height: 8.0),
Text(
"Tile Name",
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: const TextStyle(fontSize: 12.0),
),
Text(
"1 MB",
style: const TextStyle(
fontSize: 12.0,
color: Colors.black54,
),
),
],
);
}
}
class ExtentScrollPhysics extends ScrollPhysics {
final double itemExtent;
final double dividerSpacing;
const ExtentScrollPhysics(
{ScrollPhysics parent, this.itemExtent, double separatorSpacing})
: assert(itemExtent != null && itemExtent > 0),
dividerSpacing = separatorSpacing ?? 0,
super(parent: parent);
@override
ExtentScrollPhysics applyTo(ScrollPhysics ancestor) {
return ExtentScrollPhysics(
parent: buildParent(ancestor),
itemExtent: itemExtent,
separatorSpacing: dividerSpacing,
);
}
double _getItem(ScrollPosition position) {
return position.pixels / (itemExtent + dividerSpacing);
}
double _getPixels(ScrollPosition position, double item) {
return item * (itemExtent + dividerSpacing);
}
double _getTargetPixels(
ScrollPosition position, Tolerance tolerance, double velocity) {
double page = _getItem(position);
if (velocity < -tolerance.velocity)
page -= 0.5;
else if (velocity > tolerance.velocity) page += 0.5;
return _getPixels(position, page.roundToDouble());
}
@override
Simulation createBallisticSimulation(
ScrollMetrics position, double velocity) {
// If we're out of range and not headed back in range, defer to the parent
// ballistics, which should put us back in range at a page boundary.
if ((velocity <= 0.0 && position.pixels <= position.minScrollExtent) ||
(velocity >= 0.0 && position.pixels >= position.maxScrollExtent))
return super.createBallisticSimulation(position, velocity);
final Tolerance tolerance = this.tolerance;
final double target = _getTargetPixels(position, tolerance, velocity);
if (target != position.pixels)
return ScrollSpringSimulation(spring, position.pixels, target, velocity,
tolerance: tolerance);
return null;
}
@override
bool get allowImplicitScrolling => false;
}