Управление состоянием Flutter getX не работает

#flutter #flutter-getx

Вопрос:

Проблема, с которой я сталкиваюсь, заключается в том, что текст пользовательского интерфейса не обновляется. Данные, которые я хочу, в данном случае placeName, успешно напечатаны на консоли ОТЛАДКИ, но это не меняет пользовательский интерфейс текстового виджета. Я думал, что при простом управлении операторами в Getx он обновит переменную с помощью update() в методе. Я столкнулся с подобной ситуацией, и я не мог понять этого раньше, поэтому мне пришлось использовать statefuleWidget, чтобы решить ее. Но на этот раз я хотел бы продолжить это с Getx.

введите описание изображения здесь

Кто-нибудь может мне помочь?

2021-11-10 Я обновил свой код, но он по-прежнему не работает

2021-11-18 Я понял это, изменив возвращаемый тип функции (searchCoordinateAddress) и присвоив его переменной (pickUpLocation), которую я хотел обновить.

 // home_controller.dart
class HomeController extends GetxController {
  ...
  Rx<Address> pickUpLocation = Address().obs;
  RxBool isLoading = false.obs;
  ...


void locatePosition() async {
  ...
  ...

//  String? address = await UberRepository.to.searchCoordinateAddress(position); // previous

// current
 Address? address = await UberRepository.to.searchCoordinateAddress(position);
 pickUpLocation(address);
 isLoading(true);
}


// previous
/*
void updatePickUpLocationAddress(Address pickUpAddress) {
print("pickUpAddress.placeName: ${pickUpAddress.placeName}");
pickUpLocation(pickUpAddress);
print("pickUpLocation.value.placeName: ${pickUpLocation.value.placeName}");
  }
}
*/
 
 // uber_repository.dart
class UberRepository extends GetConnect {
  static UberRepository get to => Get.find();

  @override
  void onInit() {
    httpClient.baseUrl = 'https://maps.googleapis.com/';
    super.onInit();
  }

  // Future<String?> // previous
  // current
  Future<Address?> searchCoordinateAddress(Position position) async {
    String placeAddress = "";

    String url =
        'maps/api/geocode/json?latlng=${position.latitude},${position.longitude}amp;key=$mapKey';

    final response = await get(url);

    if (response.status.hasError) {
      Future.error(response.statusText!);
    } else {
      placeAddress = response.body["results"][0]["formatted_address"];

      Address userPickUpAddress = Address();
      userPickUpAddress.latitude = position.latitude;
      userPickUpAddress.longitude = position.longitude;
      userPickUpAddress.placeName = placeAddress;


      return userPickUpAddress; // current

      // HomeController().updatePickUpLocationAddress(userPickUpAddress); // previous


    }
    // return placeAddress; // previous
  }
}
 
 // home.dart
class Home extends GetView<HomeController> {
  const Home({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
...
 body:  Stack(
          children: [
            GoogleMap(
              padding: EdgeInsets.only(
                bottom: controller.bottomPaddingOfMap.value,
              ),
              mapType: MapType.normal,
              myLocationButtonEnabled: true,
              initialCameraPosition: controller.kGooglePlex,
              myLocationEnabled: true,
              zoomGesturesEnabled: true,
              zoomControlsEnabled: true,
              onMapCreated: (GoogleMapController googleMapController) {
                controller.controllerGoogleMap.complete(googleMapController);
                controller.newGoogleMapController = googleMapController;
                // when map created and map is set correctly, call current location
                controller.setBottomPaddingOfMap();
                controller.locatePosition(); 
              },
            ),
...
...
Expanded(
    child: Obx(() => 
                  Column(
              crossAxisAlignment: CrossAxisAlignment.start,
                 children: [
                    // TODO: UI update...
                    !controller.isLoading.value
                          ? CircularProgressIndicator()
                          : Text(
                             controller.pickUpLocation.value != null
                          ? controller.pickUpLocation.value.placeName!
                               : "Add Home",
                               maxLines: 2,
                            overflow: TextOverflow.ellipsis,
                            softWrap: false,
                              ),
                              SizedBox(height: 4.0),
                              Text(
                                "Your living home address",
                                style: TextStyle(
                                  color: Colors.black54,
                                  fontSize: 12.0,
                   ),
                   ),
                  ],
            ),
           ),
     ),
...

 
 // address.dart
class Address {
  String? placeFormattedAddress;
  String? placeName;
  String? placeId;
  double? latitude;
  double? longitude;

  Address({
    this.placeFormattedAddress,
    this.placeName,
    this.placeId,
    this.latitude,
    this.longitude,
  });
}

 

Ответ №1:

Вы можете сделать это с помощью Obx . В вашем классе контроллера сделайте что-то вроде этого

 class HomeController extends GetxController {

     Address get pickupLocation => _pickupLocation.value;

  set pickupLocation(Address value) {
    _pickupLocation.value = value;
  }

  Rx<Address> pickUpLocation= Rx<Address>(Address());
  ...
  ...
  ...


  void updatePickUpLocationAddress(Address? pickUpAddress) {
    pickUpLocation = pickUpAddress;
    print("Here? ${pickUpLocation!.placeName!}"); // printed correctly as I intended
  }
}
 

Теперь в вашем классе пользовательского интерфейса используйте Obx вместо GetBuilder следующим образом

 // home.dart
class Home extends GetView<HomeController> {
  const Home({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
...
...
...
...
            Obx(() => controller.pickupLocation.placeName != null
                        ? Text(controller.pickupLocation.placeName)
                        : Text('Add home')),
...
 

Комментарии:

1. Я следовал вашему коду, и была ошибка проверки null, и я переместил Obx(() => ) в родительский виджет, и ошибка исчезла, но данные имени места — «, пустая строка. Я думаю, что это не относится к пользовательскому интерфейсу… Что мне делать?

2. Я могу распечатать нужные данные в HomeController, но сторона пользовательского интерфейса не обновляется и показывает («») пустую строку. Итак, я предполагаю, что метод сборки Home возвращает виджеты раньше, чем HomeController, поэтому данные не применяются к пользовательскому интерфейсу. У вас есть какие-либо идеи для этого?

3. Вызовите метод api внутри OnInit() и добавьте флаг, чтобы отслеживать, получен ли ответ, который вы уже получили. Когда вы загружаете состояние, вы можете показать диалоговое окно прогресса, когда pickupLocation обновляется значением api, обновите состояние.

4. Я все еще борюсь с этим вопросом… Я добавил переменную isLoading Rxbool на контроллер, и я закодировал, когда это значение true, показываю имя места, также я проверил, что печать pickUpLocation.value.placeName обновляется раньше, чем isLoading становится true. Но controller.pickUpLocation.value.placeName на стороне пользовательского интерфейса равно нулю. Что мне делать? Это так расстраивает

5. Обновляются ли значения в контроллере?

Ответ №2:

Я думаю, вам нужно вызвать pickUpLocation.refresh() свой виджет. Вот соответствующая часть из примера со страницы разработки getx https://pub.dev/packages/get:

 // `Rx` don't have any clue when you change something inside user.
// So, for custom classes, we need to manually "notify" the change.
user.refresh();
 

Комментарии:

1. Я добавил pickUpLocation.refresh() в функцию updatePickUpLocationAddress, она не работает, и я использовал метод обновления, как показано ниже: « pickUpLocation.update((value) { значение! .placeName = pickUpAddress.placeName; }); « пользовательский интерфейс по-прежнему не обновляется… Я вижу только, что имя места напечатано правильно… но не на стороне пользовательского интерфейса.