Build APK + AAB

JavaScript bridge API

Home-screen widget

A small Android card on the home screen that your page fills with a title, a value and a few lines - AppMint.widget.

AppMint.widget helper#

AppMint.widget.set(card) / clear() / placedCount() / requestPin()

Example

Fills your app's home-screen widget: a small native card with a title, one big value, up to six short lines, your colours, and a route that a tap opens. A widget cannot show HTML - you send it the text.

Returns: every method returns a Promise. set(card) → {ok: true, placed} (placed = how many copies are on the home screen) or {ok: false, error}. clear() → undefined. placedCount() → a number. requestPin() → true if the launcher's "Add" sheet opened. Needs: nothing - every build has the widget.

Set the card - once at start, and again every time the data it shows changes:

async function updateWidget(events) {
  if (!(window.AppMint && AppMint.widget)) return;          // in a browser
  var r = await AppMint.widget.set({
    title: 'Today',
    value: events.length + ' events',
    lines: events.slice(0, 6).map(function (ev) { return ev.time + ' ' + ev.name; }),
    route: '/calendar',      // a tap opens the app at this route
    bg: '#0E7C86',           // background
    ink: '#FFFFFF',          // title and lines
    accent: '#FFE08A'        // the big value
  });
  if (!r.ok) console.log('Widget not updated: ' + r.error);
}

An "Add to home screen" button:

async function addWidget() {
  if (!(window.AppMint && AppMint.widget)) return;
  if ((await AppMint.widget.placedCount()) > 0) { alert('The widget is already on your home screen'); return; }
  var opened = await AppMint.widget.requestPin();
  if (!opened) alert('Touch and hold your home screen, tap Widgets, and pick this app.');
}

Empty it (for example after the user signs out):

if (window.AppMint && AppMint.widget) AppMint.widget.clear();   // shows "Nothing to show yet"

A tap on the widget opens the app with appmint-shortcut://<package><route> as its launch link - read it with WebToApk.getLaunchUrl() on start and the appmint:deep-link event while running:

