Today’s tutorial demonstrates how to create a sliding panel that has many uses, ranging from games to business applications. You might build an adventure game where the player’s inventory needs to slide on the screen, then hide itself when the user is done. Or you might build a menu that slides in when the player taps the infamous “hamburger” menu icon. Yet another possibility is sliding notices in and out.

As with the previous UI tutorials on navigation panels and text fields, this tutorial will extend the widget library, creating a new widget called widget.newPanel(). Let’s look at the entire function:

function widget.newPanel( options )
    local customOptions = options or {}
    local opt = {}

    opt.location = customOptions.location or "top"

    local default_width, default_height
    if ( opt.location == "top" or opt.location == "bottom" ) then
        default_width = display.contentWidth
        default_height = display.contentHeight * 0.33
        default_width = display.contentWidth * 0.33
        default_height = display.contentHeight

    opt.width = customOptions.width or default_width
    opt.height = customOptions.height or default_height

    opt.speed = customOptions.speed or 500
    opt.inEasing = customOptions.inEasing or easing.linear
    opt.outEasing = customOptions.outEasing or easing.linear

    if ( customOptions.onComplete and type(customOptions.onComplete) == "function" ) then
        opt.listener = customOptions.onComplete
        opt.listener = nil

    local container = display.newContainer( opt.width, opt.height )

    if ( opt.location == "left" ) then
        container.anchorX = 1.0
        container.x = display.screenOriginX
        container.anchorY = 0.5
        container.y = display.contentCenterY
    elseif ( opt.location == "right" ) then
        container.anchorX = 0.0
        container.x = display.actualContentWidth
        container.anchorY = 0.5
        container.y = display.contentCenterY
    elseif ( opt.location == "top" ) then
        container.anchorX = 0.5
        container.x = display.contentCenterX
        container.anchorY = 1.0
        container.y = display.screenOriginY
        container.anchorX = 0.5
        container.x = display.contentCenterX
        container.anchorY = 0.0
        container.y = display.actualContentHeight

    function container:show()
        local options = {
            time = opt.speed,
            transition = opt.inEasing
        if ( opt.listener ) then
            options.onComplete = opt.listener
            self.completeState = "shown"
        if ( opt.location == "top" ) then
            options.y = display.screenOriginY + opt.height
        elseif ( opt.location == "bottom" ) then
            options.y = display.actualContentHeight - opt.height
        elseif ( opt.location == "left" ) then
            options.x = display.screenOriginX + opt.width
            options.x = display.actualContentWidth - opt.width
        end self, options )

    function container:hide()
        local options = {
            time = opt.speed,
            transition = opt.outEasing
        if ( opt.listener ) then
            options.onComplete = opt.listener
            self.completeState = "hidden"
        if ( opt.location == "top" ) then
            options.y = display.screenOriginY
        elseif ( opt.location == "bottom" ) then
            options.y = display.actualContentHeight
        elseif ( opt.location == "left" ) then
            options.x = display.screenOriginX
            options.x = display.actualContentWidth
        end self, options )

    return container

For consistency, this new widget will follow the coding standard of the other widgets in the Corona open source widget library. Like other widgets, we begin by passing in a table of parameters necessary to create the panel (lines 5 through 27). These include:

  • location — where the panel originates from (left, right, top, or bottom).
  • width — the width of the panel.
  • height — the height of the panel.
  • speed — how fast the panel shows and hides (slides in or out).
  • inEasing — the transition easing method used when the panel shows.
  • outEasing — the transition easing method used when the panel hides.
  • onComplete — a function to execute when the panel completes showing and hiding.

All parameters are optional and are set to reasonable defaults. The location parameter is a string value of either "left", "right", "top" or "bottom" that defaults to "top". The speed parameter defaults to 500 milliseconds. inEasing and outEasing are linear by default but can be set to any of the standard easing methods.

onComplete is optional and defaults to nil. In the above example, we actually create two completion listeners, one for each of the actions the panel supports (panel:show() and panel:hide()).

Code notes

