Is this blinking supposed to work

Hi, I wanted the code below to display a blinking label, but it just displays a gray one. Where could be the problem?

#include <stdio.h>
#include <stdlib.h>
#include "pico/stdlib.h"
#include "lvgl.h"

void lvgl_display_init();

int main() {
    stdio_usb_init();
    sleep_ms(2000);

    lv_init();
    lvgl_display_init();

    lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x000000), LV_PART_MAIN);

    lv_obj_t *label = lv_label_create(lv_scr_act());
    lv_label_set_text(label, "Hello");
    lv_obj_center(label);

    static signed char BlinkCounter = 1;
    while (1) {
        lv_timer_handler();

        BlinkCounter = -BlinkCounter;
        lv_obj_set_style_text_color(label,
            lv_color_hex(BlinkCounter > 0 ? 0xFFFF00 : 0x00F000),
            LV_PART_MAIN | LV_STATE_DEFAULT);
        lv_label_set_text(label, "Hello2");
        lv_obj_invalidate(label);

        sleep_ms(500);
    }
}

Welcome to the forum,

This will not work.

You will have to make an lv_timer and handle settings the colors there. See the docs.
https://docs.lvgl.io/master/details/main-modules/timer.html

Secondly, you do not need to invalidate and set the text for the label every time the timer occurs. Just set the label once upon creation, and don’t call invalidate.

Thirdly, I would just use the blink variable as follows:

// LVGL TIMER HANDLER
{
   static int blink = 0;

   blink = !blink;

   lv_obj_set_style_text_color(label,
   lv_color_hex(BlinkCounter == 0 ? 0xFFFF00 : 0x00F000),
               LV_PART_MAIN | LV_STATE_DEFAULT);
 
   
}

Maybe the example is not representative because what I want is a generic method of asynchronous widget modification, outside of timers and animations. Adding both lv_timer_handler() and lv_tick_inc() into the loop seems to help.

lv_tick_inc() should ideally be called inside of a hardware timer interrupt.

I think what you are looking for instead are observers Observer — LVGL 9.3 documentation (if not timers).

I want to make clear here that you really are not supposed to call LVGL functions outside of timing/loops setup by LVGL itself.

I want to make clear here that you really are not supposed to call LVGL functions outside of timing/loops setup by LVGL itself.

Could you tell why? Maybe show an example of an asynchronous, trivial one-time widget state change “inside of timing/loops setup by LVGL itself” and why it is required?

I would want to know these details, in order to understand better what is happening behind.