Search code examples
flutterflutter-getxflutter-state

Flutter UI is refreshed only after quick reload when using GetX


I'm trying to use GetX to change the color of a selected "Category", that comes from a list of categories. Currently, in the background the values are changing as expected, but the UI is not refreshed.

I tried both GetBuilder and Obx to have my UI updated, but with same outcome. Please see the code bellow:

import 'package:flutter/material.dart';

class Category {
  String name;
  IconData icon;
  Set<SubCategory> subcategories;

  Category({
    required this.name,
    required this.icon,
    required this.subcategories,
  });
}

class SubCategory {
  String name;
  IconData icon;

  SubCategory({
    required this.name,
    required this.icon,
  });
}

import 'package:annual_budget/backend/model/spending_categories.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

class ExpensesController extends GetxController {
  var expenseCategories = [].obs;
  var expenseSubcategories = [].obs;
  var selectedCategoryName = ''.obs;
  var selectedSubcategoryName = ''.obs;

  @override
  void onInit() {
    super.onInit();
    expenseCategories.value = [
      Category(
        icon: Icons.all_inclusive_rounded,
        name: 'Everyday',
        subcategories: {
          SubCategory(icon: Icons.local_grocery_store, name: 'Groceries'),
          SubCategory(icon: Icons.restaurant, name: 'Restaurant'),
          SubCategory(icon: Icons.delivery_dining, name: 'Food Order'),
        },
      ),
      Category(
        icon: Icons.travel_explore,
        name: 'Travel',
        subcategories: {
          SubCategory(
              icon: Icons.airplane_ticket_outlined, name: 'Transportation'),
          SubCategory(icon: Icons.food_bank_rounded, name: 'Food'),
          SubCategory(icon: Icons.house, name: 'Accomodation'),
        },
      ),
      Category(
        icon: Icons.pets_rounded,
        name: 'Pet',
        subcategories: {
          SubCategory(icon: Icons.food_bank, name: 'Food'),
          SubCategory(icon: Icons.medical_services, name: 'Veterinary'),
        },
      ),
    ];
  }

  void setSubcategories(Set<SubCategory> subcategories, String categoryName) {
    print(selectedCategoryName);
    selectedCategoryName.value = categoryName;
    expenseSubcategories.value = subcategories.toList();
    print(selectedCategoryName);
    refresh();
  }
}

In the above print statements, the value changes as expected, but without propagating to the UI

import 'package:flutter/material.dart';
import 'package:get/get.dart';

class NewExpenseScreen extends StatelessWidget {
  static const String route = 'new_expense_screen2';
  final ExpensesController exc = Get.put(ExpensesController());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      appBar: AppBar(
        leading: MaterialButton(
          onPressed: () => Navigator.of(context).pop(),
          child: const Icon(Icons.arrow_back_ios_new_rounded),
        ),
        title: const Text('Add a new Expense'),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 10.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            Expanded(
              child: Obx(
                () => ListView.builder(
                    itemCount: exc.expenseCategories.length,
                    itemBuilder: (context, index) {
                      Category category = exc.expenseCategories[index];

                      return MaterialButton(
                        color: category.name == exc.selectedCategoryName.value
                            ? ThemeData.dark().colorScheme.secondary
                            : ThemeData.dark().colorScheme.background,
                        onPressed: () {
                          exc.setSubcategories(
                              category.subcategories, category.name);

                          print(
                              'category ${category.name} isSelected: ${category.name == exc.selectedCategoryName.value}  ');
                          FocusManager.instance.primaryFocus?.unfocus();
                        },
                        child: Padding(
                          padding: const EdgeInsets.symmetric(vertical: 10),
                          child: Column(
                            children: [
                              Icon(
                                category.icon,
                                color: category.name ==
                                        exc.selectedCategoryName.value
                                    ? Colors.black
                                    : Colors.white,
                              ),
                              Text(
                                category.name,
                                style: TextStyle(
                                  color: category.name ==
                                          exc.selectedCategoryName.value
                                      ? Colors.black
                                      : Colors.white,
                                ),
                              )
                            ],
                          ),
                        ),
                      );
                    }),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

The print statement from the OnPressed shows the desired changes, but the UI is still not refreshed until I do a hot reload.


Solution

  • Please check out below code.

    void setSubcategories(Set<SubCategory> subcategories, String categoryName) {
        print(selectedCategoryName);
        selectedCategoryName.value = categoryName;
        expenseSubcategories.value = subcategories.toList();
        print(selectedCategoryName);
        expenseCategories.refresh();
    }
    

    You have to refresh the expenseCategories list in the setSubcategories method.

    When you need to update multiple lists, you should use getbuilder.