Let’s step through the function code and inspect some important aspects:

  • When adding the code to define the listener (lines 23-27), it’s helpful to make sure that a parameter was passed, but also that it’s a function and not some other variable type.
  • If a panel slides in from the left or right, it’s reasonable to take up the full height of the visible screen, but the width would need to be specific to the app. Panels coming in from the top or bottom may take up the full width of the device but be limited to 1/3 of the screen.
  • Line 29 creates a display.newContainer(). This is similar to display.newGroup(), except that its bounds are automatically clipped to the defined width and height. This container object is returned on line 95 to the calling function.
  • The panel is positioned off screen based on the location specified. Since there are many ways to set up the size and scale in config.lua, this function uses display.screenOriginX and display.screenOriginY to reference the left and top of the screen. Likewise, display.actualContentWidth and display.actualContentHeight represents the right and bottom edges of the screen. By setting the anchor point to the side of the container that faces the content area, we can use these values to position it. The other value will simply center the container along that edge.
  • To show and hide the panel, two methods are included named show() and hide(). Lines 53-93 implement these two functions. The panels will be shown or hidden using a simple call, but first we need to determine where to move the panel to. If we’re sliding the panel in/out from the top or bottom, we need to transition the y value. If we’re sliding it in/out from either side, we need to use the x value. Again, we use the defined points for the sides of the screen and either add or subtract the width or height of the panel.

Using the “widget.newPanel()”

To use the panel, simply create a new object, in this case panel, and above that, the optional listener function that’s specified as the onComplete parameter:

local function panelTransDone( target )
    native.showAlert( "Panel", "Complete", { "Okay" } )
    if ( target.completeState ) then
        print( "PANEL STATE IS: " )

local panel = widget.newPanel{
    location = "top",
    onComplete = panelTransDone,
    width = display.contentWidth * 0.8,
    height = display.contentHeight * 0.8,
    speed = 250,
    inEasing = easing.outBack,
    outEasing = easing.outCubic

When done, panel will be the container object to which we can add whatever content desired using the object:insert() call. To keep things more organized, display objects can optionally be added directly to the panel object:

panel.background = display.newRect( 0, 0, panel.width, panel.height )
panel.background:setFillColor( 0, 0.25, 0.5 )
panel:insert( panel.background )

panel.title = display.newText( "menu", 0, 0, native.systemFontBold, 18 )
panel.title:setFillColor( 1, 1, 1 )
panel:insert( panel.title )

Showing and hiding

When desired, we can show or hide the panel by simply calling the proper method:


Composer notes

If you’re using Composer, you may not want to insert the panel into the Composer group because the panel is generally intended to slide over anything else in the scene. However, if you choose to add it to the scene’s view, it should be the last thing inserted into the scene, or else you should call object:toFront() to move it to the top of the scene’s view.

On the general topic of Composer, you may ask “shouldn’t this be done with an overlay?”. While that is a valid approach, overlays are individual scenes and it takes more effort to construct and deconstruct a scene. In contrast, widget.newPanel() yields a simple slide-in/out unit which can be used with or without Composer.

In summary

This tutorial should get you started with implementing basic sliding panels in your app. With some clever adjustments to the various parameters, you can easily implement a wide variety of panels that appear from different sides of the screen and utilize unique easing transitions.

To use this widget in your own projects, please download the code from our GitHub repository.

Share this post....Share on Facebook0Share on Google+6Tweet about this on TwitterShare on LinkedIn0
    • Rob Miracle says:

      Sometimes we add new features to the core and write tutorials to help you understand how to use them. Other times like this, it’s a matter of showing you how to do things and give you an ah-ha moment and empower you to build the features you need.

      We are in the process of adding it to the community code and to our GitHub repository for those who want to fork the project and customize it to their needs.

  1. Hi Rob,

    Can you change the location of entry on the fly? Meaning is it possible to say rotate the entry from left clockwise to bottom each time panel:show() is called by setting the location dynamically?


    • Some programmers might want to have background items touchable. It’s easy to fix that. Had we done a Composer overlay scene, you would have had a chance to set the .isModal flag. You can expand this to include your own version of .isModal by having a full screen invisible rectangle that has it’s .isHitTestable property set to true and then add a touch and tap handler to it that consumes them.


      • Hi Rob

        I am not sure where should I put “.isModal = true” above code to make the background items untouchable?

        Please, advise.

        Thank you.


        • Rob Miracle says:

          The sliding panel as written does not support .isModal. That’s a feature using Composer. If you want to implement something similar to .isModal, simply put a touch and tap handler on the panels’s background that throws the touch or tap away.

    • I answer myself … yes :)

      panel.title = display.newBitmapText(“Old text”, screenCenterX, screenCenterY, “lobster0”, 42)
      panel:insert( panel.title )


      panel.title:setText(“New text”)

      Works :)

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>