Skip to content

TabBarView crashes when switching index before layout #86222

@tomgilder

Description

@tomgilder

TabBarView crashes if you try to switch index before it's been laid out.

Steps to Reproduce

  1. Create a TabBarView with a TabController
  2. Try to switch tabs programatically using the controller - but before the TabBarView has been laid out

Tested on stable 2.2.3 and dev 2.4.0-0.0.pre.

Repro

Run this repro and click the button.

The reason the TabBarView hasn't been laid out is due to using Navigator 2.0 - imagine that this state has been restored on startup, with two pages already in the stack.

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

// This allows switching tabs from anywhere
class NavigationManager extends ChangeNotifier {
  int _index = 0;
  int get index => _index;
  set index(int newIndex) {
    _index = newIndex;
    notifyListeners();
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: HomePage());
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final _navManager = NavigationManager();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Navigator(
        onPopPage: (route, result) {
          return route.didPop(result);
        },
        pages: [
          MaterialPage(child: TabPage(navManager: _navManager)),
          MaterialPage(child: SubPage(navManager: _navManager)),
        ],
      ),
    );
  }
}

class TabPage extends StatefulWidget {
  final NavigationManager navManager;

  TabPage({required this.navManager});

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

class _TabPageState extends State<TabPage> with SingleTickerProviderStateMixin {
  late TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 2, vsync: this);

    widget.navManager.addListener(() {
      // Global navigation has updated, change controller

      // This is where the crash occurs, because the TabBarView has not
      // yet been laid out
      _tabController.index = widget.navManager.index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: TabBarView(
        controller: _tabController,
        children: [
          Scaffold(body: Text('One')),
          Scaffold(body: Text('Two')),
        ],
      ),
    );
  }
}

class SubPage extends StatelessWidget {
  final NavigationManager navManager;

  SubPage({required this.navManager});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // Switch the tab
            navManager.index = 1;
          },
          child: Text('Change tab'),
        ),
      ),
    );
  }
}

Expected results: On eventually showing the TabBarView, the index is correct.

Actual results: Crashes due to viewportDimension being null.

Logs
[   +1 ms] An Observatory debugger and profiler on macOS is available at: http://127.0.0.1:51581/6xepRBr4z90=/
[ +110 ms] Activating Dart DevTools...
[+1585 ms] [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: Null check operator used on a null value
[        ] #0      ScrollPosition.viewportDimension (package:flutter/src/widgets/scroll_position.dart:172:53)
[        ] #1      _PagePosition.getPixelsFromPage (package:flutter/src/widgets/page_view.dart:382:19)
[        ] #2      PageController.animateToPage (package:flutter/src/widgets/page_view.dart:197:16)
[        ] #3      _TabBarViewState._warpToCurrentIndex (package:flutter/src/material/tabs.dart:1417:29)
[        ] #4      _TabBarViewState._handleTabControllerAnimationTick (package:flutter/src/material/tabs.dart:1403:7)
[        ] #5      AnimationLocalListenersMixin.notifyListeners (package:flutter/src/animation/listener_helpers.dart:136:19)
[        ] #6      AnimationController.value= (package:flutter/src/animation/animation_controller.dart:366:5)
[        ] #7      TabController._changeIndex (package:flutter/src/material/tab_controller.dart:232:29)
[        ] #8      TabController.index= (package:flutter/src/material/tab_controller.dart:250:5)
[        ] #9      _TabPageState.initState.<anonymous closure> (package:tab_crash/main.dart:70:22)
[        ] #10     ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:243:25)
[        ] #11     NavigationManager.index= (package:tab_crash/main.dart:13:5)
[        ] #12     SubPage.build.<anonymous closure> (package:tab_crash/main.dart:101:24)
[        ] #13     _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:989:21)
[        ] #14     GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24)
[        ] #15     TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:607:11)
[        ] #16     BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:296:5)
[        ] #17     BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:230:7)
[        ] #18     PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:475:9)
[        ] #19     PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:93:12)
[        ] #20     PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:138:9)
[        ] #21     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:397:8)
[        ] #22     PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:136:18)
[        ] #23     PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:122:7)
[        ] #24     GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:439:19)
[        ] #25     GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:419:22)
[        ] #26     RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:287:11)
[        ] #27     GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:374:7)
[        ] #28     GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:338:5)
[        ] #29     GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:296:7)
[        ] #30     GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:279:7)
[        ] #31     _rootRunUnary (dart:async/zone.dart:1370:13)
[        ] #32     _CustomZone.runUnary (dart:async/zone.dart:1265:19)
[        ] #33     _CustomZone.runUnaryGuarded (dart:async/zone.dart:1170:7)
[        ] #34     _invoke1 (dart:ui/hooks.dart:182:10)
[        ] #35     PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:282:7)
[        ] #36     _dispatchPointerDataPacket (dart:ui/hooks.dart:96:31)
[+3413 ms] Activating Dart DevTools... (completed in 5.0s)
[ +562 ms] The Flutter DevTools debugger and profiler on macOS is available at: http://127.0.0.1:9104?uri=http%3A%2F%2F127.0.0.1%3A51581%2F6xepRBr4z90%3D%2F
No issues found! (ran in 1.6s)
[✓] Flutter (Channel stable, 2.2.3, on macOS 11.4 20F71 darwin-x64, locale en-FI)
    • Flutter version 2.2.3 at /Users/tom/fvm/versions/stable
    • Framework revision f4abaa0735 (9 days ago), 2021-07-01 12:46:11 -0700
    • Engine revision 241c87ad80
    • Dart version 2.13.4

[!] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
    • Android SDK at /Users/tom/Library/Android/sdk
    • Platform android-30, build-tools 30.0.2
    • ANDROID_HOME = /Users/tom/Library/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.8+10-b944.6916264)
    ✗ Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/macos#android-setup for more details.

[✓] Xcode - develop for iOS and macOS
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 12.5.1, Build version 12E507
    • CocoaPods version 1.10.1

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 4.2)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.8+10-b944.6916264)

[✓] VS Code (version 1.58.0)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.24.0

[✓] Connected device (2 available)
    • macOS (desktop) • macos  • darwin-x64     • macOS 11.4 20F71 darwin-x64
    • Chrome (web)    • chrome • web-javascript • Google Chrome 91.0.4472.114

! Doctor found issues in 1 category.

Metadata

Metadata

Assignees

Labels

c: crashStack traces logged to the consolef: material designflutter/packages/flutter/material repository.f: routesNavigator, Router, and related APIs.found in release: 2.2Found to occur in 2.2frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onr: fixedIssue is closed as already fixed in a newer versionteam-designOwned by Design Languages teamtriaged-designTriaged by Design Languages team

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions