【问题标题】:"'!_needsLayout': is not true" error in Flutter when changing between screens with BottomNavigationBar使用 BottomNavigationBar 在屏幕之间切换时 Flutter 中出现“'!_needsLayout': is not true”错误
【发布时间】:2021-07-20 15:57:36
【问题描述】:

我有一个ScaffoldBottomNavigationBar

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {

  int _currentIndex = 0;
  final List<Widget> _children = [
    const FirstPage(),
    const SecondPage(),
    const ThirdPage(),
  ];

  void onTabTapped(int index) {
  setState(() {
    _currentIndex = index;
  });
}

  @override
  Widget build(BuildContext context) {

    size = MediaQuery.of(context).size;
    topPadding = MediaQuery.of(context).padding.top;


    return Scaffold(
      body: _children[_currentIndex],
      bottomNavigationBar: SizedBox(
        height: 60,
        child: BottomNavigationBar(
          elevation: 0,
          selectedItemColor: Colors.orange,
          unselectedItemColor: Colors.black,
          showSelectedLabels: false,
          showUnselectedLabels: false,
          onTap: onTabTapped,
          currentIndex: _currentIndex, // this will be set when a new tab is tapped
          items: const [
            BottomNavigationBarItem(
              icon: Icon(Icons.home),
              label: 'Home',
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.video_call_rounded),
              label: "Settings",
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.person),
              label: 'Profile',
            )
          ],
        ),
      ),
    );
  }
}

但是当我使用BottomNavigationBar 在屏幕之间切换时,我有时会收到错误Unhandled Exception: 'package:flutter/src/painting/text_painter.dart': Failed assertion: line 881 pos 12: '!_needsLayout': is not true.

如何解决这个问题?

编辑

这是带有错误日志的完整代码:

ma​​in.dart

import 'package:cook_it/home.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {

    SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter App!',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        splashFactory: InkRipple.splashFactory,
      ),
      home: Home(),
    );
  }
}

home.dart

import 'package:cook_it/screens/profile_page.dart';
import 'package:flutter/material.dart';

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {

  int _currentIndex = 0;
  final List<Widget> _children = [
    const ProfilePage(),
    const ProfilePage(),
    const ProfilePage(),
  ];

  void onTabTapped(int index) {
  setState(() {
    _currentIndex = index;
  });
}

  @override
  Widget build(BuildContext context) {


    return Scaffold(
      body: _children[_currentIndex],
      bottomNavigationBar: SizedBox(
        height: 60,
        child: BottomNavigationBar(
          elevation: 0,
          selectedItemColor: Colors.orange,
          unselectedItemColor: Colors.black,
          showSelectedLabels: false,
          showUnselectedLabels: false,
          onTap: onTabTapped,
          currentIndex: _currentIndex, // this will be set when a new tab is tapped
          items: const [
            BottomNavigationBarItem(
              icon: Icon(Icons.home),
              label: 'Home',
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.video_call_rounded),
              label: "Settings",
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.person),
              label: 'Profile',
            )
          ],
        ),
      ),
    );
  }
}

profile_page.dart

import 'package:flutter/material.dart';

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

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

class _ProfilePageState extends State<ProfilePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(backgroundColor: Colors.red,),
    );
  }
}

错误

