{shinyMobile}
is built on top of the framework7 template (V5.7.14) and has
different purposes:
{shinyMobile}
offers 3 skins:
It automatically detects if the app is running with android or iOS and accordingly adapts the layout. It is of course possible to apply the iOS skin on an android device and inversely, although not recommended.
{shinyMobile}
also provides 2 themes, namely light and
dark.
{shinyMobile}
brings 3 out of the box layouts:
f7SingleLayout()
: develop simple apps
(best choice for iOS/android Apps).f7TabLayout()
: develop complex
multi-tabbed apps (best choice for iOS/android
Apps).f7SplitLayout()
: for tablets and
desktop with a sidebar, navbar and a main panel{shiny}
{shinyMobile}
has its own custom input widgets with
unique design for each skin (iOS/android/aurora). Below we summarise all
known shiny inputs and their equivalent with
{shinyMobile}
.
Features (sample) | shiny | shinyMobile |
---|---|---|
Range slider | sliderInput() |
f7Slider() |
Text input | textInput() ,
textAreaInput() |
f7Text() , f7Password() ,
f7TextArea() |
Checkbox | checkboxInput() ,
checkboxGroupInput() |
f7Checkbox() ,
f7CheckboxGroup() |
Radio | radioButtons() |
f7Radio() |
Toggle switch | ❌ (see {bslib} ) |
f7Toggle() |
Numeric | numericInput() |
f7Stepper() |
Select | selectInput() |
f7Select() , f7SmartSelect() ,
f7Picker() |
Autocomplete | ❌ | f7AutoComplete() |
Action button | actionButton() |
f7Button() f7Fab() |
Date | dateInput() ,
dateRangeInput() |
f7DatePicker() |
Color | ❌ | f7ColorPicker() |
Download | downloadButton() |
f7DownloadButton() |
It is the main template skeleton.
options sets up the app look and feel (See dedicated
section below). f7Page()
accepts any
{shinyMobile}
layout (See below).
This is an option if you decide not to embed a
f7SubNavbar()
. The toolbar is the right place to add
f7Button()
, f7Link()
, f7Badge()
…
Its location is controlled with the position parameter (either top or
bottom).
f7Toolbar(
...,
position = c("top", "bottom"),
hairline = TRUE,
shadow = TRUE,
icons = FALSE,
scrollable = FALSE
)
Under the hood, f7Tabs()
is a custom
f7Toolbar()
.
Panels are also called sidebars, f7Panel()
being the
corresponding function.
f7Panel(
...,
id = NULL,
title = NULL,
side = c("left", "right"),
theme = c("dark", "light"),
effect = c("reveal", "cover"),
resizable = FALSE
)
f7Panel()
has a theme option,
regardless of the main app theme. For instance, it is entirely possible
to create a dark f7Panel()
while the page theme is light,
and conversely. Its behavior is controlled via the effect argument:
The resizable argument allows to dynamically resize the panel.
Note that for the moment, there is no option to control the width of each panel.
As stated previously for the f7SplitLayout()
, the
f7Panel()
may also be considered as a sidebar. In that
case, we may include f7PanelMenu()
. Finally do not forget
to set up the f7Navbar()
so that panels are allowed!
This choice is crucial when you are developing an App. It depends on
the complexity of your visualizations and content. If your plan is to
develop a simple graph or table, you should go for the
f7SingleLayout()
option. For more complex design, the best
is f7TabLayout()
. f7SplitLayout()
is specific
for tablets and desktop apps.
f7SingleLayout()
is dedicated to build simple, one-page
apps or gadgets.
While only the navbar is mandatory, other components such as the
toolbar are optional for the f7SingleLayout()
.
The app below runs with specific options:
library(shiny)
library(shinyMobile)
library(apexcharter)
library(dplyr)
library(ggplot2)
data("economics_long")
economics_long <- economics_long %>%
group_by(variable) %>%
slice((n()-100):n())
shinyApp(
ui = f7Page(
title = "My app",
options = list(dark = FALSE, filled = FALSE, theme = "md"),
f7SingleLayout(
navbar = f7Navbar(
title = "Single Layout",
hairline = TRUE,
shadow = TRUE
),
toolbar = f7Toolbar(
position = "bottom",
f7Link(label = "Link 1", href = "https://www.google.com"),
f7Link(label = "Link 2", href = "https://www.google.com")
),
# main content
f7Shadow(
intensity = 16,
hover = TRUE,
f7Card(
title = "Card header",
apexchartOutput("areaChart")
)
)
)
),
server = function(input, output) {
output$areaChart <- renderApexchart({
apex(
data = economics_long,
type = "area",
mapping = aes(
x = date,
y = value01,
fill = variable
)
) %>%
ax_yaxis(decimalsInFloat = 2) %>% # number of decimals to keep
ax_chart(stacked = TRUE) %>%
ax_yaxis(max = 4, tickAmount = 4)
})
}
)
Choose this layout to develop complex multi-tabbed apps (best choice for iOS/android Apps).
The … argument requires
f7Tabs(..., id = NULL, swipeable = FALSE, animated = TRUE)
.
The id argument is mandatory if you want to exploit the
updateF7Tabs()
function. f7Tabs()
expect to
have f7Tab(..., tabName, icon = NULL, active = FALSE)
passed inside.
The app below runs with specific options:
library(shiny)
library(shinyMobile)
library(apexcharter)
poll <- data.frame(
answer = c("Yes", "No"),
n = c(254, 238)
)
shinyApp(
ui = f7Page(
options = list(dark = FALSE, filled = FALSE, theme = "md"),
title = "My app",
f7TabLayout(
panels = tagList(
f7Panel(title = "Left Panel", side = "left", theme = "light", "Blabla", effect = "cover"),
f7Panel(title = "Right Panel", side = "right", theme = "dark", "Blabla", effect = "cover")
),
navbar = f7Navbar(
title = "Tabs",
hairline = TRUE,
shadow = TRUE,
leftPanel = TRUE,
rightPanel = TRUE
),
f7Tabs(
animated = TRUE,
#swipeable = TRUE,
f7Tab(
title = "Tab 1",
tabName = "Tab1",
icon = f7Icon("folder"),
active = TRUE,
f7Shadow(
intensity = 10,
hover = TRUE,
f7Card(
title = "Card header",
apexchartOutput("pie")
)
)
),
f7Tab(
title = "Tab 2",
tabName = "Tab2",
icon = f7Icon("keyboard"),
active = FALSE,
f7Shadow(
intensity = 10,
hover = TRUE,
f7Card(
title = "Card header",
apexchartOutput("scatter")
)
)
),
f7Tab(
title = "Tab 3",
tabName = "Tab3",
icon = f7Icon("layers_alt"),
active = FALSE,
f7Shadow(
intensity = 10,
hover = TRUE,
f7Card(
title = "Card header",
f7SmartSelect(
"variable",
"Variables to show:",
c("Cylinders" = "cyl",
"Transmission" = "am",
"Gears" = "gear"),
openIn = "sheet",
multiple = TRUE
),
tableOutput("data")
)
)
)
)
)
),
server = function(input, output, session) {
# river plot
dates <- reactive(seq.Date(Sys.Date() - 30, Sys.Date(), by = input$by))
output$pie <- renderApexchart({
apex(
data = poll,
type = "pie",
mapping = aes(x = answer, y = n)
)
})
output$scatter <- renderApexchart({
apex(
data = mtcars,
type = "scatter",
mapping = aes(
x = wt,
y = mpg,
fill = cyl
)
)
})
# datatable
output$data <- renderTable({
mtcars[, c("mpg", input$variable), drop = FALSE]
}, rownames = TRUE)
}
)
f7SplitLayout()
is the third layout introduced with
{shinyMobile}
, similar to sidebarLayout
with
{shiny}. This template is focused for tablet/desktop use. It is composed
of a sidebar, and a main panel.
The main content goes in the … parameter. Navigation
items are gathered in the sidebar slot. The sidebar expect a
f7Panel()
. Importantly, the side parameter must be set to
left
and the style to reveal
. The navigation
menu is organized as follows:
f7PanelMenu(
id = "menu",
f7PanelItem(
tabName = "tab1",
title = "Tab 1",
icon = f7Icon("email"),
active = TRUE
),
f7PanelItem(
tabName = "tab2",
title = "Tab 2",
icon = f7Icon("home")
)
)
The id argument is important if you want to get the
currently selected item or update the select tab. Each
f7PanelItem()
has a mandatory tabName. The
associated input will be input$menu
in that example, with
tab1
for value since the first tab was set to an active
state. To adequately link the body and the sidebar, you must wrap the
body content in f7Items()
containing as many
f7Item()
as sidebar items. The tabName
must correspond.
library(shiny)
library(ggplot2)
library(shinyMobile)
library(apexcharter)
library(thematic)
fruits <- data.frame(
name = c('Apples', 'Oranges', 'Bananas', 'Berries'),
value = c(44, 55, 67, 83)
)
thematic_shiny(font = "auto")
new_mtcars <- reshape(
data = head(mtcars),
idvar = "model",
varying = list(c("drat", "wt")),
times = c("drat", "wt"),
direction = "long",
v.names = "value",
drop = c("mpg", "cyl", "hp", "dist", "qsec", "vs", "am", "gear", "carb")
)
shinyApp(
ui = f7Page(
title = "My app",
options = list(
theme = "aurora",
dark = TRUE,
filled = FALSE,
color = "#007aff",
touch = list(
tapHold = TRUE,
tapHoldDelay = 750,
iosTouchRipple = FALSE
),
iosTranslucentBars = FALSE,
navbar = list(
iosCenterTitle = TRUE,
hideNavOnPageScroll = TRUE
),
toolbar = list(
hideNavOnPageScroll = FALSE
),
pullToRefresh = FALSE
),
f7SplitLayout(
sidebar = f7Panel(
title = "Sidebar",
side = "left",
theme = "light",
f7PanelMenu(
id = "menu",
f7PanelItem(
tabName = "tab1",
title = "Tab 1",
icon = f7Icon("equal_circle"),
active = TRUE
),
f7PanelItem(
tabName = "tab2",
title = "Tab 2",
icon = f7Icon("equal_circle")
),
f7PanelItem(
tabName = "tab3",
title = "Tab 3",
icon = f7Icon("equal_circle")
)
),
uiOutput("selected_tab"),
effect = "reveal"
),
navbar = f7Navbar(
title = "Split Layout",
hairline = FALSE,
shadow = TRUE
),
toolbar = f7Toolbar(
position = "bottom",
f7Link(label = "Link 1", href = "https://www.google.com"),
f7Link(label = "Link 2", href = "https://www.google.com")
),
# main content
f7Items(
f7Item(
tabName = "tab1",
f7Button("toggleSheet", "Plot parameters"),
f7Sheet(
id = "sheet1",
label = "Plot Parameters",
orientation = "bottom",
swipeToClose = TRUE,
backdrop = TRUE,
f7Slider(
"obs",
"Number of observations:",
min = 0, max = 1000,
value = 500
)
),
br(),
plotOutput("distPlot")
),
f7Item(
tabName = "tab2",
apexchartOutput("radar")
),
f7Item(
tabName = "tab3",
f7Toggle(
inputId = "plot_show",
label = "Show Plot?",
checked = TRUE
),
apexchartOutput("multi_radial")
)
)
)
),
server = function(input, output, session) {
observeEvent(input$toggleSheet, {
updateF7Sheet(id = "sheet1")
})
observeEvent(input$obs, {
if (input$obs < 500) {
f7Notif(
text = paste0("The slider value is only ", input$obs, ". Please
increase it"),
icon = f7Icon("bolt_fill"),
title = "Alert",
titleRightText = Sys.Date()
)
}
})
output$radar <- renderApexchart({
apex(
data = new_mtcars,
type = "radar",
mapping = aes(
x = model,
y = value,
group = time)
)
})
output$selected_tab <- renderUI({
HTML(paste0("Access the currently selected tab: ", strong(input$menu)))
})
output$distPlot <- renderPlot({
dist <- rnorm(input$obs)
hist(dist)
})
output$multi_radial <- renderApexchart({
if (input$plot_show) {
apex(data = fruits, type = "radialBar", mapping = aes(x = name, y = value))
}
})
}
)
This is where you can customize the global app behavior:
options = list(
theme = c("ios", "md", "auto", "aurora"),
dark = TRUE,
filled = FALSE,
color = "#007aff",
touch = list(
tapHold = TRUE,
tapHoldDelay = 750,
iosTouchRipple = FALSE
),
iosTranslucentBars = FALSE,
navbar = list(
iosCenterTitle = TRUE,
hideOnPageScroll = TRUE
),
toolbar = list(
hideOnPageScroll = FALSE
),
pullToRefresh = FALSE
)
As stated above, you may choose between 3 skins and 2 color themes.
There is a third option called filled that allows to
fill the navbar and toolbar if enabled. The color
options simply changes the color of elements such as buttons, panel
triggers, tabs triggers, … {shinyMobile}
brings a lot of
different colors. hideOnPageScroll allows to hide/show
the navbar and toolbar which is useful to focus on the content. The
tapHold parameter ensure that the “long-press” feature
is activated. preloader is useful in case you want to
display a loading screen. Framework7 has many more options
which can be passed through this options parameter.