Search code examples
flutterflutter-layouttabviewflutter-get

How to build a TabView using Flutter GetX


I'm trying to build a app using Flutter with GetX for State Management, and in one of my pages i need to build a TabView widget in the middle of the page, i already found a lot of stuff explaining how to build a TabView as widget, as this post and this article, but all of these extends a State controller with a SingleTickerProviderStateMixin.

As i understand reading the documentation, i'm not supposed to use StatefulWidgets and States like that, but i cant figure out how to elaborate a solution using the GetX architecture.

I already tried in my page things like:

class CourseDetailPage extends StatelessWidget {
  final TabController _tabController = Get.put(TabController(vsync: null, length: 2));
}

and

class CourseDetailPage extends StatelessWidget {
  final TabController _tabController = TabController(vsync: null, length: 2);
}  

But the VSYNC argument for the TabController cannot be null, and i don't figure out how i cant obtain a TickerProvider to populate it.


Solution

  • Would the following be a solution to using a GetX controller to control a TabView?

    GetTicker

    There's a Get version of SingleTickerProviderMixin which implements the TickerProvider interface (using the same Ticker class from Flutter SDK).

    It has a catchy name: GetSingleTickerProviderStateMixin

    (Updated 21-12-20: SingleGetTickerProviderMixin has been deprecated with latest GetX. Thanks to commenter pointing this out.)

    The below example is basically from the Flutter docs for TabController.

    From the linked example's StatefulWidget I transplanted its contents into a GetxController (MyTabController) and added mixin of SingleGetTickerProviderMixin:

    class MyTabController extends GetxController with GetSingleTickerProviderStateMixin {
      final List<Tab> myTabs = <Tab>[
        Tab(text: 'LEFT'),
        Tab(text: 'RIGHT'),
      ];
    
      late TabController controller;
    
      @override
      void onInit() {
        super.onInit();
        controller = TabController(vsync: this, length: myTabs.length);
      }
    
      @override
      void onClose() {
        controller.dispose();
        super.onClose();
      }
    }
    

    To use MyTabController inside a Stateless widget:

    class MyTabbedWidget extends StatelessWidget {
      const MyTabbedWidget();
    
      @override
      Widget build(BuildContext context) {
        final MyTabController _tabx = Get.put(MyTabController());
        // ↑ init tab controller
    
        return Scaffold(
          appBar: AppBar(
            bottom: TabBar(
              controller: _tabx.controller,
              tabs: _tabx.myTabs,
            ),
          ),
          body: TabBarView(
            controller: _tabx.controller,
            children: _tabx.myTabs.map((Tab tab) {
              final String label = tab.text!;
              return Center(
                child: Text(
                  'This is the $label tab',
                  style: const TextStyle(fontSize: 36),
                ),
              );
            }).toList(),
          ),
        );
      }
    }
    
    

    Here's the rest of the app example to just copy & paste into Android Studio / VisualStudio Code to run:

    import 'package:flutter/material.dart';
    import 'package:get/get.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
          ),
          home: HomePage(),
        );
      }
    }
    
    class HomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('GetX Tab Example'),
          ),
          body: Column(
            children: [
              Expanded(
                flex: 1,
                child: Container(
                  alignment: Alignment.center,
                  child: Text('Some random stuff'),
                ),
              ),
              Expanded(
                flex: 4,
                child: MyTabbedWidget(),
              )
            ],
          ),
        );
      }
    }
    
    class MyTabController extends GetxController with GetSingleTickerProviderStateMixin {
      final List<Tab> myTabs = <Tab>[
        Tab(text: 'LEFT'),
        Tab(text: 'RIGHT'),
      ];
    
      late TabController controller;
    
      @override
      void onInit() {
        super.onInit();
        controller = TabController(vsync: this, length: myTabs.length);
      }
    
      @override
      void onClose() {
        controller.dispose();
        super.onClose();
      }
    }
    
    class MyTabbedWidget extends StatelessWidget {
      const MyTabbedWidget();
    
      @override
      Widget build(BuildContext context) {
        final MyTabController _tabx = Get.put(MyTabController());
        // ↑ init tab controller
    
        return Scaffold(
          appBar: AppBar(
            bottom: TabBar(
              controller: _tabx.controller,
              tabs: _tabx.myTabs,
            ),
          ),
          body: TabBarView(
            controller: _tabx.controller,
            children: _tabx.myTabs.map((Tab tab) {
              final String label = tab.text!;
              return Center(
                child: Text(
                  'This is the $label tab',
                  style: const TextStyle(fontSize: 36),
                ),
              );
            }).toList(),
          ),
        );
      }
    }