Skip to content

TabBarView may throw out when work with LayoutBuilder #104994

@xu-baolin

Description

@xu-baolin

Code 1

import 'package:flutter/material.dart';

List<String> tabTextContent = <String>[];
void main() => runApp(MaterialApp(
      home: StatefulBuilder(
        builder: (BuildContext context, StateSetter setState) {
          return DefaultTabController(
            length: tabTextContent.length,
            child: Scaffold(
              appBar: AppBar(
                title: const Text('Default TabBar Preview'),
                bottom: tabTextContent.isNotEmpty
                    ? TabBar(
                        isScrollable: true,
                        tabs: tabTextContent
                            .map((String textContent) => Tab(text: textContent))
                            .toList(),
                      )
                    : null,
              ),
              body: LayoutBuilder(
                builder: (_, __) {
                  return tabTextContent.isNotEmpty
                      ? TabBarView(
                          children: tabTextContent
                              .map((String textContent) =>
                                  Tab(text: "$textContent's view"))
                              .toList(),
                        )
                      : const Center(
                          child: Text('No tabs'),
                        );
                },
              ),
              bottomNavigationBar: BottomAppBar(
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    IconButton(
                      key: const Key('Add tab'),
                      icon: const Icon(Icons.add),
                      onPressed: () {
                        setState(() {
                          tabTextContent = List<String>.from(tabTextContent)
                            ..add('Tab ${tabTextContent.length + 1}');
                        });
                      },
                    ),
                    IconButton(
                      key: const Key('Delete tab'),
                      icon: const Icon(Icons.delete),
                      onPressed: () {
                        setState(() {
                          tabTextContent = List<String>.from(tabTextContent)
                            ..removeLast();
                        });
                      },
                    ),
                  ],
                ),
              ),
            ),
          );
        },
      ),
    ));

Code 2

import 'package:flutter/material.dart';

List<String> tabTextContent = <String>[];
void main() => runApp(MaterialApp(
      home: StatefulBuilder(
        builder: (BuildContext context, StateSetter setState) {
          return DefaultTabController(
            length: tabTextContent.length,
            child: Scaffold(
              extendBodyBehindAppBar: true,
              appBar: AppBar(
                title: const Text('Default TabBar Preview'),
                bottom: tabTextContent.isNotEmpty
                    ? TabBar(
                        isScrollable: true,
                        tabs: tabTextContent
                            .map((String textContent) => Tab(text: textContent))
                            .toList(),
                      )
                    : null,
              ),
              body: tabTextContent.isNotEmpty
                  ? TabBarView(
                      children: tabTextContent
                          .map((String textContent) =>
                              Tab(text: "$textContent's view"))
                          .toList(),
                    )
                  : const Center(
                      child: Text('No tabs'),
                    ),
              bottomNavigationBar: BottomAppBar(
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    IconButton(
                      key: const Key('Add tab'),
                      icon: const Icon(Icons.add),
                      onPressed: () {
                        setState(() {
                          tabTextContent = List<String>.from(tabTextContent)
                            ..add('Tab ${tabTextContent.length + 1}');
                        });
                      },
                    ),
                    IconButton(
                      key: const Key('Delete tab'),
                      icon: const Icon(Icons.delete),
                      onPressed: () {
                        setState(() {
                          tabTextContent = List<String>.from(tabTextContent)
                            ..removeLast();
                        });
                      },
                    ),
                  ],
                ),
              ),
            ),
          );
        },
      ),
    ));

Steps

1, Run the code 1 or 2, both are ok
2, Tap the add icon to add a tab view
2, Tap the recycle icon to del a tab view

Then the framework will throw out,

======== Exception caught by widgets library =======================================================
The following assertion was thrown building TabBarView(dirty, dependencies: [_TabControllerScope], state: _TabBarViewState#f654a):
Controller's length property (0) does not match the number of tabs (1) present in TabBar's tabs property.

The relevant error-causing widget was: 
  TabBarView TabBarView:file:///D:/My%20Documents/Desktop/untitled/lib/main.dart:24:21
When the exception was thrown, this was the stack: 
#0      _TabBarViewState.build.<anonymous closure> (package:flutter/src/material/tabs.dart:1557:9)
#1      _TabBarViewState.build (package:flutter/src/material/tabs.dart:1563:6)
#2      StatefulElement.build (package:flutter/src/widgets/framework.dart:4975:27)
#3      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4861:15)
#4      StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5033:11)
#5      Element.rebuild (package:flutter/src/widgets/framework.dart:4587:5)
#6      BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2667:19)
#7      WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:882:21)
#8      RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:378:5)
#9      SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1172:15)
#10     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1101:9)
#11     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:1012:5)
#12     _invoke (dart:ui/hooks.dart:148:13)
#13     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:318:5)
#14     _drawFrame (dart:ui/hooks.dart:115:31)
====================================================================================================

Metadata

Metadata

Assignees

No one assigned

    Labels

    c: crashStack traces logged to the consolef: material designflutter/packages/flutter/material repository.found in release: 3.0Found to occur in 3.0found in release: 3.1Found to occur in 3.1frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work on

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions