Flutter: BottomNavigationBar + PageView 翻页时崩溃

3,354 阅读9分钟

转载请标明出处: juejin.cn/post/684490…
本文出自:Wos的主页

如果发生 '_debugUltimatePreviousSiblingOf(after, equals: _firstChild)': is not true. 的错误, 请检查是否是以下原因造成的, 并根据下文进行修改

注意: 这篇文章版本久远, 可能不再适用于新版本. 仅供参考, 慎用

示例:

class _HomeContainerState extends State<HomeContainer> {
  PageController _pageController;
  int _currentIndex = 0;

  @override
  void initState() {
    super.initState();
    _pageController = new PageController();
  }
  
  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: PageView(
        children: <Widget>[
          new Container(
            child: new Page1(),
          ),
          new Container(
            child: new Page2(),
          ),
          new Container(
            child: new Page3(),
          ),
          Container(
            child: new Page4(),
          ),
        ],
        controller: _pageController,
        physics: NeverScrollableScrollPhysics(),
        onPageChanged: (int index){
          setState(() {
            _currentIndex = index;
          });
        },
      ),
      bottomNavigationBar: BottomNavigationBar(
        items: [
          _buildItem('page1'),
          _buildItem('page2'),
          _buildItem('page3'),
          _buildItem('page4'),
        ],
        onTap: (int index) {
          _pageController.jumpToPage(index);
        },
        currentIndex: _currentIndex,
        type: BottomNavigationBarType.fixed,
      ),
    );
  }

  _buildItem(String title){
    return BottomNavigationBarItem(
      icon: Icon(
        Icons.account_balance,
      ),
      title: Text(title),
    );
  }
}

以上代码会造成崩溃, 报错信息如下:

报错信息:

