preface

For some scenarios, we may have multiple components sharing a piece of state data, but if the state data changes, we may only need to update one or more of the components, rather than all of the components that depend on the state. This is where we can use GetX’s directional update. GetX can provide two optional arguments in the update method:

void update([List<Object>? ids, bool condition = true]) {
  if(! condition) {return;
  }
  if (ids == null) {
    refresh();
  } else {
    for (final id inids) { refreshGroup(id); }}}Copy the code
  • ids: To be updatedidArray,idCan be found inGetBuilderSpecify it at build time, if specifiedids, and then updated withidsIn theidMatching components:
GetBuilder<Controller>(
  id: 'text'
  init: Controller(), // use it only first time on each controller
  builder: (_) => Text(
    '${Get.find<Controller>().counter}'.//here),),Copy the code
  • condition: conditional expression that updates the component only if the condition is true.

For example, the following code only updates the component with id text when counter is less than 10.

update(['text'], counter < 10);
Copy the code

Next, we simulate a traffic light with a countdown to demonstrate the use of GetX’s directional updates.

The business logic

To control traffic lights separately, we need three components, red, green and yellow.

Then for the countdown, we set the rules as follows:

  • The length of the green light is 20 seconds, the red light is 10 seconds, the yellow light is 3 seconds, the timing is completed through the timer, every 1 second minus 1.
  • The three red lights share a timer, but the countdown time of which light is updated according to the state of the current light. Meanwhile, we do not display the countdown time of the light that is not on (because the countdown time is shared, it will be wrong if displayed).
  • Use an enumeration to determine which light is currently on in the order green -> yellow -> red -> green…

Business straightened out, began to masturbate code!

Traffic light code

First we build a generic traffic light component, TrafficLed, with four parameters:

  • Color of lamp:ledColor, control the color of the countdown number of the lamp;
  • Countdown time:secondsLeft, countdown time;
  • Whether to display countdown:showSeconds, use Offstate to control whether the countdown time is displayed.
  • Lamp size:ledSize, the default size is 60, used to control the size of the lamp.

The corresponding code is very simple, here we make a little shadow to make it look a little bit three-dimensional.

class TrafficLed extends StatelessWidget {
  final Color ledColor;
  final int secondsLeft;
  final bool showSeconds;
  final double ledSize;
  const TrafficLed({
    Key? key,
    required this.ledColor,
    required this.secondsLeft,
    required this.showSeconds,
    this.ledSize = 60.0,}) :super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        alignment: Alignment.center,
        width: ledSize,
        height: ledSize,
        decoration: BoxDecoration(
          color: Colors.black,
          borderRadius: BorderRadius.circular(ledSize / 2),
          boxShadow: [
            BoxShadow(
              color: Color(0xFF505050),
              offset: Offset(1.- 1),
              blurRadius: 0.2,
            )
          ],
        ),
        child: Offstage(
          child: Text(
            '$secondsLeft',
            textAlign: TextAlign.center,
            style: TextStyle(
              color: this.ledColor,
              fontSize: 36,
              fontWeight: FontWeight.bold,
            ),
          ),
          offstage: !showSeconds,
        ),
      ),
    );
  }
}
Copy the code

The next step is the combination of lights. Here we use horizontal traffic lights and shadows to create a three-dimensional background. The key code uses the GetBuilder package for each light and then specifies the ID of each light, using the green light as an example:

GetBuilder<TrafficLightController>(
  id: 'green',
  init: lightController,
  builder: (state) => TrafficLed(
    ledColor: (state.currentLight == TrafficLight.green
        ? Colors.green
        : Colors.black),
    secondsLeft: state.counter,
    showSeconds: state.currentLight == TrafficLight.green,
  ),
),
Copy the code

The logic for each lamp is as follows:

  • If the lamp displayed in the current state is consistent with itself, the color of the countdown text will use the corresponding color of the lamp, that is, red, yellow and green; Otherwise, display black (consistent with background color);
  • The countdown time of the binding status object;
  • If the lamp displayed in the current state is consistent with itself, the countdown will be displayed, otherwise it will not be displayed.

Status management code

The state management controller is TrafficLightController and the code is as follows:

enum TrafficLight { green, red, yellow }

class TrafficLightController extends GetxController {
  late TrafficLight _currentLight;
  get currentLight => _currentLight;

  int _counter = 0;
  get counter => _counter;

  late Timer _downcountTimer;

  @override
  void onInit() {
    _counter = 20;
    _currentLight = TrafficLight.green;
    super.onInit();
  }

  @override
  void onReady() {
    _downcountTimer = Timer.periodic(Duration(seconds: 1), decreament);
    super.onReady();
  }

  void decreament(Timer timer) {
    _counter--;
    if (_counter == 0) {
      switch (_currentLight) {
        case TrafficLight.green:
          _currentLight = TrafficLight.yellow;
          _counter = 3;
          update(['green'.'yellow']);
          break;
        case TrafficLight.yellow:
          _currentLight = TrafficLight.red;
          _counter = 10;
          update(['red'.'yellow']);
          break;
        case TrafficLight.red:
          _currentLight = TrafficLight.green;
          _counter = 20;
          update(['red'.'green']);
          break; }}else {
      switch (_currentLight) {
        case TrafficLight.green:
          update(['green']);
          break;
        case TrafficLight.yellow:
          update(['yellow']);
          break;
        case TrafficLight.red:
          update(['red']);
          break; }}}@override
  void onClose() {
    _downcountTimer.cancel();
    super.onClose(); }}Copy the code

Three declared periodic functions are used:

  • onInit: Set the countdown time to 20 seconds, the light status is green;
  • onReady: Start timer;
  • onClose: Disables the timer.

The core business logic is all in decreament, the timer callback method, which also decreament if we switch the light state when the countdown reaches 0, resets the countdown time, and only updates the lights that need to be refreshed in that case (2 lights at a time). If the countdown does not reach 0, then we just need to update the current light. This way, we can target updates and reduce unnecessary flushes while sharing status data.

Running effect

The operation effect is shown below, the source code has been submitted to: GetX related code.

conclusion

The GetBuilder of GetX uses the ID parameter to implement a directional refresh feature. This applies to situations where multiple components share a state object but the update conditions are different, such as traffic lights in this example. At the same time, GetxController update method can also achieve conditional update, through the COMBINATION of ID and condition can achieve more accurate directional refresh.


I am dao Code Farmer with the same name as my wechat official account. This is a column about the introduction and practice of Flutter, providing systematic learning articles about Flutter. See the corresponding source code here: The source code of Flutter Introduction and Practical column. If you have any questions, please add me to the wechat account: island-coder.

👍🏻 : feel the harvest please point a praise to encourage!

🌟 : Collect articles, easy to look back!

💬 : Comment exchange, mutual progress!