Karp
Karp

Reputation: 439

"'!_needsLayout': is not true" error in Flutter when changing between screens with BottomNavigationBar

I have a Scaffold with BottomNavigationBar:

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',
            )
          ],
        ),
      ),
    );
  }
}

But when I switch between screens using BottomNavigationBar I sometimes get an error Unhandled Exception: 'package:flutter/src/painting/text_painter.dart': Failed assertion: line 881 pos 12: '!_needsLayout': is not true.

How to fix this issue?

EDIT

Here is the full code with error logs:

main.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,),
    );
  }
}

ERRORS

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

I modified the code a little bit to make it more clear but I tested this code and sometimes it still gives the same error (logs are above).

Upvotes: 3

Views: 6497

Answers (5)

Hiram
Hiram

Reputation: 1

I also encountered the same problem, and I searched for a long time and couldn't find a better solution.

Looking at the code, the reason for the problem is guessed that the theme has been updated (color, font), which causes the TextPainter object to be re-layout, but before the layout is completed, the HitTest event continues to process, which causes the problem.

This question should be a problem with the discussion here https://github.com/flutter/flutter/issues/85108 But still haven't found a feasible solution.

The component that will cause this problem is not only BottomNavigationBarItem but also components such as TextButton that respond to HitTest events.

My temporary workaround is

  1. Modify text_painter.dart to add
bool get needsLayout => _paragraph == null;
  1. Modify 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);
}

Upvotes: 0

Jav T
Jav T

Reputation: 478

There seems to be a bug within flutter itself, by the moment my work around was setting the label styles fontsize to 0 whenever I want to hide the labels.

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

Upvotes: 0

Mohsen Mohamed
Mohsen Mohamed

Reputation: 11

try this workaround

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

i found it on github and tried it and worked for me

Upvotes: 1

Vu Thanh
Vu Thanh

Reputation: 398

I don't know the reason, but try to remove these 2 properties from BottomNavigationBar and it should work. No need to run flutter clean.

showSelectedLabels: false,
showUnselectedLabels: false,

In case you want to use them, add empty string to label property label='' in BottomNavigationBarItem.

Upvotes: 2

Md. Yeasin Sheikh
Md. Yeasin Sheikh

Reputation: 63769

it works here , here try it

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',
            )
          ],
        ),
      ),
    );
  }
}


Upvotes: 1

Related Questions