将 flutter PageView 对齐到屏幕左侧
Align a flutter PageView to the screen left
我想渲染带有水平分页滚动的卡片,并且能够在每次可见时看到上一张和下一张卡片的边框。 flutter PageView 小部件几乎可以产生我想要的结果,但它不会显示页面以我想要的方式对齐,这是我的代码
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'PageView Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'PageView Alignment'),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: PageView.builder(
itemCount: 5,
itemBuilder: (context, i) => Container(
color: Colors.blue,
margin: const EdgeInsets.only(right: 10),
child: Center(child: Text("Page $i")),
),
controller: PageController(viewportFraction: .7),
),
);
}
}
这是上面代码产生的结果
我希望 PageView 与屏幕左侧对齐,或者至少与第一页对齐,即删除 Page 0
左侧的空白 space。我是否缺少任何 PageView 参数?或者是否存在产生我正在寻找的结果的其他组件?
在对我自己的需求进行了更深入的分析并检查了 PageView
小部件的源代码之后,我意识到我需要一个滚动小部件,它可以逐项工作,但同时我需要给每个项目的 space 与普通滚动条相同,所以我需要更改普通滚动条的 ScrollPhysics
。在 found this post 中,它在某种程度上描述了 flutter 中的滚动物理并且接近我的需要,不同之处在于我需要在当前可见小部件的双侧添加 space,而不是只在右边。
所以我把post中的CustomScrollPhysics
改成这样(post代码中变化的部分被<--
和-->
条评论:
class CustomScrollPhysics extends ScrollPhysics {
final double itemDimension;
const CustomScrollPhysics(
{required this.itemDimension, ScrollPhysics? parent})
: super(parent: parent);
@override
CustomScrollPhysics applyTo(ScrollPhysics? ancestor) {
return CustomScrollPhysics(
itemDimension: itemDimension, parent: buildParent(ancestor));
}
double _getPage(ScrollMetrics position, double portion) {
// <--
return (position.pixels + portion) / itemDimension;
// -->
}
double _getPixels(double page, double portion) {
// <--
return (page * itemDimension) - portion;
// -->
}
double _getTargetPixels(
ScrollMetrics position,
Tolerance tolerance,
double velocity,
double portion,
) {
// <--
double page = _getPage(position, portion);
// -->
if (velocity < -tolerance.velocity) {
page -= 0.5;
} else if (velocity > tolerance.velocity) {
page += 0.5;
}
// <--
return _getPixels(page.roundToDouble(), portion);
// -->
}
@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 portion = (position.extentInside - itemDimension) / 2;
final double target =
_getTargetPixels(position, tolerance, velocity, portion);
// -->
if (target != position.pixels) {
return ScrollSpringSimulation(spring, position.pixels, target, velocity,
tolerance: tolerance);
}
return null;
}
@override
bool get allowImplicitScrolling => false;
}
综上所述,我做的就是把当前可见的widget(即(position.extentInside - itemDimension) / 2
)剩下的多余space的一半,根据滚动位置加入到页面计算中,允许小部件小于可见滚动大小,但将整个范围视为单个页面,并将其减去基于页面的滚动像素计算,防止将“页面”放置在一半可见部分之前或之前他们身边的小部件。
另一个变化是itemDimension
不是滚动范围除以元素数量,我需要这个值是滚动方向上每个小部件的大小。
这就是我的结局:
当然,这个实现有一些限制:
- 滚动方向上每个元素的大小必须固定,如果单个元素的大小不同,则整个滚动行为不规律
- 大小必须包括填充,以防有一些,否则它与具有不同大小的小部件具有相同的效果
我没有专注于解决这个限制和拥有一个更完整的小部件,因为在我需要这个小部件的情况下确保了这个限制。下面是上面例子的完整代码。
https://gist.github.com/rolurq/5db4c0cb7db66cf8f5a59396faeec7fa
将 PageView 的 padEnds
属性 设置为 false
应该可以解决问题。
...属性 过去可能不存在。
Flutter 添加了新的 属性 padEnds 将其设置为 False
我想渲染带有水平分页滚动的卡片,并且能够在每次可见时看到上一张和下一张卡片的边框。 flutter PageView 小部件几乎可以产生我想要的结果,但它不会显示页面以我想要的方式对齐,这是我的代码
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'PageView Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'PageView Alignment'),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: PageView.builder(
itemCount: 5,
itemBuilder: (context, i) => Container(
color: Colors.blue,
margin: const EdgeInsets.only(right: 10),
child: Center(child: Text("Page $i")),
),
controller: PageController(viewportFraction: .7),
),
);
}
}
这是上面代码产生的结果
我希望 PageView 与屏幕左侧对齐,或者至少与第一页对齐,即删除 Page 0
左侧的空白 space。我是否缺少任何 PageView 参数?或者是否存在产生我正在寻找的结果的其他组件?
在对我自己的需求进行了更深入的分析并检查了 PageView
小部件的源代码之后,我意识到我需要一个滚动小部件,它可以逐项工作,但同时我需要给每个项目的 space 与普通滚动条相同,所以我需要更改普通滚动条的 ScrollPhysics
。在 found this post 中,它在某种程度上描述了 flutter 中的滚动物理并且接近我的需要,不同之处在于我需要在当前可见小部件的双侧添加 space,而不是只在右边。
所以我把post中的CustomScrollPhysics
改成这样(post代码中变化的部分被<--
和-->
条评论:
class CustomScrollPhysics extends ScrollPhysics {
final double itemDimension;
const CustomScrollPhysics(
{required this.itemDimension, ScrollPhysics? parent})
: super(parent: parent);
@override
CustomScrollPhysics applyTo(ScrollPhysics? ancestor) {
return CustomScrollPhysics(
itemDimension: itemDimension, parent: buildParent(ancestor));
}
double _getPage(ScrollMetrics position, double portion) {
// <--
return (position.pixels + portion) / itemDimension;
// -->
}
double _getPixels(double page, double portion) {
// <--
return (page * itemDimension) - portion;
// -->
}
double _getTargetPixels(
ScrollMetrics position,
Tolerance tolerance,
double velocity,
double portion,
) {
// <--
double page = _getPage(position, portion);
// -->
if (velocity < -tolerance.velocity) {
page -= 0.5;
} else if (velocity > tolerance.velocity) {
page += 0.5;
}
// <--
return _getPixels(page.roundToDouble(), portion);
// -->
}
@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 portion = (position.extentInside - itemDimension) / 2;
final double target =
_getTargetPixels(position, tolerance, velocity, portion);
// -->
if (target != position.pixels) {
return ScrollSpringSimulation(spring, position.pixels, target, velocity,
tolerance: tolerance);
}
return null;
}
@override
bool get allowImplicitScrolling => false;
}
综上所述,我做的就是把当前可见的widget(即(position.extentInside - itemDimension) / 2
)剩下的多余space的一半,根据滚动位置加入到页面计算中,允许小部件小于可见滚动大小,但将整个范围视为单个页面,并将其减去基于页面的滚动像素计算,防止将“页面”放置在一半可见部分之前或之前他们身边的小部件。
另一个变化是itemDimension
不是滚动范围除以元素数量,我需要这个值是滚动方向上每个小部件的大小。
这就是我的结局:
当然,这个实现有一些限制:
- 滚动方向上每个元素的大小必须固定,如果单个元素的大小不同,则整个滚动行为不规律
- 大小必须包括填充,以防有一些,否则它与具有不同大小的小部件具有相同的效果
我没有专注于解决这个限制和拥有一个更完整的小部件,因为在我需要这个小部件的情况下确保了这个限制。下面是上面例子的完整代码。
https://gist.github.com/rolurq/5db4c0cb7db66cf8f5a59396faeec7fa
将 PageView 的 padEnds
属性 设置为 false
应该可以解决问题。
...属性 过去可能不存在。
Flutter 添加了新的 属性 padEnds 将其设置为 False