From a8a3365569241fb2e160309023877da76911076a Mon Sep 17 00:00:00 2001 From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com> Date: Wed, 4 Feb 2026 14:24:10 -0300 Subject: [PATCH] feat: support DynamicTheme initialization in multi-version add-ons Close #142 --- README.md | 5 ++ .../vaadin/addons/demo/DynamicTheme.java | 49 +++++++++++++++++-- .../addons/demo/DynamicThemeInitializer.java | 41 ++++++++++++++++ ...adin.flow.server.VaadinServiceInitListener | 3 +- 4 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/flowingcode/vaadin/addons/demo/DynamicThemeInitializer.java diff --git a/README.md b/README.md index 455555a..bb2f475 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,11 @@ public class AppShellConfiguratorImpl implements AppShellConfigurator { } ``` +When targeting Vaadin 14-25 or 23-25, the `AppShellConfigurator` approach cannot be used due to framework and library limitations. +To resolve this, you must create a configuration file `src/test/resources/META-INF/dynamic-theme.properties` with the following content: +```properties +theme=LUMO +``` ## Code Viewer diff --git a/src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java b/src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java index 1bffa17..679d4a0 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java +++ b/src/main/java/com/flowingcode/vaadin/addons/demo/DynamicTheme.java @@ -6,8 +6,10 @@ import com.vaadin.flow.server.AppShellSettings; import com.vaadin.flow.server.VaadinSession; import com.vaadin.flow.server.Version; +import com.vaadin.flow.server.communication.IndexHtmlResponse; import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.jsoup.nodes.Element; /** * Enumeration representing supported themes for dynamic switching. @@ -84,12 +86,12 @@ public static DynamicTheme getCurrent() { } /** - * Initializes the theme settings. + * Initializes the theme settings into the provided {@code AppShellSettings}. *
- * This method performs a lazy initialization of the {@link DynamicTheme} within the + * This method performs a lazy initialization of the {@code DynamicTheme} within the * current {@link VaadinSession}. If no theme is present, it registers this instance * as the session default. Subsequently, it injects the corresponding CSS stylesheet - * link into the {@link AppShellSettings}. + * link into the document head. *
* * @param settings the application shell settings to be modified @@ -116,6 +118,47 @@ public void initialize(AppShellSettings settings) { } } + /** + * Initializes the theme settings into the provided {@code IndexHtmlResponse}. + *+ * This method performs a lazy initialization of the {@code DynamicTheme} within the + * current {@link VaadinSession}. If no theme is present, it registers this instance + * as the session default. Subsequently, it injects the corresponding CSS stylesheet + * link into the document head. + *
+ * + * @param response the index HTML response to be modified + * @throws UnsupportedOperationException if the runtime Vaadin version is older than 25 + */ + public void initialize(IndexHtmlResponse response) { + assertFeatureSupported(); + + DynamicTheme theme = getCurrent(); + if (theme == null) { + theme = this; + VaadinSession.getCurrent().setAttribute(DynamicTheme.class, theme); + } + + String href = null; + switch (theme) { + case AURA: + href = "aura/aura.css"; + break; + case LUMO: + href = "lumo/lumo.css"; + break; + default: + break; + } + + if (href != null) { + Element link = response.getDocument().createElement("link"); + link.attr("rel", "stylesheet"); + link.attr("href", href); + response.getDocument().head().appendChild(link); + } + } + /** * Prepares the component for dynamic theme switching by preloading stylesheets. *diff --git a/src/main/java/com/flowingcode/vaadin/addons/demo/DynamicThemeInitializer.java b/src/main/java/com/flowingcode/vaadin/addons/demo/DynamicThemeInitializer.java new file mode 100644 index 0000000..c302be3 --- /dev/null +++ b/src/main/java/com/flowingcode/vaadin/addons/demo/DynamicThemeInitializer.java @@ -0,0 +1,41 @@ +package com.flowingcode.vaadin.addons.demo; + +import com.vaadin.flow.server.ServiceInitEvent; +import com.vaadin.flow.server.VaadinServiceInitListener; +import com.vaadin.flow.server.communication.IndexHtmlRequestListener; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +/** + * Service initialization listener that automatically applies a dynamic theme. + *
+ * If the dynamic theme feature is supported, this listener checks for the presence of a + * {@code /META-INF/dynamic-theme.properties} file. If found, it reads the {@code theme} property + * (e.g., {@code theme=LUMO}) and registers an {@link IndexHtmlRequestListener} to initialize the + * theme for all requests. + *
+ */ +@SuppressWarnings("serial") +public class DynamicThemeInitializer implements VaadinServiceInitListener { + + @Override + public void serviceInit(ServiceInitEvent event) { + if (DynamicTheme.isFeatureSupported()) { + try (InputStream in = getClass().getResourceAsStream("/META-INF/dynamic-theme.properties")) { + if (in != null) { + Properties props = new Properties(); + props.load(in); + String themeName = props.getProperty("theme"); + if (themeName != null) { + DynamicTheme theme = DynamicTheme.valueOf(themeName.trim()); + event.addIndexHtmlRequestListener(theme::initialize); + } + } + } catch (IOException e) { + throw new RuntimeException("Error reading dynamic-theme.properties", e); + } + } + } + +} diff --git a/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener b/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener index 49978bf..12a3a7f 100644 --- a/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener +++ b/src/main/resources/META-INF/services/com.vaadin.flow.server.VaadinServiceInitListener @@ -1 +1,2 @@ -com.flowingcode.vaadin.addons.DevSourceRequestHandlerInitializer \ No newline at end of file +com.flowingcode.vaadin.addons.DevSourceRequestHandlerInitializer +com.flowingcode.vaadin.addons.demo.DynamicThemeInitializer \ No newline at end of file