使用自定义ScrollPhysics调整具有动态边界的PageView [英] flutter PageView with dynamic boundary using custom ScrollPhysics

查看:61
本文介绍了使用自定义ScrollPhysics调整具有动态边界的PageView的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试动态设置PageView边界.这是一个简化的示例,从第10页开始,由随机数生成器(leftEnd,rightEnd)设置左右边界.

I'm trying to set PageView boundary dynamically. Here's a simplified example, which starts page 10, and left and right boundary is set by random number generator (leftEnd, rightEnd).

import 'package:flutter/material.dart';
import 'dart:math';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      home: new MyTabbedPage(),
    );
  }
}

class MyTabbedPage extends StatefulWidget {
  const MyTabbedPage({Key key}) : super(key: key);

  @override
  _MyTabbedPageState createState() => new _MyTabbedPageState();
}

class _MyTabbedPageState extends State<MyTabbedPage> with SingleTickerProviderStateMixin {
  final leftEnd = Random().nextInt(5);
  final rightEnd = 10 + Random().nextInt(5);
  CustomScrollPhysics scrollPhysics = CustomScrollPhysics();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(),
        body: PageView.builder(
          controller: PageController(initialPage: 10),
          physics: scrollPhysics,
          itemBuilder: (context, index) {
            scrollPhysics.leftEnd = (index <= leftEnd);
            scrollPhysics.rightEnd = (index >= rightEnd);

            // --------- print (1) ----------
            print("is leftEnd: ${index <= leftEnd}");
            print("is rightEnd: ${index >= rightEnd}");
            print("scrollphysics.leftEnd: ${scrollPhysics.leftEnd}");
            print("scrollphysics.rightEnd: ${scrollPhysics.rightEnd}");
            return Center(
              child: Text("Item $index"),
            );
          }
        ));
  }
}

class CustomScrollPhysics extends ScrollPhysics {
  CustomScrollPhysics({ScrollPhysics parent}) : super(parent: parent);

  bool leftEnd = false;
  bool rightEnd = false;
  bool isGoingLeft = false;

  @override
  CustomScrollPhysics applyTo(ScrollPhysics ancestor) {
    return CustomScrollPhysics(parent: buildParent(ancestor));
  }

  @override
  double applyPhysicsToUserOffset(ScrollMetrics position, double offset) {
    isGoingLeft = offset.sign < 0;
    return offset;
  }

  @override
  double applyBoundaryConditions(ScrollMetrics position, double value) {
    //print("applyBoundaryConditions");
    assert(() {
      if (value == position.pixels) {
        throw FlutterError(
            '$runtimeType.applyBoundaryConditions() was called redundantly.\n'
                'The proposed new position, $value, is exactly equal to the current position of the '
                'given ${position.runtimeType}, ${position.pixels}.\n'
                'The applyBoundaryConditions method should only be called when the value is '
                'going to actually change the pixels, otherwise it is redundant.\n'
                'The physics object in question was:\n'
                '  $this\n'
                'The position object in question was:\n'
                '  $position\n');
      }
      return true;
    }());
    if (value < position.pixels && position.pixels <= position.minScrollExtent)
      return value - position.pixels;
    if (position.maxScrollExtent <= position.pixels && position.pixels < value)
      // overscroll
      return value - position.pixels;
    if (value < position.minScrollExtent &&
        position.minScrollExtent < position.pixels) // hit top edge

      return value - position.minScrollExtent;

    if (position.pixels < position.maxScrollExtent &&
        position.maxScrollExtent < value) // hit bottom edge
      return value - position.maxScrollExtent;

    // --------- print (2) ----------
    if (leftEnd) print("leftEnd");
    if (rightEnd) print("rightEnd");
    if (isGoingLeft) print("isGoingLeft");

    if (leftEnd && !isGoingLeft) {
      return value - position.pixels;
    } else if (rightEnd && isGoingLeft) {
      return value - position.pixels;
    }
    return 0.0;
  }
}

scrollphysics.leftEnd/rightEnd在PageView.builder内部更改(基于打印结果(1)),但在CustomScrollPhysics中没有更改(无打印结果(2)).

scrollphysics.leftEnd/rightEnd changes inside PageView.builder (based on the print (1)), but it doesn't change in CustomScrollPhysics (no print (2)).

有人可以解释这里发生了什么吗?这是为PageView设置动态边界的正确方法吗?

Could anyone explain what's happening here? Is this the right way to set a dynamic boundary for PageView?

推荐答案

ScrollPhysics 被标记为不可变.即使您在 itemBuilder 中更改其布尔值,实际的布尔值也不会更改(如未在中打印 leftEnd rightEnd applyBoundaryConditions ).我没有正式的解释,为什么您的 itemBuilder 中的 scrollPhysics.leftEnd 为什么在类本身未更改的情况下显示所需的已更改布尔值,但我猜是这样是因为您没有将这些变量设置为 final ,并且没有任何阻碍您更改它们,所以您在 _MyTabbedPageState scrollPhysics >会显示这些更改,即使它们在内部未更改也是如此. isGoingLeft 只会被打印,因为它是在 CustomScrollPhysics 本身内部进行更改,而不是从外部进行更改,如 itemBuilder 中所示.

ScrollPhysics which your CustomScrollPhysics is extending from is marked as immutable. Even though you are changing its booleans inside your itemBuilder the actual booleans are not changed (as seen by not printing leftEnd and rightEnd in applyBoundaryConditions). I don't have an official explanation as why scrollPhysics.leftEnd in your itemBuilder shows the desired changed boolean while it didn't change in it's class itself but i would guess it is because you didn't set those variables as final as they should be and nothing is holding you back to change them so your local scrollPhysics in _MyTabbedPageState is showing those changes even though they are not changed internally. isGoingLeft is only getting printed because it is being changed inside the CustomScrollPhysics itself rather then from the outside as seen in itemBuilder.

作为快速解决方案,我创建了另一个类:

As a quick fix i created another class:

class Status {
  bool leftEnd = false;
  bool rightEnd = false;
  bool isGoingLeft = false;
}

稍微更改了 CustomScrollPhysics :

class CustomScrollPhysics extends ScrollPhysics {
  final Status status;

  CustomScrollPhysics(this.status, {ScrollPhysics parent})
      : super(parent: parent);

  @override
  CustomScrollPhysics applyTo(ScrollPhysics ancestor) {
    return CustomScrollPhysics(this.status, parent: buildParent(ancestor));
  }

  ...

并在您的状态的构造函数调用中添加了 Status 实例:

and added a Status instance inside the constructor call in your state:

CustomScrollPhysics scrollPhysics = CustomScrollPhysics(Status());

CustomScrollPhysics 中的 this.status 进行所有更改.现在它应该可以按预期工作了.

Apply every change to this.status inside CustomScrollPhysics. Now it should work as you intended.

这篇关于使用自定义ScrollPhysics调整具有动态边界的PageView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