Open lpylpyleo opened 4 years ago
Hy @lpylpyleo,
controller
for each view and you will not need to use StatefullWidget. The onInit method of the controller will be called only once. See an example:import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
runApp(GetMaterialApp(home: Home()));
}
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
final controller = Get.put(HomeController());
return Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: controller.tabController,
tabs: [
Tab(icon: Icon(Icons.directions_car)),
Tab(icon: Icon(Icons.directions_bike)),
],
),
title: Text('Tabs Demo'),
),
body: TabBarView(
controller: controller.tabController,
children: [
CarPage(),
BikePage(),
],
),
);
}
}
class HomeController extends GetxController with SingleGetTickerProviderMixin {
TabController tabController;
@override
void onInit() {
tabController = TabController(vsync: this, length: 2);
super.onInit();
}
}
class CarPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final controller = Get.put(CarPageController());
return Center(
child: Obx(() => Text(controller.car.value)),
);
}
}
class CarPageController extends GetxController {
final car = ''.obs;
@override
void onInit() {
print('Call API Car'); // called only once
car.value = 'Ferrari';
super.onInit();
}
}
class BikePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final controller = Get.put(BikePageController());
return Center(
child: Obx(() => Text(controller.bike.value)),
);
}
}
class BikePageController extends GetxController {
final bike = ''.obs;
@override
void onInit() {
print('Call API Bike'); // called only once
bike.value = 'BMC';
super.onInit();
}
}
Thanks for you sample code. @eduardoflorence
I edited your second TabView
's using a ListView
. When I switched to this tab, scroll the list, then switched to first tab, then back. The ListView
's position is reset to 0.0. Also, the build
is called every time I switched to this tab.
class BikePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
print('BikePage build'); // called every time
final controller = Get.put(BikePageController());
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
return Text(index.toString());
},
);
}
}
class BikePageController extends GetxController {
final bike = ''.obs;
@override
void onInit() {
print('Call API Bike'); // called only once
bike.value = 'BMC';
super.onInit();
}
}
As a comparison, the AutomaticKeepAliveClientMixin
version will automatically store the ListView
's position, as well as
other state like TextField
. Also, the build
method only fired once. So should I just use StatefulWidget
+ AutomaticKeepAliveClientMixin
instead of GetView
+ GetxController
in cases like this?
class BikePage1 extends StatefulWidget {
@override
_BikePage1State createState() => _BikePage1State();
}
class _BikePage1State extends State<BikePage1>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
print('BikePage1 build'); //called only once
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
return Text(index.toString());
},
);
}
@override
bool get wantKeepAlive => true;
}
You're right, AutomaticKeepAliveClientMixin does not execute the build method again on StatefullWidget. I will mark this issue as a feature request
I think this should not be on the controller side, since it changes how the build method behaves. The view is responsible for this. I think it is possible to create a class of its own for this, maybe it is even better than a mixin, as it does not need to overwrite the super.build, and because it is possible to add the getter to the controller. Let's implement this, I just don't know if it would be better to create it inside a GetX/Obx/GetBuilder (as a widget wrapper), or as an abstract class (like GetView/GetWiget)
Any update? I wanna keep state between two tab. How to?
@pqtrong17, use the example I provided above, but do what @lpylpyleo informed, replacing the StatelessWidget of each page with a Statefulwidget with AutomaticKeepAliveClientMixin
@eduardoflorence I use StatefulWidget and work for me but I wanna use GetView or GetWidget, any solution?
Hello, I think AutomaticKeepAliveClientMixin is useful. If you add it on GetxController,please notice me.Thanks
@jy6c9w08 If you made it, you will get the error:
'AutomaticKeepAliveClientMixin<StatefulWidget>' can't be mixed onto 'GetxController' because 'GetxController' doesn't implement 'State<StatefulWidget>'.
@pqtrong17 Thanks for your reply, I know your mean. So,how can I useing getx to keep page state?I want avoid to use StatefulWidget. I see you mark this issue as a feature request.If you realize this feature,Please notice me.Thsnks.
Any update, please? @eduardoflorence
Any update, please? @eduardoflorence
????
Any update, please? @eduardoflorence
????
I use StatefulWidget and work for me but I wanna use GetView or GetWidget, any solution?
@lpylpyleo an alternative to keep the Lisview off set position would be to use PageStorageKey() for every tabBarView or PageView. You can follow this example here.
This feature would be more than appreciated, I'm experiencing this issue myself as my App is nested with TabBars. Thanks again
@maares Thanks. Now I use a simple KeepAliveWrapper
as a work around.
TabBarView(
controller: controller.tabController,
children: const [
KeepAliveWrapper(child: CarPage()),
KeepAliveWrapper(child: BikePage()),
],
)
My KeepAliveWrapper
:
class KeepAliveWrapper extends StatefulWidget {
final Widget child;
const KeepAliveWrapper({Key key, this.child}) : super(key: key);
@override
_KeepAliveWrapperState createState() => _KeepAliveWrapperState();
}
class _KeepAliveWrapperState extends State<KeepAliveWrapper>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
return widget.child;
}
@override
bool get wantKeepAlive => true;
}
Anyone found a solution to keep the ScrollController level please ?
Anyone found a solution to keep the ScrollController level please ?
See above (Stateful)
I've been using a similar approach to the one provided by @lpylpyleo for a while now, but you should consider passing a closure that returns a Widget
instead of doing it directly. That way your widget won't be built until you go to this tab for the first time, which for me was what I wanted, because it was fetching some data from database. No need to fetch if user might not go there at all.
Any updates?
Any updates?
Any updates?
Any updates?
Update on this ?
I think this should be taken into consideration.
Nice!!!
Update
const KeepAliveWrapper({Key? key, required this.child}) : super(key: key);
Any updates on AutomaticKeepAliveClientMixin in getx ??
+1 for this feature :)
Thanks for you sample code. @eduardoflorence
I edited your second
TabView
's using aListView
. When I switched to this tab, scroll the list, then switched to first tab, then back. TheListView
's position is reset to 0.0. Also, thebuild
is called every time I switched to this tab.class BikePage extends StatelessWidget { @override Widget build(BuildContext context) { print('BikePage build'); // called every time final controller = Get.put(BikePageController()); return ListView.builder( itemBuilder: (BuildContext context, int index) { return Text(index.toString()); }, ); } } class BikePageController extends GetxController { final bike = ''.obs; @override void onInit() { print('Call API Bike'); // called only once bike.value = 'BMC'; super.onInit(); } }
As a comparison, the
AutomaticKeepAliveClientMixin
version will automatically store theListView
's position, as well as other state likeTextField
. Also, thebuild
method only fired once. So should I just useStatefulWidget
+AutomaticKeepAliveClientMixin
instead ofGetView
+GetxController
in cases like this?class BikePage1 extends StatefulWidget { @override _BikePage1State createState() => _BikePage1State(); } class _BikePage1State extends State<BikePage1> with AutomaticKeepAliveClientMixin { @override Widget build(BuildContext context) { super.build(context); print('BikePage1 build'); //called only once return ListView.builder( itemBuilder: (BuildContext context, int index) { return Text(index.toString()); }, ); } @override bool get wantKeepAlive => true; }
Update
Support for null security
class KeepAliveWrapper extends StatefulWidget {
final Widget child;
const KeepAliveWrapper(this.child, {super.key});
@override
State<KeepAliveWrapper> createState() => _KeepAliveWrapperState();
}
class _KeepAliveWrapperState extends State<KeepAliveWrapper>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
return widget.child;
}
@override
bool get wantKeepAlive => true;
}
Try this
import 'package:flutter/material.dart';
import 'package:get/get.dart';
abstract class GetViewKeepAlive<T> extends StatefulWidget {
const GetViewKeepAlive({super.key, this.tag});
@override
State<GetViewKeepAlive<T>> createState() => _GetViewKeepAliveState<T>();
@protected
Widget build(BuildContext context);
@protected
final String? tag;
@protected
T get controller => GetInstance().find<T>(tag: tag)!;
}
class _GetViewKeepAliveState<T> extends State<GetViewKeepAlive<T>> with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
return widget.build(context);
}
@override
bool get wantKeepAlive => true;
}
Change your existed view from
class RickRollView extends GetView<RickRollController>
to
class RickRollView extends GetViewKeepAlive<RickRollController>
Any updates ?
AutomaticKeepAliveClientMixin
is useful it comes toTabView
orPageView
.Tried to use
TextEditingController
andPageStorageKey
to keep user input andListView
's scroll position. But it will be more convinient if we haveAutomaticKeepAliveClientMixin
.I have noticed that there is a
SingleGetTickerProviderMixin
equivalent toSingleTickerProviderMixin
in Getx. So is it possible to make a mixin likeAutomaticKeepAliveClientMixin
as well or I just don't know the correct usage?Not sure if it is appropriate to use Feature request. Thank you advance.