如何使用Flutter GetX构建TabView

时间:2020-09-16 16:39:00

标签: flutter flutter-layout tabview flutter-get

我正在尝试使用带有GetX的Flutter进行状态管理,并且在我的一个页面中,我需要在页面中间构建一个TabView小部件,我已经发现了很多东西解释了如何以this postthis article的形式构建TabView作为小部件,但是所有这些都扩展了 State 控制器和 SingleTickerProviderStateMixin 。 >

据我了解阅读文档,我不应该使用StatefulWidgets和类似的状态,但是我无法弄清楚如何使用GetX架构来完善解决方案。

我已经在页面中尝试过类似的事情:

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

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

但是TabController的VSYNC参数不能为null,而且我不知道如何无法获取TickerProvider来填充它。

3 个答案:

答案 0 :(得分:0)

正如您在所有文章中所说,它们将State扩展为SingleTickerProviderStateMixin,因为您将无法在TabController之外将State初始化为{{1} } 管理状态doc)。

一种解决方法是不对控制器使用变量,而将TabControllerTabBar小部件树包装在TabView中。

以下是official doc中的示例:

DefaultTabController

这样做,您无需将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(), ), ), ); } } 放在TabView内,但是您也不会使用State

答案 1 :(得分:0)

我碰到了确切的问题,它表明GetX框架尚未针对许多可能的情况准备就绪,您应该谨慎使用它。

我想出的解决方案是包装小部件。在建议的示例中,我使用了HookWidget,但是您也可以自由使用有状态的小部件。

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);
  }
}

如您所见,有一个Rx变量控制tabView的状态。因此,您必须从外部传递RxInt,并且每当其值更改时,tabView都会分别更新。

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,
             ...,
          ],
        ),
      ),
    );
  }

现在,我们借助HookWidget来照顾控制器。如果您使用的是有状态小部件,则必须正确处理它。

答案 2 :(得分:0)

以下是使用GetX控制器控制TabView的解决方案吗?

GetTicker

有一个实现了TickerProvider接口的SingleTickerProviderMixin的Get版本(使用Flutter SDK中的同一Ticker类)。

它的名称很吸引人: SingleGetTickerProviderMixin

下面的示例基本上来自Flutter docs for TabController

从链接的示例的StatefulWidget中,将其内容转换为GetxController(MyTabController)并添加SingleGetTickerProviderMixin的mixin:

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();
  }
}

要在无状态小部件中使用MyTabController,请执行以下操作:

class MyTabbedWidget extends StatelessWidget {
  final MyTabController _tabx = Get.put(MyTabController());

  @override
  Widget build(BuildContext context) {
    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(),
      ),
    );
  }
}

以下是该应用程序示例的其余部分,只需将其复制并粘贴到Android Studio / VisualStudio代码中即可运行:

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 {
  final MyTabController _tabx = Get.put(MyTabController());

  @override
  Widget build(BuildContext context) {
    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(),
      ),
    );
  }
}