将颤动的 PageView 与屏幕左侧对齐

Posted

技术标签:

【中文标题】将颤动的 PageView 与屏幕左侧对齐【英文标题】:Align a flutter PageView to the screen left 【发布时间】:2019-11-14 05:58:08 【问题描述】:

我想渲染带有水平分页滚动的卡片,并且每次可见时都能看到上一张和下一张卡片的边框。颤动的 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 左侧的空白区域。我缺少任何 PageView 参数吗?或者是否存在其他产生我正在寻找的结果的组件?

【问题讨论】:

【参考方案1】:

在对自己的需求进行更深入的分析并检查PageView 小部件的源代码后,我意识到我需要一个逐项工作的滚动小部件,但同时我也需要它每个项目的空间与普通滚动条相同,因此我需要更改普通滚动条的ScrollPhysics。在找到的 this 帖子中,它在某种程度上描述了颤动中的滚动物理并且接近我的需要,不同之处在于我需要在当前可见小部件的两侧添加空间,而不仅仅是在右侧。

于是我把帖子里的CustomScrollPhysics拿了下来,这样修改(帖子代码中改动的部分用<---->cmets包围:

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;

综上所述,我所做的就是将当前可见小部件(即(position.extentInside - itemDimension) / 2)留下的额外空间的一半,添加到基于滚动位置的页面计算中,让小部件小于可见滚动大小,但将整个范围视为单个页面,并将其减去基于页面的滚动像素计算,防止将“页面”放置在其侧面小部件的半可见部分之后或之前。

另一个变化是itemDimension不是滚动范围除以元素数量,我需要这个值是每个小部件在滚动方向上的大小。

这就是我最终的结果:

当然,这个实现有一些限制:

每个元素在滚动方向上的大小必须是固定的,如果单个元素的大小不同,那么整个滚动就会出现异常 大小必须包含内边距,以防有内边距,否则会产生与拥有不同大小的小部件相同的效果

我没有专注于解决这个限制并拥有一个更完整的小部件,因为在我需要这个小部件的情况下可以确保这些限制。这是上面例子的完整代码。

https://gist.github.com/rolurq/5db4c0cb7db66cf8f5a59396faeec7fa

【讨论】:

支持这个家伙。非常好的解决方案。我希望在 PageView 中实现这一点 谢谢@KoenVanLooveren !!我同意你的观点,这应该在PageView中实现,我不知道为什么不是,我在很多应用程序中都看到了这种行为 确实,也许您可​​以尝试在页面浏览本身中实现这一点并将其合并到主分支? 你是对的,我会的,我应该稍微清理一下并在限制范围内工作。另外,我认为更改设置viewportFraction 时使用的条子可以工作,而无需处理ScrollPhysics ios设备上,默认物理是BouncingScrollPhysics,返回super.createBallisticSimulation(position, velocity)的条件应该包含position.outOfRange,或者方法applyBoundaryConditions应该被覆盖以避免iOS弹跳引起的意外bug效果。【参考方案2】:

PageViewpadEnds 属性设置为 false 应该可以解决问题 ??。

...该属性在过去可能不存在。

【讨论】:

老兄,你拯救了我的一天【参考方案3】:

Flutter 新增属性 padEnds 设置为 False

【讨论】:

以上答案***.com/a/69151759/4575350抄袭 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。

以上是关于将颤动的 PageView 与屏幕左侧对齐的主要内容,如果未能解决你的问题,请参考以下文章

颤动中的幻灯片过渡外观容器

颤动水平列表视图/页面视图,选定元素动画到全宽

在中心对齐列,但在 Flutter 中将文本对齐到屏幕左侧

如何在颤动的pageView中使用AnimatedCrossFade多个小部件?

设置左侧(在容器中)对齐的轮播以到达屏幕右侧(在容器外)

水印将内容对齐到最左端