E/flutter (15525): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: 'package:flutter/src/painting/text_painter.dart': Failed assertion: line 881 pos 12: '!_needsLayout': is not true.
E/flutter (15525): #0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:46:39)
E/flutter (15525): #1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:36:5)
E/flutter (15525): #2      TextPainter.getPositionForOffset
package:flutter/…/painting/text_painter.dart:881
E/flutter (15525): #3      RenderParagraph.hitTestChildren
package:flutter/…/rendering/paragraph.dart:456
E/flutter (15525): #4      RenderBox.hitTest
package:flutter/…/rendering/box.dart:2414
E/flutter (15525): #5      RenderProxyBoxMixin.hitTestChildren
package:flutter/…/rendering/proxy_box.dart:131
E/flutter (15525): #6      RenderTransform.hitTestChildren.<anonymous closure>
package:flutter/…/rendering/proxy_box.dart:2347
E/flutter (15525): #7      BoxHitTestResult.addWithRawTransform
package:flutter/…/rendering/box.dart:826
E/flutter (15525): #8      BoxHitTestResult.addWithPaintTransform
package:flutter/…/rendering/box.dart:751
E/flutter (15525): #9      RenderTransform.hitTestChildren
package:flutter/…/rendering/proxy_box.dart:2343
E/flutter (15525): #10     RenderTransform.hitTest
package:flutter/…/rendering/proxy_box.dart:2337
E/flutter (15525): #11     RenderProxyBoxMixin.hitTestChildren
package:flutter/…/rendering/proxy_box.dart:131
E/flutter (15525): #12     RenderBox.hitTest
package:flutter/…/rendering/box.dart:2414
E/flutter (15525): #13     RenderShiftedBox.hitTestChildren.<anonymous closure>
package:flutter/…/rendering/shifted_box.dart:92
E/flutter (15525): #14     BoxHitTestResult.addWithPaintOffset
package:flutter/…/rendering/box.dart:787
E/flutter (15525): #15     RenderShiftedBox.hitTestChildren
package:flutter/…/rendering/shifted_box.dart:87
E/flutter (15525): #16     RenderBox.hitTest
package:flutter/…/rendering/box.dart:2414
E/flutter (15525): #17     RenderBoxContainerDefaultsMixin.defaultHitTestChildren.<anonymous closure>
package:flutter/…/rendering/box.dart:2775
E/flutter (15525): #18     BoxHitTestResult.addWithPaintOffset
package:flutter/…/rendering/box.dart:787
E/flutter (15525): #19     RenderBoxContainerDefaultsMixin.defaultHitTestChildren
package:flutter/…/rendering/box.dart:2770
E/flutter (15525): #20     RenderFlex.hitTestChildren
package:flutter/…/rendering/flex.dart:1072
E/flutter (15525): #21     RenderBox.hitTest
package:flutter/…/rendering/box.dart:2414
E/flutter (15525): #22     RenderShiftedBox.hitTestChildren.<anonymous closure>
package:flutter/…/rendering/shifted_box.dart:92
E/flutter (15525): #23     BoxHitTestResult.addWithPaintOffset
package:flutter/…/rendering/box.dart:787
E/flutter (15525): #24     RenderShiftedBox.hitTestChildren
package:flutter/…/rendering/shifted_box.dart:87
E/flutter (15525): #25     RenderBox.hitTest
package:flutter/…/rendering/box.dart:2414
E/flutter (15525): #26     RenderProxyBoxMixin.hitTestChildren
package:flutter/…/rendering/proxy_box.dart:131
E/flutter (15525): #27     RenderProxyBoxWithHitTestBehavior.hitTest
package:flutter/…/rendering/proxy_box.dart:178
E/flutter (15525): #28     RenderProxyBoxMixin.hitTestChildren
package:flutter/…/rendering/proxy_box.dart:131
E/flutter (15525): #29     RenderBox.hitTest
package:flutter/…/rendering/box.dart:2414
E/flutter (15525): #30     RenderProxyBoxMixin.hitTestChildren
package:flutter/…/rendering/proxy_box.dart:131
E/flutter (15525): #31     RenderBox.hitTest
package:flutter/…/rendering/box.dart:2414
E/flutter (15525): #32     RenderMouseRegion.hitTest
package:flutter/…/rendering/proxy_box.dart:2905
E/flutter (15525): #33     RenderProxyBoxMixin.hitTestChildren
package:flutter/…/rendering/proxy_box.dart:131
E/flutter (15525): #34     RenderBox.hitTest
package:flutter/…/rendering/box.dart:2414
E/flutter (15525): #35     RenderProxyBoxMixin.hitTestChildren
package:flutter/…/rendering/proxy_box.dart:131
E/flutter (15525): #36     RenderBox.hitTest
package:flutter/…/rendering/box.dart:2414
E/flutter (15525): #37     RenderProxyBoxMixin.hitTestChildren
package:flutter/…/rendering/proxy_box.dart:131
E/flutter (15525): #38     RenderProxyBoxWithHitTestBehavior.hitTest
package:flutter/…/rendering/proxy_box.dart:178
E/flutter (15525): #39     RenderBoxContainerDefaultsMixin.defaultHitTestChildren.<anonymous closure>
package:flutter/…/rendering/box.dart:2775
E/flutter (15525): #40     BoxHitTestResult.addWithPaintOffset
package:flutter/…/rendering/box.dart:787
E/flutter (15525): #41     RenderBoxContainerDefaultsMixin
E/flutter (15525): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: 'package:flutter/src/painting/text_painter.dart': Failed assertion: line 881 pos 12: '!_needsLayout': is not true.
E/flutter (15525): #0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:46:39)
E/flutter (15525): #1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:36:5)
E/flutter (15525): #2      TextPainter.getPositionForOffset
package:flutter/…/painting/text_painter.dart:881
E/flutter (15525): #3      RenderParagraph.hitTestChildren
package:flutter/…/rendering/paragraph.dart:456

我稍微修改了代码以使其更清晰,但我测试了这段代码,有时它仍然给出相同的错误(日志在上面)。

【问题讨论】:

  • 它在这里工作。评论size = MediaQuery.of(context).size;andtopPadding = MediaQuery.of(context).padding.top;
  • @YeasinSheikh 我评论了这些行,但不幸的是这没有帮助

