如何使用 Flutter GetX 构建 TabView

问题描述 投票:0回答:4

我正在尝试使用 Flutter 和 GetX 来构建一个应用程序进行状态管理,在我的一个页面中,我需要在页面中间构建一个 TabView 小部件,我已经找到了很多解释如何构建的内容TabView 作为小部件,如这篇文章这篇文章,但所有这些都使用SingleTickerProviderStateMixin扩展了State控制器。

据我了解阅读文档,我不应该使用 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 参数不能为空,而且我不知道如何无法获取 TickerProvider 来填充它。

flutter flutter-layout tabview flutter-get
4个回答
60
投票

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

获取股票代码

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

它有一个朗朗上口的名字:

GetSingleTickerProviderStateMixin

(2020 年 12 月 21 日更新:

SingleGetTickerProviderMixin
已被最新的
GetX
弃用。感谢评论者指出这一点。)

下面的示例基本上来自于 TabController 的 Flutter 文档

从链接示例的 StatefulWidget 中,我将其内容移植到 GetxController (

MyTabController
) 中,并添加了
SingleGetTickerProviderMixin
:

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

要在无状态小部件中使用

MyTabController

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

以下是应用程序示例的其余部分,只需复制并粘贴到 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 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(),
      ),
    );
  }
}

0
投票

正如您在所有文章中所说,它们用

State
扩展
SingleTickerProviderStateMixin
,因为您无法在
TabController
之外将
State
初始化为
TabController
管理状态 (doc)。

解决方法是不为控制器使用变量,并将

TabBar
TabView
小部件树包装在
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
中,但也不会使用
GetX


0
投票

我遇到了确切的问题,它表明 GetX 框架尚未准备好应对许多可能的场景,您应该谨慎使用它。

我想出的解决方案是包装器小部件。在建议的示例中,我使用了

HookWidget
,但您也可以自由使用 Stateful 小部件。

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 的帮助下处理控制器。如果您使用 Stateful widget,则必须正确处置它。


0
投票

To implement the functionality where clicking the

要实现单击“结束通话”按钮的功能,我需要在 flutter 应用程序中返回响应,而不是单击任何其他事件

© www.soinside.com 2019 - 2024. All rights reserved.