Java Nerd
Java Nerd

Reputation: 187

Flutter GetX Re-Initialise GetX Controller Reset GetX Controller, Reset GetX Controller Values

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

Upvotes: 7

Views: 22997

Answers (7)

Stanislav Glushak
Stanislav Glushak

Reputation: 321

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

Upvotes: 0

Tushar Asodariya
Tushar Asodariya

Reputation: 817

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.

Upvotes: 3

Hyungju Moon
Hyungju Moon

Reputation: 141

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.

Upvotes: 0

Java Nerd
Java Nerd

Reputation: 187

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

Upvotes: 9

Maqsood
Maqsood

Reputation: 937

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(

Upvotes: 0

Naveed Ullah
Naveed Ullah

Reputation: 145

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.

Upvotes: 0

Ivo
Ivo

Reputation: 23144

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

Upvotes: 4

Related Questions