So far, GSoC has been an incredibly rewarding journey. Building mobile‑friendly UIs with GWT UiBinder and Java has pushed me to solve real‑world challenges—and every obstacle has taught me something new. In this post, I’ll share my progress through Week 4 and the key lessons I’ve learned along the way.
Week 1: Planning & Mentor Review
During the first week, I dove into the existing codebase to understand its structure and began envisioning how the mobile UI components would connect. I sketched out my implementation plan and discussed it in detail with my mentor, outlining the key integration points and data flows. Although this period coincided with my semester exams (so I couldn’t code as much as I’d hoped), I came away with a clear roadmap for building and wiring up the mobile interface.
Week 2: Building a Standalone Mobile UI with GWT & UiBinder
This week, I began building a standalone mobile UI module from the ground up instead of retrofitting the existing desktop code with media queries—an approach that often leads to cluttered styles and unintended breakages. By leveraging GWT and UiBinder (the same stack behind MIT App Inventor), we write type‑safe Java that compiles to optimised JavaScript, enjoy robust IDE support and compile‑time checks, and define our layouts declaratively in XML for a clean separation of structure and behaviour. This lets us reuse the desktop’s core business logic without touching its styles, avoid the cross‑browser quirks common in pure JavaScript frameworks, and benefit from GWT‑RPC’s efficient client–server communication and strong unit‑testing tools. In doing so, we achieve a maintainable, scalable codebase with clear separation of concerns—desktop versus mobile—and a mentor‑approved foundation for a robust mobile interface.
Challenges
Breakpoint-Driven Desktop‑to‑Mobile Interface Swap
In our GWT app, we define a numeric breakpoint—say, 768px—that separates desktop from mobile. On initial load, we call
int width = Window.getClientWidth();
to read the current viewport width. If
width < BREAKPOINT
, we inject our mobile CSS bundle (for example,mobileStyles.ensureInjected()
), which swaps in touch‑friendly layouts and hides desktop‑only elements. Otherwise, we keep the default desktop styles.To handle user‑driven resizes—when someone drags the browser window or rotates their device—we register a resize handler:
Window.addResizeHandler(event -> { int newWidth = event.getWidth(); if (newWidth < BREAKPOINT) { mobileStyles.ensureInjected(); } else { desktopStyles.ensureInjected(); } });
This listener means we don’t just detect the breakpoint once; we continuously watch for the window crossing that threshold, dynamically swapping style bundles as needed.
Why this matters:
getClientWidth()
gives a one‑time measurement on load, ensuring your UI starts in the right mode.addResizeHandler()
lets you react to changes—critical for desktop users who resize or tablets that rotate.- CSS bundles in GWT let you package and inject only the rules you need (mobile vs. desktop), keeping your CSS lean and avoiding messy overrides in a single stylesheet.
Together, these APIs provide a type‑safe, maintainable way to implement true adaptive behaviour—rather than bolting on media queries to an existing stylesheet, you cleanly separate and inject the appropriate styles at runtime.
Week 3: Begin TopToolbar and TopPanel Development Following Successful Mobile Interface Binding
This week, I began developing the TopPanel—specifically the TopToolbar—after successfully binding our mobile interface. Inheriting all of the desktop styles initially prevented any toolbar changes from appearing on mobile; instead, the app continued to render the parent‑class TopPanel.
During troubleshooting, my mentor, Susan, guided me to a second culprit in the build menu configuration: if the buildDropDown
items aren’t explicitly defined, the template silently falls back to the Classic layout. To work around this, she suggested mirroring those menu entries in the mobile menu (and simply hiding the original build menu) so the template can load correctly.
Digging deeper, we found the underlying XML parsers enforce a strict mapping between custom tags and Java classes. These parsers handle our drop‑down buttons without extra Java or CSS, but they’re not very feature‑rich—labels aren’t even supported yet. Although we could refine them later, ensuring every inherited component tag appears in UiBinder was enough to restore the mobile styling for now.
Although the popup‑based approach I initially built might still work, Susan and I opted for a single menu built on our existing framework to guarantee stability. Solving these nested inheritance and parser quirks taught me a valuable lesson about GWT’s markup requirements and reinforced the importance of aligning UiBinder XML with component hierarchies. Despite the week’s frustrations, Week 3 turned into a highly educational deep dive into GWT’s theming and templating mechanics.
Week 4: Implementing Hamburger Menu & PopupPanel via DisclosurePanel
This week’s focus was on replacing our dropdown build menu with a responsive hamburger menu. When tapped, it now opens a PopupPanel containing the full menu and submenus, structured with GWT’s DisclosurePanel
.
A DisclosurePanel
provides a collapsible header and content area that expands when clicked—perfect for nested submenus. Within our mobile UI, each header acts as a section title (e.g., Build, Settings), and the content panel holds related submenu items.
Why I chose DisclosurePanel + PopupPanel:
-
PopupPanel
overlays the rest of the UI and supports auto-hide behaviour and backdrop glass. -
DisclosurePanel
nested inside the popup lets users expand and collapse sub-categories smoothly.
Implementation summary:
- Tapping the hamburger icon opens a vertical menu panel within a
PopupPanel
, positioned just next to the icon. - Inside the
PopupPanel
’s UiBinder template, each menu section is wrapped in a<g:DisclosurePanel>
, allowing GWT to link these sections to corresponding Java event handlers. - Here’s an example of the UiBinder markup:
<ai:DropDownButton name="Settings"
styleName="ode-TopPanelButton"
ui:field="settingsDropDown"
align="left"
icon="menu"
caption="{messages.menuButton}">
</ai:DropDownButton>
<g:VerticalPanel ui:field="menuContent" visible="false" spacing="10">
<!-- Projects Section -->
<g:DisclosurePanel>
<g:header styleName="mobile-SectionHeader">Projects</g:header>
<g:VerticalPanel styleName="mobile-SectionPanel">
<g:Button ui:field="myProjectsButton"
text="{messages.projectMenuItem}"
visible="false"/>
<g:Button ui:field="newButton"
text="{messages.newProjectMenuItem}"
visible="{hasWriteAccess}"/>
<g:Button ui:field="importProjectButton"
text="{messages.importProjectMenuItem}"
visible="false"/>
<!-- Other subMenu Items here -->
</g:VerticalPanel>
</g:DisclosurePanel>
<!-- Additional sections here -->
</g:VerticalPanel>
settingsDropDown.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
LOG.warning("Hamburger menu clicked");
menuPopup.setWidget(menuContent);
menuPopup.center();
menuContent.setVisible(true);
}
});
What changed and why it works:
- Clarified that the vertical menu is shown inside a
PopupPanel
when the hamburger is tapped. - Specified that each section uses
DisclosurePanel
to enable collapsible behavior and backend event handling. - Improved markup readability by aligning attributes and showing how GWT binds UI to Java logic.
Resources I relied on this week:
Week 4 delivered not just a functional hamburger menu, but also strengthened my understanding of GWT’s composable widgets. It’s been a gratifying step toward a more intuitive mobile experience.
Here is my Pr Link:
https://github.com/mit-cml/appinventor-sources/pull/3491
Thank you for reading to the end! I hope you found this post insightful and full of useful takeaways. I’ll be back soon with Week 5’s update—so stay tuned. Until then, keep coding and keep exploring!
Top comments (2)
this is extremely impressive, reading through your progress makes me want to go back and rebuild half my old interfaces from scratch. where do you think is the real bottleneck for keeping mobile and desktop UIs cleanly separated in these big legacy codebases
Thanks so much for the kind words! In large legacy UI codebases, the real bottleneck often lies in tightly coupled dependencies, shared services, data models, or UI components that spill over between mobile and desktop layers. Without a clearly defined core layer, teams tend to duplicate logic, which gradually leads to divergence and inconsistency. The lack of documentation or meaningful tests only makes every change feel risky. What has worked well for me is extracting a focused core module that contains business logic and shared contracts, then building two lightweight and isolated shells for mobile and desktop on top of it, without cross-imports. Adding just enough diagrams, documentation, and a few smoke tests around critical flows has also helped keep things maintainable as the code evolves.