标签: flutter flutter-layout flutter-bottomnavigation


【解决方案1】:

我不知道原因,但尝试从BottomNavigationBar 中删除这两个属性,它应该可以工作。无需运行flutter clean

showSelectedLabels: false,
showUnselectedLabels: false,

如果您想使用它们,请在BottomNavigationBarItem 中的标签属性label='' 中添加空字符串。

【讨论】:

    【解决方案2】:

    这里有用,这里试试

    import 'package:flutter/material.dart';
    
    class FirstPage extends StatelessWidget {
      const FirstPage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Container(
          child: Text("1stPage"),
        );
      }
    }
    
    class SecondPage extends StatelessWidget {
      const SecondPage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Container(
          child: Text("2nd Page"),
        );
      }
    }
    
    class ThirdPage extends StatelessWidget {
      const ThirdPage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Container(
          child: Text("3rd PAge"),
        );
      }
    }
    
    class Home extends StatefulWidget {
      @override
      _HomeState createState() => _HomeState();
    }
    
    class _HomeState extends State<Home> {
      int _currentIndex = 0;
      final List<Widget> _children = [
        const FirstPage(),
        const SecondPage(),
        const ThirdPage(),
      ];
    
      void onTabTapped(int index) {
        setState(() {
          _currentIndex = index;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        // size = MediaQuery.of(context).size;
        // topPadding = MediaQuery.of(context).padding.top;
    
        return Scaffold(
          body: _children[_currentIndex],
          bottomNavigationBar: SizedBox(
            height: 60,
            child: BottomNavigationBar(
              elevation: 0,
              selectedItemColor: Colors.orange,
              unselectedItemColor: Colors.black,
              showSelectedLabels: false,
              showUnselectedLabels: false,
              onTap: onTabTapped,
              currentIndex:
                  _currentIndex, // this will be set when a new tab is tapped
              items: const [
                BottomNavigationBarItem(
                  icon: Icon(Icons.home),
                  label: 'Home',
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.video_call_rounded),
                  label: "Settings",
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.person),
                  label: 'Profile',
                )
              ],
            ),
          ),
        );
      }
    }
    
    
    

    【讨论】:

    • 我评论了这些行,但不幸的是这没有帮助
    • 没有新的错误。剩下的就是Unhandled Exception: 'package:flutter/src/painting/text_painter.dart': Failed assertion: line 881 pos 12: '!_needsLayout': is not true.
    • 我编辑了问题并添加了带有错误日志的完整代码
    • 您在 _children 上使用相同的小部件。
    • 尝试flutter clean并再次运行
    【解决方案3】:

    试试这个解决方法

        BottomNavigationBar(
            unselectedFontSize: 0.0,
            selectedFontSize: 0.0,
            showSelectedLabels: false,
            showUnselectedLabels: false,
    )
    

    我在 github 上找到它并尝试了它并为我工作

    【讨论】:

      【解决方案4】:

      flutter 本身似乎存在一个错误,当我想隐藏标签时,我的工作是将标签样式 fontsize 设置为 0。

      selectedLabelStyle: TextStyle(
            fontSize: widget.showSelectedLabels ? 14 : 0.0,
          ),
          unselectedLabelStyle: TextStyle(
            fontSize: widget.showUnSelectedLabels ? 14 : 0.0,
          ),
      

      【讨论】:

        【解决方案5】:

        我也遇到了同样的问题,找了半天也没找到更好的解决办法。

        看代码,问题原因猜测是主题更新了(颜色,字体),导致TextPainter对象重新布局,但是在布局完成之前,HitTest事件继续进程,从而导致问题。

        这个问题应该是这里讨论的问题 https://github.com/flutter/flutter/issues/85108 但是还没有找到可行的解决方案。

        会导致这个问题的组件不仅是BottomNavigationBarItem,还有TextButton等响应HitTest事件的组件。

        我的临时解决方法是

        1. 修改 text_painter.dart 以添加
        bool get needsLayout => _paragraph == null;
        
        1. 修改paragraph.dart
        bool hitTestChildren(BoxHitTestResult result, { required Offset position }) {
            // Hit test text spans.
            bool hitText = false;
            // add the following three lines
            if (_textPainter.needsLayout) {
                return hitText;
            }
            // This line is the code that caused the exception. 
            // Before this, judge whether layout is required. If so,
            // return false directly and do not process the hittest of this process.
            final TextPosition textPosition = _textPainter.getPositionForOffset(position);
        }
        

        【讨论】:

        • 请好好展示代码
        猜你喜欢
        • 2021-07-13
        • 2021-10-01
        • 2020-08-01
        • 2020-06-30
        • 2020-02-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多