Flutter GetX Re-Initialise GetX Controller Reset GetX Controller, Reset GetX Controller Values
Asked Answered
C

7

6

I am learning Flutter GetX to my own and stuck on a point. Actually I want to know why onInit method of GetX Controlled is not calling whenever I revisit that page/dialog again.

Suppose that I have dialog with a simple TextField, a Listview the TextField is used for searching the listview. When the User enters any filter key inside the text field, the listview will be filtered.

Here is the Sample Dialog:

import 'package:flutter/material.dart';
import 'package:flutter_base_sample/util/apptheme/colors/app_colors.dart';
import 'package:flutter_base_sample/util/apptheme/styles/text_styles_util.dart';
import 'package:flutter_base_sample/util/commons/app_util.dart';
import 'package:flutter_base_sample/util/widgets/alert/controllers/country_finder_alert_controller.dart';
import 'package:flutter_base_sample/util/widgets/marquee/marquee_widget.dart';
import 'package:flutter_base_sample/util/widgets/textfields/app_text_field.dart';
import 'package:get/get.dart';

class SampleDialogWidget extends StatelessWidget {
  final CountryFinderAlertController controller = Get.put(CountryFinderAlertController(),permanent: true);

  @override
  Widget build(BuildContext context) {
    return Dialog(
      insetPadding: AppUtil.dialogPadding(context),
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(20),
      ),
      elevation: 0.0,
      backgroundColor: Colors.white,
      child: dialogContent(context),
    );
  }

  Widget dialogContent(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      mainAxisAlignment: MainAxisAlignment.start,
      mainAxisSize: MainAxisSize.max,
      children: [
        Text(
          "Hello Heading",
          style: TextStyleUtil.quickSandBold(context, fontSize: 16, color: Colors.blue),
          textAlign: TextAlign.center,
        ),
        SizedBox(
          height: 20,
        ),
        Expanded(
          child: SingleChildScrollView(
            child: Container(
              height: AppUtil.deviceHeight(context),
              padding: EdgeInsetsDirectional.all(20),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.stretch,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text("Hello Text1"),
                  SizedBox(
                    height: 10,
                  ),
                  getSearchField(context),
                  SizedBox(
                    height: 5,
                  ),
                  Expanded(
                    child: Obx(()=> getFavoritesListView(context)),
                  )
                ],
              ),
            ),
          ),
        ),
        SizedBox(
          height: 20,
        ),
        Container(
          margin: EdgeInsetsDirectional.only(start: 20,end: 20),
          child: ElevatedButton(
            onPressed: () {},
            style: ButtonStyle(
              overlayColor: MaterialStateProperty.all<Color>(Colors.red),
              // splashFactory: NoSplash.splashFactory,
              elevation: MaterialStateProperty.all(0.5),
              backgroundColor: MaterialStateProperty.resolveWith<Color>(
                (Set<MaterialState> states) {
                  if (states.contains(MaterialState.pressed)) {
                    return AppColors.instance.black.withOpacity(0.1);
                  } else {
                    return Colors.blue; // Use the component's default.
                  }
                },
              ),
            ),
            child: Text(
              "Hello Footer",
              style: TextStyleUtil.quickSandBold(context, fontSize: 16, color: Colors.yellow),
              textAlign: TextAlign.center,
            ),
          ),
        )
      ],
    );
  }

  Widget getFavoritesListView(BuildContext context) {
    if (controller.favoritesList.length > 0) {
      return ListView.separated(
        shrinkWrap: true,
        itemCount: controller.favoritesList.length,
        itemBuilder: (BuildContext context, int index) => _topupFavoriteContent(context, index),
        separatorBuilder: (context, index) {
          return Divider(
            indent: 15,
            endIndent: 15,
          );
        },
      );
    } else {
      return Center(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          mainAxisAlignment: MainAxisAlignment.center,
          mainAxisSize: MainAxisSize.min,
          children: [
            Text(
              "No Data Found!",
              textAlign: TextAlign.center,
            ),
            SizedBox(
              height: 20,
            ),
          ],
        ),
      );
    }
  }

  Widget _topupFavoriteContent(BuildContext context, int index) {
    final item = controller.favoritesList[index];
    return InkWell(
        onTap: () {
          Get.back(result:item);
          // AppUtil.pop(context: context, valueToReturn: item);
        },
        child: getChildItems(context, index));
  }

  Widget getChildItems(BuildContext context, int index) {
    return Directionality(textDirection: TextDirection.ltr, child: getContactNumberAndNameHolder(context, index));
  }

  Widget getContactNumberAndNameHolder(BuildContext context, int index) {
    final item = controller.favoritesList[index];
    return Container(
      padding: EdgeInsetsDirectional.only(start: 20, end: 20, top: 20, bottom: 10),
      child: Column(
        children: [
          Row(
            // crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Flexible(
                child: Align(
                  alignment: AlignmentDirectional.centerStart,
                  child: Text(
                    item.name ?? "",
                    style: TextStyleUtil.quickSandBold(context, fontSize: 15, color: AppColors.instance.black),
                  ),
                ),
              ),
              SizedBox(
                width: 5,
              ),
              Container(),
              Align(
                alignment: AlignmentDirectional.centerEnd,
                child: MarqueeWidget(
                  child: Text(
                    item.dialCode ?? "",
                    style: TextStyleUtil.quickSandBold(context, fontSize: 15, color: Colors.blue),
                  ),
                ),
              ),
            ],
          )
        ],
      ),
    );
  }

  Widget getSearchField(
    BuildContext context,
  ) {
    return Container(
        margin: EdgeInsetsDirectional.only(start: 20, end: 20, top: 20),
        child: Row(
          children: [
            Expanded(
              child: AppTextField(
                onChanged: (String text) {
                  controller.performSearchOnForFavoriteContact(text);
                },
                isPasswordField: false,
                keyboardType: TextInputType.text,
                suffixIconClickCallBack: () {},
              ),
            )
          ],
        ));
  }
}

