Как создать TabView с помощью Flutter GetX
Я пытаюсь создать приложение, используя Flutter с GetX для управления состоянием, и на одной из моих страниц мне нужно создать виджет TabView в середине страницы, я уже нашел много вещей, объясняющих, как создать TabView как виджет, так как этот пост и эта статья, но все это расширяет государственный контроллер с 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);
}
Но аргумент VSYNC для TabController не может быть нулевым, и я не понимаю, как я не могу получить TickerProvider для его заполнения.
3 ответа
Будет ли следующее решение использования контроллера GetX для управления TabView?
GetTicker
Существует версия Get SingleTickerProviderMixin, которая реализует интерфейс TickerProvider (с использованием того же класса Ticker из Flutter SDK).
У него запоминающееся название:SingleGetTickerProviderMixin
Приведенный ниже пример в основном взят из документации Flutter для TabController.
Из связанного примера StatefulWidget я перенес его содержимое в GetxController (MyTabController
) и добавил миксин
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();
}
}
Использовать
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(),
),
);
}
}
Я наткнулся на точную проблему, и она показывает, что платформа 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. Если вы используете виджет с отслеживанием состояния, вы должны правильно его удалить.
Как вы сказали во всех статьях, они расширяют
State
с участием
SingleTickerProviderStateMixin
потому что вы не сможете инициализировать свой
TabController
вне
State
в виде
TabController
управлять государством (док).
Обходной путь - не использовать переменную для вашего контроллера и обернуть ваш
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
или.