Использование виджета Switch () с getX / Obx()

#flutter #flutter-getx

#flutter #flutter-getx

Вопрос:

Я возвращаюсь к вопросу: как я могу использовать виджет Switch () с getX / Obx ()? Во многих местах я читал, что с getX / Obx нет необходимости в StatefullWidget. Итак, в моем приложении я использую только StatelessWidgets. Проблема в том, что я не могу заставить Switch() работать, потому что setState() недоступен в StatelessWidget.

Может кто-нибудь помочь, пожалуйста? Спасибо за вашу доброту. А.КОТЭ

Ответ №1:

getX Переключатель

Вот пример копирования / вставки использования getX с Switch виджетом внутри StatelessWidget .

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

/// GetX Controller for holding Switch's current value
class SwitchX extends GetxController {
  RxBool on = false.obs; // our observable

  // swap true/false amp; save it to observable
  void toggle() => on.value = on.value ? false : true;
}

/// Stateless Page here
class SwitchGetxPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    SwitchX sx = Get.put(SwitchX()); // Instantiate Get Controller, *in* build()

    return Scaffold(
      appBar: AppBar(
        title: Text('Switch Field'),
      ),
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              // Obx will rebuild Text amp; Switch when "on" observable changes
              Obx(() => Text('Switch Setting: ${sx.on}')),
              Obx(() => Switch(
                onChanged: (val) => sx.toggle(), 
                value: sx.on.value),
              )
            ],
          ),
        ),
      ),
    );
  }
}
 

getX Состояние

С getX вам не нужно StatefulWidgets , потому что вы держите «состояние» вне виджетов и «внутри» a GetxController .

В приведенном выше примере RxBool on = false.obs это «состояние» вашего приложения. Это будет true или false на протяжении всего срока службы вашего приложения.

Когда Switch's значение изменяется, любые виджеты, которые вам нужны, перестраиваются, помещаются в Obx GetX или GetBuilder и используют наблюдаемую переменную.

Obx , GetX будут перестраиваться всякий раз, когда их наблюдаемые изменения. GetBuilder требуется, чтобы вы вызывали update() его для восстановления. Точно так же, как setState() вызов.

Почему два Obx в примере?

Чтобы было до боли очевидно, что Obx/GetX это должно напрямую переносить наблюдаемую переменную.

GetX просит вас использовать наблюдаемые переменные непосредственно Obx/GetX , а не дальше вложенных вниз, как в дочернем элементе дочернего элемента.

Например, это ниже, выдаст ошибку:

 class SwitchGetxPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    SwitchX sx = Get.put(SwitchX());

    return Scaffold(
      appBar: AppBar(
        title: Text('Switch Field'),
      ),
      body: SafeArea(
        child: Center(
          child: Obx(() => MyTextSwitch(sx)), // don't do this, will explode
        ),
      ),
    );
  }
}

class MyTextSwitch extends StatelessWidget {
  final SwitchX sx;

  MyTextSwitch(this.sx);

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text('Switch Setting: ${sx.on}'),
        Switch(
            onChanged: (val) => sx.toggle(),
            value: sx.on.value)
      ],
    );
  }
}
 

Эта версия ниже по-прежнему в порядке, но не используется в первом примере, так как это может привести к появлению вредных привычек (например, к перестроению виджетов, которые в этом не нуждаются) и ошибкам, подобным примеру, приведенному непосредственно выше. Просто помните, что Obx/GetX в его дочернем элементе должен быть observable:

 class SwitchGetxPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    SwitchX sx = Get.put(SwitchX());

    return Scaffold(
      appBar: AppBar(
        title: Text('Switch Field'),
      ),
      body: SafeArea(
        child: Center(
          child: Obx( // still OK, but rebuilds Column unnecessarily
            () => Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text('Switch Setting: ${sx.on}'),
                Switch(
                    onChanged: (val) => sx.toggle(),
                    value: sx.on.value)
              ],
            ),
          ),
        ),
      ),
    );
  }
}
 

Ответ №2:

Для простого переключения состояния переключателя вы не должны использовать контроллер. Контроллер предназначен для более сложной реализации бизнес-логики, которая может быть разделена между экранами. Вы можете использовать ValueBuilder из Get пакета, который может дать желаемый результат с меньшим количеством кода. Вот пример :

 Center(
          child: ValueBuilder<bool>(
            initialValue: true,
            builder: (isChecked, updateFn) => Switch(
              value: isChecked,
              onChanged: (newValue) => updateFn(newValue),
            ),
          ),
        ),
 

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

1. Привет, все в порядке, это работает!!! Большое спасибо. Можно ли использовать тот же метод для переключателей (я имею в виду radioListTile)?

2. Да, любой виджет, для которого требуется локальное состояние, можно легко выполнить таким образом, если вы используете пакет getX.

3. выдает ошибку: Expected a value of type '(bool?, (bool?) => void) => Widget', but got one of type '(bool, (bool) => void) => Switch'

4. Ответ был до того, как был введен нулевой сейф, но теперь его необходимо настроить, чтобы он был нулевым.

Ответ №3:

Я могу привести вам пример, а затем вы можете реализовать его в своем коде.

Вы можете использовать GetxController для обработки состояния виджета.

Встречный пример:

 class YourController extends GetxController {
  //The current value
  int counter = 0;

  void increment() {
    counter  ;
    update(); // use update() to update counter variable on UI when increment be called
  }
}
 

Затем в вашем StatelessWidget вы можете прослушивать.

 class YourWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Center(
      child: GetBuilder<YourController>(
        init: Controller(), // INIT IT ONLY THE FIRST TIME
        builder: (_) => Text(
          '${_.counter}',
        ),
      ),
    ));
  }
}
 

ДОКУМЕНТЫ: https://github.com/jonataslaw/getx/blob/master/documentation/en_US/state_management.md#simple-state-manager

Как вы можете обновить значение?

Добавьте строку в свой контроллер

 class YourController extends GetxController {
      //New line added
      static YourController  get to => Get.find();
      int counter = 0;

      void increment() {
        counter  ;
        update(); // use update() to update counter variable on UI when increment be called
      }
    }
 

Обновите пользовательский интерфейс

 return Scaffold(
      // body: /*... */
      floatingActionButton: FloatingActionButton(
        child: Text('Hit'),
        onPressed: () {
          //Apply the logic to your Switch widget
          YourController.to.update();
        },
      ),
    );
  }
 

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

1. Привет, Роландо, большое спасибо за вашу помощь. Ваш пример мне понятен, и я уже использую тот же формат в своем коде. Моя настоящая проблема заключается в следующем: у меня есть виджет переключения в StatelessWidget. Как я могу добиться его включения / выключения? Я думаю, это будет возможно, когда я использую getX / Obx(). Но я не знаю, как это сделать?