Implicit sorting in tidyr::spread and dplyr::summarise

You can sort by name based on the order in the original data frame:

gather(df, Var, Val, V1:V2) %>% 
  unite(VarG, Var, group) %>% 
  spread(VarG, Val) %>%
  arrange( order(match(name, df$name)))

#   name V1_g1 V1_g2 V2_g1 V2_g2
# 1    B    10    40     6     3
# 2    A    20    30     1     7

The order is taken from the order of the factor levels.

str(df)
'data.frame':   4 obs. of  4 variables:
 $ name : Factor w/ 2 levels "A","B": 2 2 1 1
 $ group: Factor w/ 2 levels "g1","g2": 1 2 1 2
 $ V1   : num  10 40 20 30
 $ V2   : num  6 3 1 7

See that the levels are "A","B".

So if you set the order of the levels to the order they are shown in it will work:

df = data.frame(name=c("B","B","A","A"),
                group=c("g1","g2","g1","g2"),
                V1=c(10,40,20,30),
                V2=c(6,3,1,7))

df %>% 
    mutate(name = factor(name,levels=unique(name))) %>% 
    mutate(group = factor(group,levels=unique(group))) %>% 
    gather(Var, Val, V1:V2) %>% 
    unite(VarG, Var, group) %>% 
    spread(VarG, Val)

Results in:

  name V1_g1 V1_g2 V2_g1 V2_g2
1    B    10    40     6     3
2    A    20    30     1     7

tidyr::pivot_wider(), the recommanded replacement of tidyr::spread() since tidyr 1.0.0, keeps the rows in order so you can do :

library(tidyr)

df = data.frame(name=c("B","B","A","A"),
                group=c("g1","g2","g1","g2"),
                V1=c(10,40,20,30),
                V2=c(6,3,1,7))

pivot_wider(df, names_from = "group", values_from = c("V1", "V2"))
#> # A tibble: 2 x 5
#>   name  V1_g1 V1_g2 V2_g1 V2_g2
#>   <fct> <dbl> <dbl> <dbl> <dbl>
#> 1 B        10    40     6     3
#> 2 A        20    30     1     7

Created on 2019-09-14 by the reprex package (v0.3.0)