So erstellen Sie eine TabView mit Flutter GetX

Sep 17 2020

Ich versuche, eine App mit Flutter mit GetX for State Management zu erstellen , und auf einer meiner Seiten muss ich ein TabView-Widget in der Mitte der Seite erstellen. Ich habe bereits viele Dinge gefunden, die erklären, wie man ein TabView als erstellt Widget, wie dieser Beitrag und dieser Artikel , aber alle diese erweitern einen State Controller mit einem SingleTickerProviderStateMixin .

Da ich das Lesen der Dokumentation verstehe, sollte ich StatefulWidgets und States nicht so verwenden, aber ich kann nicht herausfinden, wie man eine Lösung unter Verwendung der GetX-Architektur ausarbeitet.

Ich habe auf meiner Seite bereits Dinge ausprobiert wie:

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

und

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

Aber das VSYNC-Argument für den TabController kann nicht null sein, und ich finde nicht heraus, wie ich keinen TickerProvider zum Auffüllen erhalten kann.

Antworten

1 Baker Dec 02 2020 at 12:07

Wäre das Folgende eine Lösung für die Verwendung eines GetX-Controllers zur Steuerung eines TabView?

GetTicker

Es gibt eine Get-Version von SingleTickerProviderMixin, die die TickerProvider-Schnittstelle implementiert (unter Verwendung derselben Ticker-Klasse aus dem Flutter SDK).

Es hat einen eingängigen Namen: SingleGetTickerProviderMixin

Das folgende Beispiel stammt im Wesentlichen aus den Flutter-Dokumenten für TabController .

Aus dem StatefulWidget des verknüpften Beispiels habe ich seinen Inhalt in einen GetxController ( MyTabController) übersetzt und eine Mischung aus SingleGetTickerProviderMixin:

class MyTabController extends GetxController with SingleGetTickerProviderMixin {
  final List<Tab> myTabs = <Tab>[
    Tab(text: 'LEFT'),
    Tab(text: 'RIGHT'),
  ];

  TabController controller;

  @override
  void onInit() {
    super.onInit();
    controller = TabController(vsync: this, length: myTabs.length);
  }

  @override
  void onClose() {
    controller.dispose();
    super.onClose();
  }
}

So verwenden Sie es MyTabControllerin einem zustandslosen Widget:

class MyTabbedWidget extends StatelessWidget {
  @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.toLowerCase();
          return Center(
            child: Text(
              'This is the $label tab',
              style: const TextStyle(fontSize: 36),
            ),
          );
        }).toList(),
      ),
    );
  }
}

Hier ist der Rest des App-Beispiels zum einfachen Ausführen und Einfügen in Android Studio / VisualStudio-Code:

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 SingleGetTickerProviderMixin {
  final List<Tab> myTabs = <Tab>[
    Tab(text: 'LEFT'),
    Tab(text: 'RIGHT'),
  ];

  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 {
  @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.toLowerCase();
          return Center(
            child: Text(
              'This is the $label tab',
              style: const TextStyle(fontSize: 36),
            ),
          );
        }).toList(),
      ),
    );
  }
}
GuillaumeRoux Sep 17 2020 at 15:54

Wie Sie in allen Artikeln sagten , sie erweitern Statemit , SingleTickerProviderStateMixinweil Sie zu initialisieren der Lage sein , Ihre nicht TabControlleraußerhalb eines Stateals TabController einen Zustand verwalten ( doc ).

Eine Abhilfe wäre nicht eine Variable für Ihren Controller zu verwenden und Ihre wickeln TabBarund TabViewWidget Baum im Inneren ein DefaultTabController.

Hier ist ein Beispiel aus dem offiziellen Dokument :

class MyDemo extends StatelessWidget {
  final List<Tab> myTabs = <Tab>[
    Tab(text: 'LEFT'),
    Tab(text: 'RIGHT'),
  ];

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: myTabs.length,
      child: Scaffold(
        appBar: AppBar(
          bottom: TabBar(
            tabs: myTabs,
          ),
        ),
        body: TabBarView(
          children: myTabs.map((Tab tab) {
            final String label = tab.text.toLowerCase();
            return Center(
              child: Text(
                'This is the $label tab',
                style: const TextStyle(fontSize: 36),
              ),
            );
          }).toList(),
        ),
      ),
    );
  }
}

Auf diese Weise müssen Sie Ihr TabViewInneres nicht haben, Stateaber Sie werden es auch nicht verwenden GetX.

HamedHamedi Nov 24 2020 at 21:16

Ich bin auf das genaue Problem gestoßen und es zeigt, dass das GetX-Framework nicht für viele mögliche Szenarien bereit ist und Sie es vorsichtig verwenden sollten.

Die Lösung, die ich gefunden habe, ist ein Wrapper-Widget. Im vorgeschlagenen Beispiel habe ich verwendet, HookWidgetaber Sie können auch das Stateful-Widget verwenden.

class TabWidget extends HookWidget {
  final List<Widget> children;
  final RxInt currentTab;
  final int initialIndex;

  TabWidget({
    @required this.children,
    @required this.currentTab,
    @required this.initialIndex,
  });

  @override
  Widget build(BuildContext context) {
    final controller = useTabController(
      initialLength: children.length,
      initialIndex: initialIndex,
    );
    currentTab.listen((page) {
      controller.animateTo(page);
    });
    return TabBarView(controller: controller, children: children);
  }
}

Wie Sie sehen können, gibt es eine Rx-Variable, die den Status der tabView steuert. Sie müssen also ein RxInt von außen übergeben, und wenn sich der Wert ändert, wird die tabView jeweils aktualisiert.

class TestView extends GetView<TestController> {
  @override
  Widget build(BuildContext context) {
    final screen = 0.obs;
    return SafeArea(
      child: Scaffold(
        appBar: AppBar(
          actions: [
            IconButton(
              icon: Icon(Icons.forward),
              onPressed: () => screen.value = screen.value + 1,
            )
          ],
        ),
        body: TabWidget(
          initialIndex: 1,
          currentTab: screen,
          children: [
             child1,
             child2,
             child3,
             ...,
          ],
        ),
      ),
    );
  }

Im Moment kümmern wir uns mit Hilfe von HookWidget um den Controller. Wenn Sie das Stateful-Widget verwenden, müssen Sie es ordnungsgemäß entsorgen.