【问题标题】:Flutter : PageController.page cannot be accessed before a PageView is built with itFlutter:PageController.page 在使用它构建 PageView 之前无法访问
【发布时间】:2020-07-18 08:52:30
【问题描述】:

如何解决异常 -

未处理的异常:'package:flutter/src/widgets/page_view.dart':断言失败:第 179 行 pos 7:'positions.isNotEmpty':PageController.page 在使用它构建 PageView 之前无法访问。

注意:- 我在两个屏幕中使用它,当我在屏幕之间切换时,它会显示上述异常。

@override
  void initState() {
    super.initState();
      WidgetsBinding.instance.addPostFrameCallback((_) => _animateSlider());
  }

  void _animateSlider() {
    Future.delayed(Duration(seconds: 2)).then(
      (_) {
        int nextPage = _controller.page.round() + 1;

        if (nextPage == widget.slide.length) {
          nextPage = 0;
        }

        _controller
            .animateToPage(nextPage,
                duration: Duration(milliseconds: 300), curve: Curves.linear)
            .then(
              (_) => _animateSlider(),
            );
      },
    );
  }

【问题讨论】:

  • 你能告诉我你使用pageViewController的整个代码吗?

标签: flutter dart flutter-pageview


【解决方案1】:

这意味着您正在尝试访问PageController.page(可能是您或第三方包,如 Page Indicator),但是,当时 Flutter 尚未渲染引用控制器的 PageView 小部件.

最佳解决方案:使用FutureBuilderFuture.value

这里我们只是使用pageController 上的page 属性将代码包装到未来的构建器中,这样它在PageView 被渲染后几乎不会被渲染。

我们使用Future.value(true),这将导致Future 立即完成,但仍然等待足够的时间等待下一帧成功完成,因此PageView 在我们引用它之前就已经构建好了。

class Carousel extends StatelessWidget {

  final PageController controller;

  Carousel({this.controller});

  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[

        FutureBuilder(
          future: Future.value(true),
          builder: (BuildContext context, AsyncSnapshot<void> snap) {
            
            //If we do not have data as we wait for the future to complete,
            //show any widget, eg. empty Container
            if (!snap.hasData) {
             return Container();
            }

            //Otherwise the future completed, so we can now safely use the controller.page
            return Text(controller.controller.page.round().toString);
          },
        ),

        //This PageView will be built immediately before the widget above it, thanks to
        // the FutureBuilder used above, so whenever the widget above is rendered, it will
        //already use a controller with a built `PageView`        

        PageView(
          physics: BouncingScrollPhysics(),
          controller: controller,
          children: <Widget>[
           AnyWidgetOne(),
           AnyWidgetTwo()
          ],
        ),
      ],
    );
  }
}

或者

或者,您仍然可以使用 FutureBuilder 与在 addPostFrameCallback 中的 initState lifehook 中完成的未来,因为它也将在当前帧渲染后完成未来,这将具有与上述相同的效果解决方案。但是我强烈推荐第一个解决方案,因为它很简单

 WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
     //Future will be completed here 
     // e.g completer.complete(true);
    });

【讨论】:

    【解决方案2】:

    我认为你可以像这样使用监听器:

    int _currentPage;
    
      @override
      void initState() {
        super.initState();
        _currentPage = 0;
        _controller.addListener(() {
          setState(() {
            _currentPage = _controller.page.toInt();
          });
        });
      }
    

    【讨论】:

      【解决方案3】:

      我没有足够的信息来确切了解您的问题出在哪里,但我刚刚遇到了一个类似的问题,我想将 PageView 和标签分组在同一个小部件中,并且我想将当前幻灯片和标签标记为活动所以我需要访问controler.page 才能做到这一点。这是我的解决方法:

      修复在使用FutureBuilder 小部件构建PageView 小部件之前访问页面索引

      class Carousel extends StatelessWidget {
        final PageController controller;
      
        Carousel({this.controller});
      
        /// Used to trigger an event when the widget has been built
        Future<bool> initializeController() {
          Completer<bool> completer = new Completer<bool>();
      
          /// Callback called after widget has been fully built
          WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
            completer.complete(true);
          });
      
          return completer.future;
        } // /initializeController()
      
        Widget build(BuildContext context) {
          return Stack(
            children: <Widget>[
              // **** FIX **** //
              FutureBuilder(
                future: initializeController(),
                builder: (BuildContext context, AsyncSnapshot<void> snap) {
                  if (!snap.hasData) {
                    // Just return a placeholder widget, here it's nothing but you have to return something to avoid errors
                    return SizedBox();
                  }
      
                  // Then, if the PageView is built, we return the labels buttons
                  return Column(
                    children: <Widget>[
                      CustomLabelButton(
                        child: Text('Label 1'),
                        isActive: controller.page.round() == 0,
                        onPressed: () {},
                      ),
                      CustomLabelButton(
                        child: Text('Label 2'),
                        isActive: controller.page.round() == 1,
                        onPressed: () {},
                      ),
                      CustomLabelButton(
                        child: Text('Label 3'),
                        isActive: controller.page.round() == 2,
                        onPressed: () {},
                      ),
                    ],
                  );
                },
              ),
              // **** /FIX **** //
              PageView(
                physics: BouncingScrollPhysics(),
                controller: controller,
                children: <Widget>[
                  CustomPage(),
                  CustomPage(),
                  CustomPage(),
                ],
              ),
            ],
          );
        }
      }
      

      修复如果您需要直接在 PageView 子项中的索引

      您可以改用有状态小部件:

      class Carousel extends StatefulWidget {
        Carousel();
      
        @override
        _HomeHorizontalCarouselState createState() => _CarouselState();
      }
      
      class _CarouselState extends State<Carousel> {
        final PageController controller = PageController();
        int currentIndex = 0;
      
        @override
        void initState() {
          super.initState();
      
          /// Attach a listener which will update the state and refresh the page index
          controller.addListener(() {
            if (controller.page.round() != currentIndex) {
              setState(() {
                currentIndex = controller.page.round();
              });
            }
          });
        }
      
        @override
        void dispose() {
          controller.dispose();
      
          super.dispose();
        }
      
        Widget build(BuildContext context) {
          return Stack(
            children: <Widget>[
                 Column(
                    children: <Widget>[
                      CustomLabelButton(
                        child: Text('Label 1'),
                        isActive: currentIndex == 0,
                        onPressed: () {},
                      ),
                      CustomLabelButton(
                        child: Text('Label 2'),
                        isActive: currentIndex == 1,
                        onPressed: () {},
                      ),
                      CustomLabelButton(
                        child: Text('Label 3'),
                        isActive: currentIndex == 2,
                        onPressed: () {},
                      ),
                    ]
              ),
              PageView(
                physics: BouncingScrollPhysics(),
                controller: controller,
                children: <Widget>[
                  CustomPage(isActive: currentIndex == 0),
                  CustomPage(isActive: currentIndex == 1),
                  CustomPage(isActive: currentIndex == 2),
                ],
              ),
            ],
          );
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2021-03-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-09
        • 1970-01-01
        • 2021-09-21
        • 2020-07-10
        • 2014-09-08
        相关资源
        最近更新 更多