Restarted app in 2,823ms.
I/flutter (24543): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (24543): The following assertion was thrown building NotificationListener<KeepAliveNotification>:
I/flutter (24543): 'package:flutter/src/rendering/object.dart': Failed assertion: line 2775 pos 14:
I/flutter (24543): '_debugUltimatePreviousSiblingOf(after, equals: _firstChild)': is not true.
I/flutter (24543): Either the assertion indicates an error in the framework itself, or we should provide substantially
I/flutter (24543): more information in this error message to help you determine and fix the underlying cause.
I/flutter (24543): In either case, please report this assertion by filing a bug on GitHub:
I/flutter (24543):   https://github.com/flutter/flutter/issues/new
I/flutter (24543): When the exception was thrown, this was the stack:
I/flutter (24543): #2      _RenderSliverMultiBoxAdaptor&RenderSliver&ContainerRenderObjectMixin._insertIntoChildList (package:flutter/src/rendering/object.dart)
I/flutter (24543): #3      _RenderSliverMultiBoxAdaptor&RenderSliver&ContainerRenderObjectMixin.insert (package:flutter/src/rendering/object.dart:2809:5)
I/flutter (24543): #4      RenderSliverMultiBoxAdaptor.insert (package:flutter/src/rendering/sliver_multi_box_adaptor.dart:209:11)
I/flutter (24543): #5      SliverMultiBoxAdaptorElement.insertChildRenderObject (package:flutter/src/widgets/sliver.dart:865:18)
I/flutter (24543): #6      RenderObjectElement.attachRenderObject (package:flutter/src/widgets/framework.dart:4513:35)
I/flutter (24543): #7      RenderObjectElement.mount (package:flutter/src/widgets/framework.dart:4279:5)
I/flutter (24543): #8      SingleChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:4653:11)
I/flutter (24543): #9      Element.inflateWidget (package:flutter/src/widgets/framework.dart:2907:14)
I/flutter (24543): #10     Element.updateChild (package:flutter/src/widgets/framework.dart:2710:12)
I/flutter (24543): #11     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #12     Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #13     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:3622:5)
I/flutter (24543): #14     ComponentElement.mount (package:flutter/src/widgets/framework.dart:3617:5)
I/flutter (24543): #15     Element.inflateWidget (package:flutter/src/widgets/framework.dart:2907:14)
I/flutter (24543): #16     Element.updateChild (package:flutter/src/widgets/framework.dart:2710:12)
I/flutter (24543): #17     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #18     Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #19     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:3622:5)
I/flutter (24543): #20     ComponentElement.mount (package:flutter/src/widgets/framework.dart:3617:5)
I/flutter (24543): #21     ParentDataElement.mount (package:flutter/src/widgets/framework.dart:3955:11)
I/flutter (24543): #22     Element.inflateWidget (package:flutter/src/widgets/framework.dart:2907:14)
I/flutter (24543): #23     Element.updateChild (package:flutter/src/widgets/framework.dart:2710:12)
I/flutter (24543): #24     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #25     Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #26     ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:3622:5)
I/flutter (24543): #27     StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:3769:11)
I/flutter (24543): #28     ComponentElement.mount (package:flutter/src/widgets/framework.dart:3617:5)
I/flutter (24543): #29     Element.inflateWidget (package:flutter/src/widgets/framework.dart:2907:14)
I/flutter (24543): #30     Element.updateChild (package:flutter/src/widgets/framework.dart:2710:12)
I/flutter (24543): #31     SliverMultiBoxAdaptorElement.updateChild (package:flutter/src/widgets/sliver.dart:744:36)
I/flutter (24543): #32     SliverMultiBoxAdaptorElement.performRebuild (package:flutter/src/widgets/sliver.dart:702:34)
I/flutter (24543): #33     SliverMultiBoxAdaptorElement.update (package:flutter/src/widgets/sliver.dart:671:7)
I/flutter (24543): #34     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #35     RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:4379:32)
I/flutter (24543): #36     MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4769:17)
I/flutter (24543): #37     _ViewportElement.update (package:flutter/src/widgets/viewport.dart:192:11)
I/flutter (24543): #38     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #39     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #40     Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #41     ProxyElement.update (package:flutter/src/widgets/framework.dart:3909:5)
I/flutter (24543): #42     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #43     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4661:14)
I/flutter (24543): #44     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #45     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4661:14)
I/flutter (24543): #46     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #47     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4661:14)
I/flutter (24543): #48     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #49     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4661:14)
I/flutter (24543): #50     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #51     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #52     Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #53     StatefulElement.update (package:flutter/src/widgets/framework.dart:3799:5)
I/flutter (24543): #54     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #55     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4661:14)
I/flutter (24543): #56     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #57     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4661:14)
I/flutter (24543): #58     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #59     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4661:14)
I/flutter (24543): #60     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #61     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4661:14)
I/flutter (24543): #62     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #63     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #64     Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #65     StatelessElement.update (package:flutter/src/widgets/framework.dart:3702:5)
I/flutter (24543): #66     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #67     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #68     Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #69     StatefulElement.update (package:flutter/src/widgets/framework.dart:3799:5)
I/flutter (24543): #70     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #71     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #72     Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #73     StatefulElement.update (package:flutter/src/widgets/framework.dart:3799:5)
I/flutter (24543): #74     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #75     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #76     Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #77     StatelessElement.update (package:flutter/src/widgets/framework.dart:3702:5)
I/flutter (24543): #78     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #79     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #80     Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #81     StatefulElement.update (package:flutter/src/widgets/framework.dart:3799:5)
I/flutter (24543): #82     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #83     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #84     Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #85     ProxyElement.update (package:flutter/src/widgets/framework.dart:3909:5)
I/flutter (24543): #86     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #87     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #88     Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #89     ProxyElement.update (package:flutter/src/widgets/framework.dart:3909:5)
I/flutter (24543): #90     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #91     RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:4379:32)
I/flutter (24543): #92     MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4769:17)
I/flutter (24543): #93     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #94     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #95     Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #96     StatefulElement.update (package:flutter/src/widgets/framework.dart:3799:5)
I/flutter (24543): #97     Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #98     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #99     Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #100    ProxyElement.update (package:flutter/src/widgets/framework.dart:3909:5)
I/flutter (24543): #101    Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #102    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #103    Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #104    StatefulElement.update (package:flutter/src/widgets/framework.dart:3799:5)
I/flutter (24543): #105    Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #106    SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4661:14)
I/flutter (24543): #107    Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #108    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #109    Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #110    StatelessElement.update (package:flutter/src/widgets/framework.dart:3702:5)
I/flutter (24543): #111    Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #112    SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4661:14)
I/flutter (24543): #113    Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #114    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #115    Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #116    StatefulElement.update (package:flutter/src/widgets/framework.dart:3799:5)
I/flutter (24543): #117    Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #118    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #119    Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #120    StatefulElement.update (package:flutter/src/widgets/framework.dart:3799:5)
I/flutter (24543): #121    Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #122    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #123    Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #124    ProxyElement.update (package:flutter/src/widgets/framework.dart:3909:5)
I/flutter (24543): #125    Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #126    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #127    Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #128    ProxyElement.update (package:flutter/src/widgets/framework.dart:3909:5)
I/flutter (24543): #129    Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #130    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #131    Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #132    StatefulElement.update (package:flutter/src/widgets/framework.dart:3799:5)
I/flutter (24543): #133    Element.updateChild (package:flutter/src/widgets/framework.dart:2699:15)
I/flutter (24543): #134    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3653:16)
I/flutter (24543): #135    Element.rebuild (package:flutter/src/widgets/framework.dart:3495:5)
I/flutter (24543): #136    BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2242:33)
I/flutter (24543): #137    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:626:20)
I/flutter (24543): #138    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:208:5)
I/flutter (24543): #139    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:990:15)
I/flutter (24543): #140    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:930:9)
I/flutter (24543): #141    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:842:5)
I/flutter (24543): #142    _invoke (dart:ui/hooks.dart:120:13)
I/flutter (24543): #143    _drawFrame (dart:ui/hooks.dart:109:3)
I/flutter (24543): (elided 2 frames from class _AssertionError)