and here is the GetX Controller:

class CountryFinderAlertController extends GetxController {
  TextEditingController countrySearchFieldEditController = TextEditingController();
  RxList<CountryHelperModel> favoritesList;
  RxList<CountryHelperModel> originalList;

  @override
  void onInit() {
    super.onInit();
    debugPrint("Hello222");
    favoritesList = <CountryHelperModel>[].obs;
    originalList = <CountryHelperModel>[].obs;
  }

  @override
  void onReady() {
    super.onReady();
    debugPrint("Hello111");
    originalList.addAll(JSONHelperUtil.getCountries());
    addAllCountries();
  }

  @override
  void dispose() {
    super.dispose();
    countrySearchFieldEditController.dispose();
  }

  @override
  void onClose() {
    super.onClose();
  }

  void performSearchOnForFavoriteContact(String filterKey) {
    if (filterKey != null && filterKey.isNotEmpty) {
      List<CountryHelperModel> filteredFavoritesList = [];

      debugPrint("filterKey" + filterKey);
      originalList.forEach((element) {
        if (element.name.toLowerCase().contains(filterKey.toLowerCase()) ||
            element.countryCode.toLowerCase().contains(filterKey.toLowerCase()) ||
            element.dialCode.toLowerCase().contains(filterKey.toLowerCase())) {
          filteredFavoritesList.add(element);
        }
      });

      if (filteredFavoritesList.isNotEmpty) {
        favoritesList.clear();
        favoritesList.addAll(filteredFavoritesList);
      } else {
        favoritesList.clear();
      }
    } else {
      //reset the list
      addAllCountries();
    }
  }

  void addAllCountries() {
    favoritesList.clear();
    favoritesList.addAll(originalList);
  }
}

So what I want is to load fresh data each time when I open this dialog. For now, if user will search for any country and close the dialog and then if reopen it the user will see the older search results.

In simple means how can GetX Controller be Reset/Destroyed or reinitialised !

Thanks in advance

Choong answered 21/4, 2022 at 10:29 Comment(0)
C
8

So the answer to this question from me is that the Flutter pub GetX do provide a way to delete any initialised controller. Let's suppose that we only have a controller that needs to call an API in its onInit() method, every time the user will land on that specific view controller suppose!

So the solution to this problem is to just call:

Get.delete<YourControllerName>();

The thing that when it should get called is important. For me the clean way to do it, when I goto a new page I register a value to return/result callback as:

Get.to(()=>YourWidgetView());

to

Get.to(()=>YourWidgetView()).then((value) => Get.delete<YourControllerName>());

