← Back to Policy Tracker guide
Toast Notifications, Replacing Notify()
Design decided from the mockups: option A (card with a coloured accent bar), sized up and moved to the top-left, overlapping the logo/title area on Main and the title on ViewItem — nothing clickable lives there, so covering it briefly when a toast fires doesn't block anything. Dismissible with an ×, auto-hides after ~3 seconds either way. Mockup for reference: toast style options (option A).
Main and ViewItem only — the two screens I have current, verified files for. Historic, Overview, and NewsletterPack also call Notify() and would need the exact same treatment, but I don't have fresh exports for those today and I'm not going to guess control names on them after today. Once you're happy with how this works on these two screens, post a fresh export of the other three and I'll do the same pass on them.1. Why this works across screens
The thing you were worried about — a "Saved" toast firing on Save & Close, then the screen changing to Main before anyone sees it — isn't actually a problem, and doesn't need any screen-aware logic. Two facts about how this app already behaves make it work automatically:
- Global variables survive
Navigate().varToastVisible/varToastHeading/varToastMessage/varToastTypeare app-wide state, not reset when the screen changes. Set them right beforeNavigate(Main, ...)and they're still set the instantMainappears. - Timers reset fresh on whichever screen is active. The auto-hide countdown doesn't get cut short mid-transition — it starts counting from zero the moment the destination screen's own copy of the Timer becomes active, so the user gets a full, undamaged view of the toast wherever they land.
So the rule is the same everywhere, no special-casing "Save" vs. "Save & Close" vs. "Delete": call the toast before whatever happens next, whether that's staying put or navigating. Every button that saves or deletes already does exactly that in that order today — the data has to be handled before the screen can leave. The only real work is duplicating the toast's visuals and its Timer onto every screen that needs one, using the same variable names everywhere.
2. Add the ShowToast() helper function
One small user-defined function, same pattern as the existing BuildLinksForSource — add it to App's Formulas property (the same place BuildLinksForSource already lives), anywhere in that property, doesn't need to go in a specific spot:
ShowToast(Heading: Text, Message: Text, Type: Text): Boolean = {
Set(varToastHeading, Heading);
Set(varToastMessage, Message);
Set(varToastType, Type);
Set(varToastVisible, true);
true
};Every place that currently calls Notify(...) becomes a single call like ShowToast("Saved", "Your changes have been saved.", "success") instead — one line, same shape everywhere, easy to scan for later. Type is just "success" or "error" for now (matching the two colours in the mockup); add more later if you want a distinct warning colour.
3. Build the toast on Main
This is a new top-level child of the Main screen itself, not nested inside ScreenContainer1 — the same pattern Main already uses for MyItemsBG/MyItems/SearchOverlay/TypeFilterOverlay1, which are also floating overlays positioned on top of everything. If it were nested inside ScreenContainer1 (an AutoLayout container), it would get pushed into the flow after the header instead of floating over it.
Select the Main screen itself in Tree view (not a control inside it), then paste this in — Studio will add it as a new top-level child, sitting on top of everything since it's last in the list:
- ToastNotification:
Control: GroupContainer@1.5.0
Variant: ManualLayout
Properties:
DropShadow: =DropShadow.Regular
Fill: =RGBA(255, 255, 255, 1)
Height: =56
RadiusBottomLeft: =10
RadiusBottomRight: =10
RadiusTopLeft: =10
RadiusTopRight: =10
Visible: =varToastVisible
Width: =430
X: =16
Y: =16
Children:
- ToastAccentBar:
Control: Rectangle@2.3.0
Properties:
Fill: =If(varToastType = "error", RGBA(194, 47, 47, 1), RGBA(11, 74, 54, 1))
Height: =Parent.Height
Width: =6
- ToastHeading:
Control: ModernText@1.0.0
Properties:
Color: =RGBA(22, 33, 28, 1)
Font: =Font.'Lato Black'
Height: =20
Size: =13
Text: =varToastHeading
Width: =Parent.Width - 60
X: =20
Y: =7
- ToastMessage:
Control: ModernText@1.0.0
Properties:
Color: =RGBA(91, 105, 97, 1)
Font: =Font.'Open Sans'
Height: =20
Size: =11
Text: =varToastMessage
Width: =Parent.Width - 60
X: =20
Y: =28
- ToastClose:
Control: Classic/Button@2.2.0
Properties:
BorderThickness: =0
Color: =RGBA(91, 105, 97, 1)
DisplayMode: =DisplayMode.Edit
Fill: =RGBA(0, 0, 0, 0)
Font: =Font.'Open Sans'
Height: =28
HoverFill: =RGBA(0, 0, 0, 0.05)
OnSelect: =Set(varToastVisible, false)
PressedFill: =RGBA(0, 0, 0, 0.08)
Size: =16
Text: ="×"
Width: =28
X: =Parent.Width - 34
Y: =6
- tmrToastHide:
Control: Timer@2.1.0
Properties:
AutoStart: =varToastVisible
Duration: =3000
OnTimerEnd: =Set(varToastVisible, false)
Repeat: =false
Start: =varToastVisible
Visible: =falseX=16, Y=16 matches ScreenContainer1's own padding, so the toast lines up flush with where the logo currently sits — it'll sit directly over the logo/"Policy Tracker"/V2 badge cluster when visible. Nudge Width/Height in the properties panel once you can see it live if it doesn't quite cover that whole cluster on your screen.
ToastNotification.Visible to =true in the formula bar, confirm it sits over the logo area and looks right, then change it back to =varToastVisible.4. Build the toast on ViewItem
Same component, same idea — a new top-level child of the ViewItem screen, matching the existing pattern already used there for DeleteItemModal1/DeleteLinkModal1/UnsavedChangesModal/DuplicateModal1/ThursdayPickerModal1, all of which are also floating screen-level overlays.
Select the ViewItem screen itself in Tree view, paste this in:
- ToastNotification:
Control: GroupContainer@1.5.0
Variant: ManualLayout
Properties:
DropShadow: =DropShadow.Regular
Fill: =RGBA(255, 255, 255, 1)
Height: =56
RadiusBottomLeft: =10
RadiusBottomRight: =10
RadiusTopLeft: =10
RadiusTopRight: =10
Visible: =varToastVisible
Width: =420
X: =16
Y: =20
Children:
- ToastAccentBar:
Control: Rectangle@2.3.0
Properties:
Fill: =If(varToastType = "error", RGBA(194, 47, 47, 1), RGBA(11, 74, 54, 1))
Height: =Parent.Height
Width: =6
- ToastHeading:
Control: ModernText@1.0.0
Properties:
Color: =RGBA(22, 33, 28, 1)
Font: =Font.'Lato Black'
Height: =20
Size: =13
Text: =varToastHeading
Width: =Parent.Width - 60
X: =20
Y: =7
- ToastMessage:
Control: ModernText@1.0.0
Properties:
Color: =RGBA(91, 105, 97, 1)
Font: =Font.'Open Sans'
Height: =20
Size: =11
Text: =varToastMessage
Width: =Parent.Width - 60
X: =20
Y: =28
- ToastClose:
Control: Classic/Button@2.2.0
Properties:
BorderThickness: =0
Color: =RGBA(91, 105, 97, 1)
DisplayMode: =DisplayMode.Edit
Fill: =RGBA(0, 0, 0, 0)
Font: =Font.'Open Sans'
Height: =28
HoverFill: =RGBA(0, 0, 0, 0.05)
OnSelect: =Set(varToastVisible, false)
PressedFill: =RGBA(0, 0, 0, 0.08)
Size: =16
Text: ="×"
Width: =28
X: =Parent.Width - 34
Y: =6
- tmrToastHide:
Control: Timer@2.1.0
Properties:
AutoStart: =varToastVisible
Duration: =3000
OnTimerEnd: =Set(varToastVisible, false)
Repeat: =false
Start: =varToastVisible
Visible: =falseViewItem's top bar also has two conditional badges (High Priority / Top Newsletter Item) that appear around X=191 and X=435 when those flags are ticked. Most of the time neither is showing, so the toast at X=16–436 has clear space. If a toast fires while both badges happen to be visible at once, it'll sit on top of the first one briefly. Rare enough not to be worth engineering around today — flagging it so it's not a surprise if you see it, not proposing a fix for it yet.Visible: =true, check it sits cleanly over the "Policy Tracker" title without covering the Save/Close buttons on the right, then revert to =varToastVisible.5. Convert your first three Notify() calls REAL EXAMPLES FROM YOUR CURRENT FILE
These three cover exactly the cases you were asking about — a plain Save that stays on screen, a Save & Close that navigates away, and a Delete that also navigates away. Same swap pattern each time: replace the Notify(...) line(s) with one ShowToast(...) call, keep everything else in the formula untouched.
btnSave1 (plain Save, stays on ViewItem) — find these two lines:
Notify("Saved successfully!", NotificationType.Success),
Notify("Save failed. Please check your connection and try again.", NotificationType.Error)Replace with:
ShowToast("Saved", "Your changes have been saved.", "success"),
ShowToast("Save failed", "Check your connection and try again.", "error")btnSaveClose1 (Save & Close, navigates to Main or Historic) — find:
Notify("Saved successfully!", NotificationType.Success);
If(varSourceScreen = "Historic", Navigate(Historic, ScreenTransition.Fade), Navigate(Main, ScreenTransition.Fade)),
Set(varSaving, false);
Notify("Save failed. Please check your connection and try again.", NotificationType.Error)Replace with:
ShowToast("Saved", "Your changes have been saved.", "success");
If(varSourceScreen = "Historic", Navigate(Historic, ScreenTransition.Fade), Navigate(Main, ScreenTransition.Fade)),
Set(varSaving, false);
ShowToast("Save failed", "Check your connection and try again.", "error")The toast call stays before Navigate(...), same position as the old Notify() was — that ordering is exactly what makes it show up correctly on the destination screen, per item 1 above.
btnModalAction1 (Delete confirm, navigates to Main) — find:
Notify("Delete failed. The item may still exist. Please check and try again.", NotificationType.Error),
UpdateContext({locDisplayDeletePopUp1: false});
ClearCollect(colUnpublishedPolicies, Filter('Policy Proof Tracker', Published = false));
Notify("Item deleted successfully.", NotificationType.Success);Replace with:
ShowToast("Delete failed", "The item may still exist, please check and try again.", "error"),
UpdateContext({locDisplayDeletePopUp1: false});
ClearCollect(colUnpublishedPolicies, Filter('Policy Proof Tracker', Published = false));
ShowToast("Item deleted", "The item was removed successfully.", "success");6. Converting the rest SAME PATTERN, YOUR OWN PACE
ViewItem has 20 Notify() calls total, Main has 4 — the three above are the highest-traffic ones. For every remaining one, same swap:
| Old | New |
|---|---|
Notify("some message", NotificationType.Success) | ShowToast("Heading", "some message", "success") |
Notify("some message", NotificationType.Error) | ShowToast("Heading", "some message", "error") |
Notify("some message", NotificationType.Warning) | ShowToast("Heading", "some message", "error") — no separate warning colour yet, error accent reads close enough until you want a third colour |
Pick a short 1-2 word Heading that matches what the message is about (skim the existing message text for the gist — "Link saved successfully!" becomes heading "Link saved", message "The document link was saved.", etc.). No need to do them all in one sitting — Notify() and ShowToast() can coexist in the app indefinitely while you convert call sites gradually, they don't interfere with each other.
Notify( to see what's left — or just ask me to check next time you post a fresh export, and I'll list exactly which ones remain.