window.addEventListener('appmint:deep-link', function (e) {
  var m = /^appmint-shortcut:\/\/[^\/?#]*([^#]*)/i.exec(e.detail.url);
  if (m) goTo(m[1] || '/');
});

Notes: All card fields are optional: an empty title shows the app name; empty value or lines are hidden; lines after the sixth are dropped; colours are #RRGGBB / #AARRGGBB (a bad colour uses the default). The widget keeps the last card, even after a restart, and changes only when your page calls set() again. requestPin() needs Android 8+ and a launcher that supports it. AI-built apps can use widget from @/lib/appmintNative.

widgetClear bridge#

window.WebToApk.widgetClear()

Removes the card; placed widgets go back to their empty state.

Example

Internal plumbing behind AppMint.widget.clear() - removes the widget's card; every placed widget goes back to "Nothing to show yet".

Returns: nothing. Needs: nothing.

The public API first:

async function onSignOut() {
  if (window.AppMint && AppMint.widget) await AppMint.widget.clear();
}

The raw call:

if (window.WebToApk && typeof window.WebToApk.widgetClear === 'function') {
  window.WebToApk.widgetClear();
}

Tie the widget to a "Show on home screen" switch in your settings - off empties it, on fills it again:

document.getElementById('widget-toggle').onchange = async function (e) {
  if (!(window.AppMint && AppMint.widget)) return;
  if (e.target.checked) {
    await AppMint.widget.set(buildWidgetCard()); // your own card builder
  } else {
    await AppMint.widget.clear();
  }
};

After deleting the account, empty the widget and tell the user if a copy is still on their home screen:

function afterAccountDeleted() {
  var W = window.WebToApk;
  if (!W || typeof W.widgetClear !== 'function') return;
  W.widgetClear();
  if (W.widgetPlacedCount() > 0) {
    showMessage('Your widget is now empty. You can remove it from your home screen.');
  }
}

Notes: The widget stays on the home screen; only its content is removed. Call AppMint.widget.set() to fill it again.

widgetPlacedCount bridge#

window.WebToApk.widgetPlacedCount(): Int

How many copies of the widget the user has placed on the home screen.

Example

Internal plumbing behind AppMint.widget.placedCount() - how many copies of your widget the user has put on the home screen.

Returns: a number, synchronously (0 when none). Needs: nothing.

The public API first - show an "Add widget" button only when the widget is not placed yet:

async function showAddWidgetButton() {
  var btn = document.getElementById('add-widget');
  if (!(window.AppMint && AppMint.widget)) { btn.hidden = true; return; }
  btn.hidden = (await AppMint.widget.placedCount()) > 0;
}

The raw call:

if (window.WebToApk && typeof window.WebToApk.widgetPlacedCount === 'function') {
  var n = window.WebToApk.widgetPlacedCount();
  console.log('Widgets on the home screen: ' + n);
}

Check again each time the user comes back from the launcher, and thank them once the widget is there:

document.addEventListener('visibilitychange', async function () {
  if (document.visibilityState !== 'visible') return;
  if (!(window.AppMint && AppMint.widget)) return;
  var n = await AppMint.widget.placedCount();
  document.getElementById('widget-promo').hidden = n > 0;
  if (n > 0) document.getElementById('widget-thanks').hidden = false;
});

Show the widget's status on a settings page with the synchronous raw call:

var status = document.getElementById('widget-status');
if (window.WebToApk && typeof window.WebToApk.widgetPlacedCount === 'function') {
  var n = window.WebToApk.widgetPlacedCount();
  status.textContent = n === 0 ? 'Not on your home screen'
    : n === 1 ? 'On your home screen' : 'On your home screen ' + n + ' times';
} else {
  status.textContent = 'Available in the installed app';
}

Notes: Check it again when the app comes back to the front (visibilitychange), since the user adds widgets from the launcher, outside your app.

widgetRequestPin bridge#

window.WebToApk.widgetRequestPin(): Boolean

Opens the launcher's "Add to home screen" sheet for the widget (Android 8+). False where the launcher cannot.

Example

Internal plumbing behind AppMint.widget.requestPin() - opens the launcher's "Add to home screen" sheet for your widget.

Returns: true if the sheet opened, false where the launcher cannot (synchronously). Needs: Android 8+ and a launcher that supports pinning widgets.

The public API first, from a button tap:

document.getElementById('add-widget').onclick = async function () {
  if (!(window.AppMint && AppMint.widget)) return;
  var opened = await AppMint.widget.requestPin();
  if (!opened) {
    alert('Touch and hold your home screen, tap Widgets, then pick this app.');
  }
};

The raw call:

if (window.WebToApk && typeof window.WebToApk.widgetRequestPin === 'function') {
  if (!window.WebToApk.widgetRequestPin()) showManualSteps();
}

Fill the card first, then open the sheet, so the widget shows real content the moment it lands:

document.getElementById('add-widget').onclick = async function () {
  if (!(window.AppMint && AppMint.widget)) return;
  await AppMint.widget.set({ title: 'Streak', value: '12 days', lines: ['Best 30'], route: '/streak' });
  if (!(await AppMint.widget.requestPin())) {
    document.getElementById('widget-help').hidden = false; // manual steps
  }
};

Find out whether the user accepted: compare the placed count before the sheet and after they come back:

document.getElementById('add-widget').onclick = function () {
  var W = window.WebToApk;
  if (!W || typeof W.widgetRequestPin !== 'function') return;
  var before = W.widgetPlacedCount();
  if (!W.widgetRequestPin()) { showManualSteps(); return; }
  document.addEventListener('visibilitychange', function check() {
    if (document.visibilityState !== 'visible') return;
    document.removeEventListener('visibilitychange', check);
    if (W.widgetPlacedCount() > before) showToast('Widget added');
  });
};

Notes: true only means the sheet opened - the user can still cancel it. Use AppMint.widget.placedCount() later to see whether it was added. Fill the card with AppMint.widget.set() first, so the new widget is not empty.

widgetSet bridge#

window.WebToApk.widgetSet(cardJson: String): String

Saves the widget card [cardJson] `{title?, value?, lines?, route?, bg?, ink?, accent?}` and redraws every placed widget. Returns `{ok, placed}`.

Example

Internal plumbing behind AppMint.widget.set(card) - pages use that helper to fill the home-screen widget.

Returns: a JSON string {"ok":true,"placed":<number>} or {"ok":false,"error":"…"} (synchronously). Needs: nothing - every build has the widget.

The public API first:

if (window.AppMint && AppMint.widget) {
  AppMint.widget.set({ title: 'Steps', value: '8 214', lines: ['Goal 10 000'], route: '/' })
    .then(function (r) { if (!r.ok) console.log(r.error); });
}

The raw call takes the card as a JSON string and returns JSON text:

if (window.WebToApk && typeof window.WebToApk.widgetSet === 'function') {
  var card = { title: 'Steps', value: '8 214', lines: ['Goal 10 000'], route: '/',
               bg: '#15161A', ink: '#FFFFFF', accent: '#22D3EE' };
  var r = JSON.parse(window.WebToApk.widgetSet(JSON.stringify(card)));
  console.log(r.ok ? 'on ' + r.placed + ' home screen(s)' : r.error);
}

Keep the widget in step with a to-do list - the open count as the value, the first six tasks as lines, and a tap opens the tasks screen:

async function syncTaskWidget(tasks) {
  if (!(window.AppMint && AppMint.widget)) return;
  var open = tasks.filter(function (t) { return !t.done; });
  var r = await AppMint.widget.set({
    title: 'To do',
    value: open.length ? open.length + ' left' : 'All done',
    lines: open.slice(0, 6).map(function (t) { return '• ' + t.title; }),
    route: '/tasks'
  });
  if (!r.ok) console.warn('Widget not updated: ' + r.error);
}

Write the latest state when the user leaves the app; the raw call is synchronous, so it finishes before the page is paused:

document.addEventListener('visibilitychange', function () {
  if (document.visibilityState !== 'hidden') return;
  if (!window.WebToApk || typeof window.WebToApk.widgetSet !== 'function') return;
  var s = getTodayStats(); // your app's own numbers
  window.WebToApk.widgetSet(JSON.stringify({
    title: 'Today', value: s.spent + ' spent',
    lines: ['Budget ' + s.budget, 'Left ' + (s.budget - s.spent)], route: '/budget'
  }));
});

Notes: Card keys: title, value, lines (up to six), route (a / is added if missing), bg, ink, accent. Every placed widget redraws at once. placed is 0 when the user has not added the widget yet - the card is still saved and appears when they do.

Generated from the app runtime and its example files on every docs build. Read it as Markdown · All families.

Checked against the shipped bridge on 2026-11-04.