So whenever the user will leave your Widget View will delete the respected controller. In this way when the user will come again to the same widget view, the controller will re-initialised and all the controller values will be reset.

If anyone does have any better solution can share with the dev community. Thanks

Choong answered 22/4, 2022 at 6:3 Comment(0)
N
4

I believe it's because of ,permanent: true
Try leaving that out.

Nought answered 21/4, 2022 at 10:35 Comment(1)
Tried that but still oninit called only once in the lifecycleChoong
S
3

Considering this is dialog, there's no need to inject the controller using Get.put() method. Instead try this, using this approach every time we call SimpleDialogWidget, its controller will be created and disposed of when Get.back() will be called.

Step 1 : Extend your SimpleDialogWidget with GetView<CountryFinderAlertController>

class SampleDialogWidget extends GetView<CountryFinderAlertController> {...}

Step 2 : Wrap your actual widget inside Getx

class SampleDialogWidget extends GetView<CountryFinderAlertController> {
 

  @override
  Widget build(BuildContext context) {
    return GetX<CountryFinderAlertController>(      //Here it is 
         init : CountryFinderAlertController(),     // like this
         builder: (controller) => Dialog(
      insetPadding: AppUtil.dialogPadding(context),
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(20),
      ),
      elevation: 0.0,
      backgroundColor: Colors.white,
      child: dialogContent(controller, context), // Also, pass the controller to dialogContent function
    );
  
    );
   }
}

That will solve your problem.

Semiautomatic answered 7/5, 2022 at 8:44 Comment(1)
You don't actually need to extend GetView if you are using GetX (or GetBuilder). Just extend it as a regular StatelessWidget.Palaeontology
Z
0

Disposing your resources always come after disposing super resources. So change the following

@override
  void dispose() {
    super.dispose();
    countrySearchFieldEditController.dispose();
  }

with

@override
  void dispose() {
    countrySearchFieldEditController.dispose();
    super.dispose();
  }

If it still not works, please attach the binding file code as well.

Zygoma answered 21/4, 2022 at 10:39 Comment(0)
R
0

Controller won't get disposed:

class SampleDialogWidget extends StatelessWidget {
  final CountryFinderAlertController controller = Get.put(CountryFinderAlertController(),permanent: true);

  @override
  Widget build(BuildContext context) {
    return Dialog(

Instantiation & registration (Get.put(...)) should not be done as a field.

Otherwise, the registration of controller is attached to LandingScreen, not MainScreen. And Controller will only get disposed when LandingScreen is disposed. Since that's the home Widget in the code above, disposal only happens upon app exit.

Fix: Move Get.put to the build() method.

class SampleDialogWidget extends StatelessWidget {
      
      @override
      Widget build(BuildContext context) {
      final CountryFinderAlertController controller = Get.put(CountryFinderAlertController());
        return Dialog(
Ramsay answered 22/4, 2022 at 5:42 Comment(1)
It will not work, please see my solution if it suits you!Choong
M
0

Others said to initialize the controller, but sometimes there are other ways. I recommend using GetWidget instead StatelessWidget

class SampleDialogWidget extends GetWidget<CountryFinderAlertController> {...}

and 'your_any_screen_bindings.dart' file seems like

class YourAnyScreenBindings implements Bindings {
  @override
  void dependencies() {
    Get.put(YourAnyScreenCtrl());
    Get.create(() => CountryFinderAlertController());
  }
}

and 'your_routes.dart' file will be...

List<GetPage<dynamic>> getPages = [
  GetPage(
    name: '/your_any_screen',
    page: () => YourAnyScreen(),
    binding: YourAnyScreenBindings(),
  ),
]

Now your dialog widget will be paired with a FRESH controller every time.

Mcintire answered 24/4, 2022 at 14:26 Comment(0)
O
0

Just add "fenix: true" and after that Get start to re-create previously disposed object

import 'package:get/get.dart';

import '../features/auth/controllers/login_controller.dart';

class AppBindings implements Bindings {
  @override
  void dependencies() {
    Get.lazyPut<LoginController>(() => LoginController(), fenix: true);
  }
}

return GetMaterialApp(
      title: 'App Title',
      initialRoute: AppRoutes.WELCOME,
      theme: lightThemeData,
      themeMode: ThemeMode.light,     
      initialBinding: AppBindings(),
    );

Oaks answered 30/6 at 7:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.