Error : NoSuchMethodError, The getter 'index' was called on null

Viewed 1218

I'm using an AnimatedContainer so that its height can adapt (with an animation) to the Tab's content present in it. I'm supposed to have access to the current index of the Tab with DefaultTabController.of(context).index and I'm transmitting this data to a function that will rebuild the widget depending on the current tab.

When I run the code, it displays me the error, but I don't understand : does it mean that DefaultTabController returns null ?

Here's the code :

import 'package:flutter/material.dart';
import 'package:travel_agent_app/loginForm.dart';
import 'package:travel_agent_app/registerForm.dart';
import 'package:travel_agent_app/bubble_tab_indicator.dart';

class Login extends StatefulWidget {
  const Login({Key key}) : super(key: key);
  _LoginState createState() => _LoginState();

}


class _LoginState extends State<Login> {

  double _containerHeight = 300;



  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
        length: 2,
        child: Scaffold(
            body: Container(
                child: Padding(
                  padding: const EdgeInsets.all(10.0),
                  child: Column(
                    children: <Widget>[
                      SizedBox(
                        height: 150,
                      ),
                      Container(
                        padding: EdgeInsets.only(left: 20.0, right:20.0),
                        child: Column(
                          children: <Widget>[
                            Container(
                              height: 52.0,
                              decoration: BoxDecoration(
                                color: Colors.grey[200],
                                borderRadius: BorderRadius.all(Radius.circular(100.0))
                              ),
                              child: TabBar(
                                indicatorSize: TabBarIndicatorSize.tab,
                                indicator: new BubbleTabIndicator(
                                    indicatorHeight: 45.0,
                                    indicatorColor: Colors.blueAccent,
                                    tabBarIndicatorSize: TabBarIndicatorSize.tab),
                                labelColor: Colors.white,
                                unselectedLabelColor: Colors.black,
                                onTap: setAnimatedContainerHeight(DefaultTabController.of(context).index),
                                tabs: <Widget>[
                                  Tab(text: 'Login'),
                                  Tab(text: 'Register'),
                                ],
                              ),
                            ),
                            SizedBox(
                              height: 20.0,
                            ),
                            AnimatedContainer(
                                duration: Duration(seconds: 1),
                                padding: EdgeInsets.all(40.0),
                                width: double.infinity,
                                height: _containerHeight,
                                decoration: BoxDecoration(
                                    color: Colors.white,
                                    borderRadius: BorderRadius.circular(8.0),
                                    boxShadow: [
                                      BoxShadow(
                                          color: Colors.black12,
                                          offset: Offset(0.0, 15.0),
                                          blurRadius: 15.0),
                                    ]),
                                child: TabBarView(
                                  children: <Widget>[
                                    Container(
                                      width: 500.0,
                                      child: LoginForm(),
                                    ),
                                    RegisterForm(),
                                  ],
                                )
                            ),
                          ],
                        ),
                      )
                    ],
                  ),
                )
              )
            )
          );
  }

  setAnimatedContainerHeight(int index){
    if(index == 0){
      setState(() {
        _containerHeight = 300;  
      });
    }
    else{
      setState(() {
        _containerHeight = 450;  
      });
    }

  }
}
2 Answers

The issue here is the DefaultTabController context is the same as in which DefaultTabController.of(context).index is defined.

To solve the error - you need to define DefaultTabController in the parent context.

Your code working:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: DefaultTabController(    // Add here
        child: Login(),
        length: 2,
      ),
    );
  }
}

class Login extends StatefulWidget {
  const Login({Key key}) : super(key: key);
  _LoginState createState() => _LoginState();
}

class _LoginState extends State<Login> {
  double _containerHeight = 300;

  @override
  Widget build(BuildContext context) {
    return Scaffold(                      // Remove from here
        body: Container(
            child: Padding(
      padding: const EdgeInsets.all(10.0),
      child: Column(
        children: <Widget>[
          SizedBox(
            height: 150,
          ),
          Container(
            padding: EdgeInsets.only(left: 20.0, right: 20.0),
            child: Column(
              children: <Widget>[
                Container(
                  height: 52.0,
                  decoration: BoxDecoration(
                      color: Colors.grey[200],
                      borderRadius: BorderRadius.all(Radius.circular(100.0))),
                  child: TabBar(
                    indicatorSize: TabBarIndicatorSize.tab,
                    indicator: new BubbleTabIndicator(
                        indicatorHeight: 45.0,
                        indicatorColor: Colors.blueAccent,
                        tabBarIndicatorSize: TabBarIndicatorSize.tab),
                    labelColor: Colors.white,
                    unselectedLabelColor: Colors.black,
                    onTap: setAnimatedContainerHeight(
                        DefaultTabController.of(context).index),
                    tabs: <Widget>[
                      Tab(text: 'Login'),
                      Tab(text: 'Register'),
                    ],
                  ),
                ),
                SizedBox(
                  height: 20.0,
                ),
                AnimatedContainer(
                    duration: Duration(seconds: 1),
                    padding: EdgeInsets.all(40.0),
                    width: double.infinity,
                    height: _containerHeight,
                    decoration: BoxDecoration(
                        color: Colors.white,
                        borderRadius: BorderRadius.circular(8.0),
                        boxShadow: [
                          BoxShadow(
                              color: Colors.black12,
                              offset: Offset(0.0, 15.0),
                              blurRadius: 15.0),
                        ]),
                    child: TabBarView(
                      children: <Widget>[
                        Container(
                          width: 500.0,
                          child: LoginForm(),
                        ),
                        RegisterForm(),
                      ],
                    )),
              ],
            ),
          )
        ],
      ),
    )));
  }

  setAnimatedContainerHeight(int index) {
    if (index == 0) {
      setState(() {
        _containerHeight = 300;
      });
    } else {
      setState(() {
        _containerHeight = 450;
      });
    }
  }
}

Or using FutureBuilder:

import 'package:flutter/material.dart';
import 'package:travel_agent_app/loginForm.dart';
import 'package:travel_agent_app/registerForm.dart';
import 'package:travel_agent_app/bubble_tab_indicator.dart';

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

  _LoginState createState() => _LoginState();
}

class _LoginState extends State<Login> {
  double _containerHeight = 300;

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 2,
      child: FutureBuilder(
        future: Future.delayed(Duration.zero),
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          return Scaffold(
            // ...
            // The context within here is from FutureBuilder, which include DefaultTabController
            // ...
          );
        },
      ),
    );
  }

  setAnimatedContainerHeight(int index) {
    if (index == 0) {
      setState(() {
        _containerHeight = 300;
      });
    } else {
      setState(() {
        _containerHeight = 450;
      });
    }
  }
}
Related