【问题标题】:Nesting PageViews in flutter在颤动中嵌套页面视图
【发布时间】:2018-12-24 09:29:43
【问题描述】:

我想在 Flutter 中嵌套 PageView,在 PageView 中的 Scaffold 中使用 PageView。在外部 PageView 中,我将有徽标和联系信息,以及辅助信息。作为一个孩子,我将有一个带有内部 PageView 和一个 BottomNavigationBar 作为主要用户交互屏幕的脚手架。这是我到目前为止的代码:

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget{

    @override
    State<StatefulWidget> createState() {
        return _MyAppState();
  }

}

class _MyAppState extends State<MyApp>{
    int index = 0;
    final PageController pageController = PageController();
    final Curve _curve = Curves.ease;
    final Duration _duration = Duration(milliseconds: 300);

    _navigateToPage(value){
        pageController.animateToPage(
            value,
            duration: _duration,
            curve: _curve
        );
        setState((){
            index = value;
        });
    }

    @override
    Widget build(BuildContext context) {

        return MaterialApp(
            title: 'PageViewCeption',
            home: PageView(
                children: <Widget>[
                    Container(
                        color: Colors.blue,
                    ),
                    Scaffold(
                        body: PageView(
                            controller: pageController,
                            onPageChanged: (page){
                                setState(() {
                                  index = page;
                                });
                            },
                            children: <Widget>[
                                Container(
                                    child: Center(
                                        child: Text('1', style: TextStyle(color: Colors.white))
                                    )
                                ),
                                Container(
                                    child: Center(
                                        child: Text('2', style: TextStyle(color: Colors.white))
                                    )
                                ),
                                Container(
                                    child: Center(
                                        child: Text('3', style: TextStyle(color: Colors.white))
                                    )
                                ),
                            ],
                        ),
                        backgroundColor: Colors.green,
                        bottomNavigationBar: BottomNavigationBar(
                            type: BottomNavigationBarType.fixed,
                            onTap: (value) =>_navigateToPage(value),
                            currentIndex: index,
                            items: [
                                BottomNavigationBarItem(
                                    icon: Icon(Icons.cake),
                                    title: Text('1')
                                ),
                                BottomNavigationBarItem(
                                    icon: Icon(Icons.cake),
                                    title: Text('2')
                                ),
                                BottomNavigationBarItem(
                                    icon: Icon(Icons.cake),
                                    title: Text('3')
                                )
                            ],
                        ),
                    ),
                    Container(
                        color: Colors.blue
                    )
                ],
            ),
        );
    }
}

结果如下:

问题是:当我在内部PageView中时,我无法从它离开到外部页面在第一页上向左滚动,或者在内部PageView的最后一页上向右滚动。在 BottomNavigationBar 上滚动(滑动)返回外部 PageView 的唯一方法。 在 Scroll Physics Class 的 the docs 中,我们在描述中找到了这个:

例如,确定当用户达到最大滚动范围或用户停止滚动时 Scrollable 的行为方式。

但我还没有想出解决方案。有什么想法吗?

更新 1

我在使用 CustomScrollPhysics 类方面取得了进展:

class CustomScrollPhysics extends ScrollPhysics{

     final PageController _controller;

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

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

