Reinitialize Getx scrollController when navigate to same page - flutter

I use Get.offNamed to navigate to same page _ but this method called
#override
void onClose() {
scrollController.removeListener(_loadData);
super.onClose();
}
As a result I lose page scrolling How do I reset the scrollController
the Controller :
class PostsController extends GetxController with StateMixin<Posts> {
ScrollController scrollController = ScrollController();
#override
void onInit() {
_getData();
scrollController = ScrollController()..addListener(_loadMore);
super.onInit();
}
_getData() async {}
_loadMore() async {}
#override
void onClose() {
scrollController.removeListener(_loadMore);
super.onClose();
}
}
---I noticed Get.offNamed at first it reads the Controller of new page (in my case it's the same page)
[GETX] Instance "PostsController" has been created
[GETX] Instance "PostsController" has been initialized
Then deletes the controller of old page (in my case it's the same page)
[GETX] "PostsController" onDelete() called
[GETX] "PostsController" deleted from memory
Because of working in this order, the scrollController is lost. If it works the other way around the scrollController will be reinitialized.
I don't know how to go to the same page after deleting the controller (Firstly) and reinitializing it (secondly), or not calling onClose() when going to the same page as replacment

You can try initializing your scroll controller in the initstate like this:
class PostsController extends GetxController with StateMixin<Posts> {
late final ScrollController scrollController;
#override
void onInit() {
_getData();
scrollController = ScrollController()..addListener(_loadMore);
super.onInit();
}
_getData() async {}
_loadMore() async {}
#override
void onClose() {
scrollController.removeListener(_loadMore);
super.onClose();
}
}

Related

Keep scroll controller open with Get.offNamed

i'm using Getx as state management
class ProductsController extends GetxController with StateMixin<Posts> {
ScrollController scrollController = ScrollController();
#override
void onInit() {
_getData();
scrollController = ScrollController()..addListener(_loadMore);
super.onInit();
}
_getData() async {}
_loadMore() async {}
#override
void onClose() {
scrollController.removeListener(_loadMore);
super.onClose();
}
}
and everything works perfectly, but for some reasons i need to navigate to same page as replacement by
Get.offNamed(Routes.products, prevent Duplicates: false);
but i note that this void
#override
void onClose() {
scrollController.removeListener(_loadMore);
super.onClose();
}
called and i lost the scroll in the bage ...
so how to keep scrollController open or how to initialize it again when navigate to the same page as replacement
or am i need to use another method to navigate to same page as replacement to it ??
thanks

Flutter GetX call fetch data only once in the build methode

I have a method called getData() which used to load data from API,
and i am displaying data in a separate screen, I have one issue which whenever I navigate to this page it rebuild the whole screen and call the API again and again.
PS: I'm using getX Obx to control the UI
Question: how to call the function only when new data has added
class CategoryPageBinding extends Bindings {
#override
void dependencies() {
Get.put(CategoryPageController(), permanent: true);
}
}
class CategoryPageController extends GetxController {
#override
void onInit() {
super.onInit();
getAllCategories();
}
}
You can call the method in Controller using getX.
#override
void onInit() {
super.onInit();
getData();
}

GetxController onClose not called

I have a getx controllers A which has a property of another controller B. But when I delete the controller A, the onClose() of A is called while the onClose of B isn't.
// Controller A
#override
void onClose() {
print('close A');
B.dispose();
super.onClose();
}
//Controller B
#override
void onClose() {
print('close B');
animationController.dispose();
super.onClose();
}
until i add this line
#override
void onClose() {
B.onClose();
B.dispose();
super.onClose();
}
I'm newly interested in Getx, thanks for viewing this question:)
Is your controller B is being used by any other page/controller? If so, it's the expected behavior.
Also, you shouldn't call lifecycle methods (onInit, onClose) manually.

How do I close a stream when my Get Controller is deleted from memory?

I am using the following package https://pub.dev/packages/get .
I have the following controller
class GroupController extends GetController{
StreamController<GroupModel> _streamController = BehaviorSubject();
Stream<GroupModel> get stream => _streamController.stream;
GroupController(DatabaseService database, GroupModel group)
{
_streamController.addStream(database.groupStream(group));
}
#override
void dispose(){
print('dispose');
_streamController.close();
super.dispose();
}
}
But when I call it the dispose is never called. I call it like this
GetBuilder<GroupController>(
init: GroupController(database, _group),
builder: (GroupController groupController) => StreamBuilder(
stream: groupController.stream,
builder: (BuildContext context, AsyncSnapshot<GroupModel> groupSnapshot) {
I want my controller's dispose method being called whenever the controller is removed from memory.
I noticed that the GetBuilder has a dispose callback. And it requires the state from a widget. So do I need to make the widget that holds the controller statefull? Or do I need to pass a new statefull widget that holds the state? The documentation is not all to clear on it. How do I call the dispose on my controller?
I noticed there is a console log whenever a controller get's deleted from memory, isn't there a callback so I can close the stream there? I would really like to avoid making the widget statefull tho.
Edit
class GroupController extends GetController{
StreamController<GroupModel> _streamController = BehaviorSubject();
Stream<GroupModel> get stream => _streamController.stream;
GroupController(DatabaseService database, GroupModel group)
{
_streamController.addStream(database.groupStream(group));
}
#override
void close(){
print('log if close is invoked');
_streamController.close();
super.close();
}
}
Generate the following log
I/flutter (23404): log if close is invoked
I/flutter (23404): Close can't be called
I/flutter (23404): [GET] GroupController deleted from memory
Somehow it error when trying to close the controller
If you insert a 'dispose' into your GetController, it will be ignored. This is because disposing is a method for discarding widgets in a StatefulWidget class, not for discarding controllers, not least because Get automatically and intelligently discards controllers to free resources when it is no longer needed. If you want to close streams, Get does it automatically, as long as you insert your streams into the onClose method.
class GroupController extends GetxController {
StreamController<GroupModel> _streamController = BehaviorSubject();
Stream<GroupModel> get stream => _streamController.stream;
GroupController(DatabaseService database, GroupModel group);
#override
void onInit() {
_streamController.addStream(database.groupStream(group));
super.onInit();
}
// this
#override
void onClose() {
print('dispose');
_streamController.close();
super.onClose();
}
}
If you are using an old version of Get, you can use the same close and close method manually:
GetBuilder<GroupController>(
init: GroupController(database, _group),
builder: (GroupController groupController) => StreamBuilder(
stream: groupController.stream,
builder: (BuildContext context, AsyncSnapshot<GroupModel> groupSnapshot) {
This is probably how you want to be doing it. Not adding the stream in the contructor because this causes problems when the controller get's rebuild. Because it will add the stream to the contructor once more.
import 'dart:async';
import 'package:get/get.dart';
import 'package:rxdart/rxdart.dart';
class GetStreamController<T> extends GetController {
final Stream<T> Function() value;
StreamController<T> _streamController = BehaviorSubject();
StreamSubscription _streamSubscription;
Stream<T> get stream => _streamController.stream;
GetStreamController(this.value);
#override
void onInit()
{
super.onInit();
_streamSubscription = value().listen((event) {
_streamController.add(event);
});
}
#override
void onClose()
{
super.onClose();
_streamSubscription.cancel();
_streamController.close();
}
}

Flutter custom navigation highlight selected page

I'm currently building a custom bottom bar for quick navigation.
I used the Navigation Service described in this article
Now I want to add highlighting based on which page the user has selected.
I tried to add RouteAware to my BottomNav widget to update the menu when the routing changed but I'm not receiving any events only when starting my app.
class _BottomNavState extends State<BottomNav> with RouteAware {
String _selectedRoute;
AppRouteObserver _routeObserver;
#override
void initState() {
super.initState();
_routeObserver = AppRouteObserver();
}
#override
void didChangeDependencies() {
super.didChangeDependencies();
_routeObserver.subscribe(this, ModalRoute.of(context));
}
#override
void dispose() {
_routeObserver.unsubscribe(this);
super.dispose();
}
#override
void didPush() {
print('didPush');
}
The Route observer is a simple class:
class AppRouteObserver extends RouteObserver<PageRoute> {
factory AppRouteObserver() => _instance;
AppRouteObserver._private();
static final AppRouteObserver _instance = AppRouteObserver._private();
}
I'm guessing that it has to with me not using the Navigator.pushNamed but the direct implementation of the Navigation Service.
class NavigationService {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
Future<dynamic> navigateTo(String routeName, {var content}) {
return navigatorKey.currentState.pushNamed(routeName, arguments: content);
}
bool goBack() {
return navigatorKey.currentState.pop();
}
}
The reason I created the NavigationService is because I want to show a consistent layout on every page (menubar / bottom bar / background).
Is there a better way to solve this problem?
I fixed the issue by extending the NavigationService with a ChangeNotifier.
Now when the user clicks the button I call a setSelected function and notify the menu items to redraw them self's.
setSelected(String newRoute) {
_showLeading = !checkIsHome(newRoute);
this._currentRoute = newRoute;
notifyListeners();
}