Search code examples
flutterdartsetstatestate-management

Call a method from one statefulWidget in tab page in Flutter


I have two widgets - one is the main page with tabs and buttons and the second one is - Page with some TextFields. I would like to clean all TextFields after the button click. I tried to use GlobalKey<_Page1> _key = GlobalKey<_Page1>(); and _key.currentState!.CleanAll(); but I received an error - Unhandled Exception: Null check operator used on a null value

Do you have any other ideas on how to do this?

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

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

class _MyTabsState extends State<MyTabs> {

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        DefaultTabController(
          length: 1, // length of tabs
          initialIndex: 0,
          child: Column(
            children: <Widget>[
              const TabBar(
                labelColor: Colors.green,
                unselectedLabelColor: Colors.blueGrey,
                tabs: [
                  Tab(text: 'Page 1'),
                ],
              ),
              SizedBox(
                height: 200,
                child: TabBarView(
                    children: <Widget>[
                      Page1(),
                    ]
                ),
              ),
            ],
          ),),

        ElevatedButton(
            onPressed: () {
              // <- clean all TextField in Page1
            },
            child: Text('Clean')
        ),
      ],
    );
  }
}

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

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

class _Page1State extends State<Page1> {

  TextEditingController text1 = TextEditingController();
  TextEditingController text2 = TextEditingController();

  void CleanAll() {
    text1.clear();
    text2.clear();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
      TextField(
        controller: text1,
      ),
        TextField(
          controller: text2,
        ),
      ],
    );
  }
}

Solution

  • You can use a ValueNotifier that will listen on Page1. then we will override initState on Page1 and clear the controlles.

    class _MyTabsState extends State<MyTabs> {
      final ValueNotifier<bool> clearNotifier = ValueNotifier(false);
    

    pass it to the Page1

    Page1(
      clearCallback: clearNotifier,
    ),
    

    to notify the listener we are just switching the value. bool value does not matter, we just need to update it.

    onPressed: () {
      clearNotifier.value = !clearNotifier.value;
    },
    

    and on Page1

    class Page1 extends StatefulWidget {
      final ValueNotifier clearCallback;
      const Page1({Key? key, required this.clearCallback}) : super(key: key);
    
      @override
      _Page1State createState() => _Page1State();
    }
    
    class _Page1State extends State<Page1> {
      @override
      void initState() {
        super.initState();
        widget.clearCallback.addListener(() {
          CleanAll();
        });
      }
    

    Test snippet

    class MyTabs extends StatefulWidget {
      const MyTabs({Key? key}) : super(key: key);
    
      @override
      _MyTabsState createState() => _MyTabsState();
    }
    
    class _MyTabsState extends State<MyTabs> {
      final ValueNotifier<bool> clearNotifier = ValueNotifier(false);
      @override
      Widget build(BuildContext context) {
        return Column(
          children: [
            DefaultTabController(
              length: 1, // length of tabs
              initialIndex: 0,
              child: Column(
                children: <Widget>[
                  const TabBar(
                    labelColor: Colors.green,
                    unselectedLabelColor: Colors.blueGrey,
                    tabs: [
                      Tab(text: 'Page 1'),
                    ],
                  ),
                  SizedBox(
                    height: 200,
                    child: TabBarView(children: <Widget>[
                      Page1(
                        clearCallback: clearNotifier,
                      ),
                    ]),
                  ),
                ],
              ),
            ),
            ElevatedButton(
                onPressed: () {
                  clearNotifier.value = !clearNotifier.value;
                },
                child: Text('Clean')),
          ],
        );
      }
    }
    
    class Page1 extends StatefulWidget {
      final ValueNotifier clearCallback;
      const Page1({Key? key, required this.clearCallback}) : super(key: key);
    
      @override
      _Page1State createState() => _Page1State();
    }
    
    class _Page1State extends State<Page1> {
      @override
      void initState() {
        super.initState();
        widget.clearCallback.addListener(() {
          CleanAll();
        });
      }
    
      TextEditingController text1 = TextEditingController();
      TextEditingController text2 = TextEditingController();
    
      void CleanAll() {
        text1.clear();
        text2.clear();
      }
    
      @override
      Widget build(BuildContext context) {
        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(
              controller: text1,
            ),
            TextField(
              controller: text2,
            ),
          ],
        );
      }
    }