Show multiple toasts in Ionic 5

Hi,

my component has this function:

async showToasts() {
    const toast = await this.toastController.create({
        message: "A",
        duration: 3000
    });
    toast.present();

    const toast2 = await this.toastController.create({
        message: "B",
        duration: 3000
    });
    toast2.present();

    const toast3 = await this.toastController.create({
        message: "C",
        duration: 3000
    });
    toast3.present();
}

I expect to see those toasts stacked one after another. But all toasts are shown at the same position overlying each other, so that they can`t be read by the user.

Is this expected? Is it possible to show the toasts stacked below each other?

Thank you!

We have an open feature request for something similar to this: feat: Implement toast waiting lists or toast stacking. · Issue #20389 · ionic-team/ionic-framework · GitHub

Hi. Thx for this. With the issue you kind-of dismissed the idea for not being md spec

Has this changed and/or how can the community act to get this feature implemented?

I think if were to implement this it would be in the form of a queue where only one toast is displayed at a time. Each additional toast would be displayed once the previous toast is dismissed.

In terms of displaying multiple toasts at once, that decision has not changed. The Material Design spec considers stacking toasts/snackbars to be an anti-pattern: Material Design

edit: The link does not go to the exact section, but if you look for “Consecutive snackbars” you should find the section I am referring to.

2 Likes

I created a queue controller for my app to display toasts one after another.

import { ToastButton } from '@ionic/core'
import { toastController as IonicToastController } from '@ionic/vue'

export enum ToastType {
    Info,
    Success,
    Warning,
    Error
}

export class ToastItem {
    message = ''
    type = ToastType.Info
    buttonText?: string | undefined
    duration = 3000
    buttonHandler?: (() => void) | undefined
}

export class ToastController {
    private toasts = Array<ToastItem>()
    private isProcessing = false

    public createWithJustMessage(message: string, type: ToastType): void {
        const item = new ToastItem()
        item.message = message
        item.type = type

        this.create(item)
    }

    public create(toast: ToastItem): void {
        this.toasts.push(toast)

        if (!this.isProcessing) {
            this.isProcessing = true

            this.process()
        }
    }

    private async process(): Promise<void> {
        const toast = this.toasts.shift()

        if (toast === undefined) {
            this.isProcessing = false

            return
        }

        const buttons = Array<ToastButton>()

        if (toast.buttonText) {
            buttons.push({
                text: toast.buttonText,
                handler: toast.buttonHandler,
            })
        }

        const toastController = await IonicToastController.create({
            message: toast.message,
            color: this.getColorFromType(toast.type),
            buttons: buttons,
            position: 'bottom',
            duration: toast.duration,
            cssClass: 'toasts-bottom',
        })

        toastController.onDidDismiss().then(() => {
            this.process()
        })

        await toastController.present()
    }

    private getColorFromType(type: ToastType): string {
        switch (type) {
            case ToastType.Success:
                return 'success'
            case ToastType.Warning:
                return 'warning'
            case ToastType.Error:
                return 'danger'
            case ToastType.Info:
            default:
                return 'primary'
        }
    }
}

To use:

// instances.ts
// Create a single instance somewhere to be used throughout the app
// Not sure if this is the best way to do this but it works for me
import { ToastController } from '@/utils/toast-controller'
export const toastControllerInstance = new ToastController()

// Use throughout the app
import { toastControllerInstance } from '@/instances'

toastControllerInstance.createWithJustMessage(
    'There was an issue loading your goals.',
    ToastType.Warning
)

So toasts don’t display on top of tabs, I added the toasts-bottom class.

/* Fix so toasts don't overlay the tabs */
.md .toasts-bottom {
    transform: translateY(-56px) !important;
}
.ios .toasts-bottom {
    transform: translateY(-50px) !important;
}
1 Like

Thank you all for than clarification and code example. My solution now is, to put the string together and show it in one toast. I wanted to show the user multiple toasts like “The item ‘abc’ could not be add because of reason”. I now combine the string to "The Items ‘abc’, ‘def’, ‘ghi’ could not be add because of reason’. Not perfect, but ok for me in that case.