     @override
     double applyBoundaryConditions(ScrollMetrics position, double value) {
       assert(() {
         if (value == position.pixels) {
           throw new 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){ // underscroll
         _controller.jumpTo(position.viewportDimension + value);
         return 0.0;
       }
       if (position.maxScrollExtent <= position.pixels && position.pixels < value) {// overscroll
         _controller.jumpTo(position.viewportDimension + (value - position.viewportDimension*2));
         return 0.0;
       }
       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;
       return 0.0;
     }
}

这是对 ClampingScrollPhysics applyBoundaryConditions 的修改。它有点工作,但由于 pageSnapping 它真的是错误的。它发生了,因为根据the docs

任何活动的动画都会被取消。如果用户当前正在滚动,那 操作被取消。

当操作被取消时,如果用户停止拖动屏幕,PageView 开始快速返回到 Scafold 页面,这会使事情变得混乱。关于在这种情况下如何避免页面捕捉或更好地实施的任何想法?

【问题讨论】:

  • 我不知道如何用一个大的 PageView 实现相同的结果。有什么想法吗?
  • 已经有一段时间了,但我认为嵌套是可能的。只听滚动的拖动事件。 (如果您在最后一个选项卡幻灯片(选项卡的长度)上使用 * 物理禁用内部滚动:leftDragActive ? NeverScrollableScrollPhysics() : PageScrollPhysics() * 让我知道它是否有效 :)

标签: dart flutter


【解决方案1】:

我能够在嵌套的PageView 上复制该问题。似乎内部的PageView 覆盖了检测到的手势。这就解释了为什么我们无法导航到外部PageView 的其他页面,但BottomNavigationBar 可以。此thread 中解释了此行为的更多详细信息。

作为一种解决方法,您可以使用单个 PageView 并在外部页面上隐藏 BottomNavigationBar。我稍微修改了你的代码。

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _MyAppState();
  }
}

class _MyAppState extends State<MyApp> {
  var index = 0;
  final PageController pageController = PageController();
  final Curve _curve = Curves.ease;
  final Duration _duration = Duration(milliseconds: 300);
  var isBottomBarVisible = false;

  _navigateToPage(value) {
    // When BottomNavigationBar button is clicked, navigate to assigned page
    switch (value) {
      case 0:
        value = 1;
        break;
      case 1:
        value = 2;
        break;
      case 2:
        value = 3;
        break;
    }
    pageController.animateToPage(value, duration: _duration, curve: _curve);
    setState(() {
      index = value;
    });
  }

  // Set BottomNavigationBar indicator only on pages allowed
  _getNavBarIndex(index) {
    if (index <= 1)
      return 0;
    else if (index == 2)
      return 1;
    else if (index >= 3)
      return 2;
    else
      return 0;
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'PageViewCeption',
      home: Scaffold(
        body: Container(
          child: PageView(
            controller: pageController,
            onPageChanged: (page) {
              setState(() {
                // BottomNavigationBar only appears on page 1 to 3
                isBottomBarVisible = page > 0 && page < 4;
                print('page: $page bottom bar: $isBottomBarVisible');
                index = page;
              });
            },
            children: <Widget>[
              Container(
                color: Colors.red,
              ),
              Container(
                color: Colors.orange,
              ),
              Container(
                color: Colors.yellow,
              ),
              Container(
                color: Colors.green,
              ),
              Container(color: Colors.lightBlue)
            ],
          ),
        ),
        bottomNavigationBar: isBottomBarVisible // if true, generate BottomNavigationBar
            ? new BottomNavigationBar(
                type: BottomNavigationBarType.fixed,
                onTap: (value) => _navigateToPage(value),
                currentIndex: _getNavBarIndex(index),
                items: [
                  BottomNavigationBarItem(icon: Icon(Icons.cake), label: '1'),
                  BottomNavigationBarItem(icon: Icon(Icons.cake), label: '2'),
                  BottomNavigationBarItem(icon: Icon(Icons.cake), label: '3')
                ],
              )
        //else, create an empty container to hide the BottomNavigationBar
            : Container(
                height: 0,
              ),
      ),
    );
  }
}

【讨论】:

  • 谢谢!这是一个很好的开箱即用解决方案!我看到的唯一缺点是BottomNavigationBar 动画不跟随滑动,并且在外部页面上时消失。但我真的很喜欢这个解决方案。
猜你喜欢
  • 1970-01-01
  • 2020-11-20
  • 2018-02-13
  • 2023-04-01
  • 2020-06-04
  • 2021-09-29
  • 1970-01-01
  • 1970-01-01
  • 2022-01-01
相关资源
最近更新 更多