下面先来介绍两种简单的解决方法:

解决办法一:

jumpToPage替换为animateToPage

_pageController.animateToPage(
  index,
  duration: Duration(milliseconds: 260),
  curve: Curves.fastOutSlowIn,
)

简单, 但是有动画效果.

解决办法二:

删除onPageChanged, 通过PageController.addListener监听页面变化

_pageController = new PageController()
  ..addListener(() {
    if (_currentIndex != _pageController.page.round()) {
      setState(() {
        _currentIndex = _pageController.page.round();
      });
    }
  });

这种方法仅在禁用PageView的滑动操作(NeverScrollableScrollPhysics)且使用_pageController.jumpToPage进行页面切换时生效.

解决办法三:

使用IndexedStack替代PageView

解决办法四: (推荐)

  1. 新建一个文件: rebuild_layout.dart

  2. 将下列代码拷贝到该文件

import 'package:flutter/material.dart';

class RebuildLayout extends StatefulWidget {
  final WidgetBuilder builder;
  final RebuildLayoutController controller;

  RebuildLayout({Key key, @required this.builder, @required this.controller}) : super(key: key);

  @override
  _RebuildLayoutState createState() => _RebuildLayoutState();
}

class _RebuildLayoutState extends State<RebuildLayout> {
  @override
  void initState() {
    super.initState();

    widget.controller.addListener(_onRebuild);
  }

  @override
  void didUpdateWidget(RebuildLayout oldWidget) {
    super.didUpdateWidget(oldWidget);

    if (oldWidget.controller != widget.controller) {
      oldWidget.controller.removeListener(_onRebuild);
      widget.controller.addListener(_onRebuild);
    }
  }

  @override
  void dispose() {
    widget.controller._listeners.clear();
    super.dispose();
  }

  _onRebuild() => setState(() {});

  @override
  Widget build(BuildContext context) {
    return widget.builder(context);
  }
}

class RebuildLayoutController extends Listenable {
  List<VoidCallback> _listeners = [];

  @override
  void addListener(listener) {
    _listeners.add(listener);
  }

  @override
  void removeListener(listener) {
    _listeners.remove(listener);
  }

  notification() {
    for(var listener in _listeners){
      listener();
    }
  }
}
  1. BottomNavigationBar外面套一层RebuildLayout
bottomNavigationBar: RebuildLayout(builder: (BuildContext context){
  return BottomNavigationBar(
    ...
    onTap: (int index) {
      _pageController.jumpToPage(index);
    },
    currentIndex: _currentIndex,
    type: BottomNavigationBarType.fixed,
  );
}, controller: _rebuildLayoutController),
  1. initState()声明周期中创建_rebuildLayoutController
RebuildLayoutController _rebuildLayoutController;

@override
void initState() {
  super.initState();
  ...
  _rebuildLayoutController = RebuildLayoutController();
}
  1. PageViewonPageChanged属性中调用rebuildLayoutController.notification()
body: PageView(
  ...
  onPageChanged: (int index){
    _currentIndex = index;
    rebuildLayoutController.notification();
  },
),

RebuildLayout的思路是: 只通知其子控件重建, 而不影响其它控件.

没使用RebuildLayout之前, onPageChanged回调中会调用setState来刷新页面, 这将导致PageView和BottomNavigationBar都被重建, 造成性能损耗.

这个控件几乎是完全解耦的, 因此可以用在很多只想局部刷新的地方.


以上, 希望能够解决你的问题 :)