[Bug] Navigation error on iOS, but not on Android
reigam opened this issue · 2 comments
The following basic navigation setup works perfectly well for me on android, but shows some strange behavior on iOS:
When I use 'Close' on the second page, everything works fine.
However, if I use the same msg on the third page, the view changes to an empty page with the title of the first page, then immediately changes to the title of the third page, still with an empty view.
Shared.fs;
type GlobalModel = {
PageStash: List<AppPages.Name>
}
module Helpers =
let rec reshuffle list: List<'a> =
match list with
| [] -> []
| l -> l |> List.rev |> List.tail |> List.rev
FirstPage.fs
let update msg (model: Model) (globalModel: GlobalModel) =
match msg with
| OpenPage s -> model, {globalModel with PageStash = List.append globalModel.PageStash [s]}, Cmd.none
let view (model: Model) (globalModel: GlobalModel) =
...
Button("Go To Second Page", OpenPage AppPages.names.SecondPage)
...
SecondPage.fs
let update msg (model: Model) (globalModel: GlobalModel) =
match msg with
| OpenPage s -> model, { globalModel with PageStash = List.append globalModel.PageStash [s] }, Cmd.none
| Close -> model, { globalModel with PageStash = [AppPages.names.FirstPage] }, Cmd.none
let view (model: Model) (globalModel: GlobalModel) =
....
Button("Go To Third Page", OpenPage AppPages.names.ThirdPage)
Button("Close All", Close)
...
ThirdPage.fs
let update msg (model: Model) (globalModel: GlobalModel) =
match msg with
| OpenPage s -> model, { globalModel with PageStash = List.append globalModel.PageStash [s] }, Cmd.none
| Close -> model, { globalModel with PageStash = [AppPages.names.FirstPage] }, Cmd.none
let view (model: Model) (globalModel: GlobalModel) =
...
Button("Close All", Close)
...
App.fs
let initModel =
{ Global = {
PageStash = [AppPages.names.FirstPage] }
LayoutsPage = fst (LayoutsPage.init())
FirstPage = fst (FirstPage.init())
SecondPage = fst (SecondPage.init())
ThirdPage = fst (ThirdPage.init())
}
let init () = initModel, Cmd.none
let update msg model =
match msg with
| LayoutsPageMsg m ->
let l, g, c = LayoutsPage.update m model.LayoutsPage model.Global
{ model with LayoutsPage = l; Global = g }, (Cmd.map LayoutsPageMsg c)
| FirstPageMsg m ->
let l, g, c = FirstPage.update m model.FirstPage model.Global
{ model with FirstPage = l; Global = g }, (Cmd.map FirstPageMsg c)
| SecondPageMsg m ->
let l, g, c = SecondPage.update m model.SecondPage model.Global
{ model with SecondPage = l; Global = g }, (Cmd.map SecondPageMsg c)
| ThirdPageMsg m ->
let l, g, c = ThirdPage.update m model.ThirdPage model.Global
{ model with ThirdPage = l; Global = g }, (Cmd.map ThirdPageMsg c)
| NavigationPopped ->
{ model with Global = { PageStash = Helpers.reshuffle model.Global.PageStash } }, Cmd.none
let view (model: Model) =
Application(
(NavigationPage(){
//View.map LayoutsPageMsg (LayoutsPage.view model.LayoutsPage model.Global)
for page in model.Global.PageStash do
match page with
|AppPages.Name "First Page" ->
let p = View.map FirstPageMsg (FirstPage.view model.FirstPage model.Global)
yield p
|AppPages.Name "Second Page" ->
let p = View.map SecondPageMsg (SecondPage.view model.SecondPage model.Global)
yield p
|AppPages.Name "Third Page" ->
let p = View.map ThirdPageMsg (ThirdPage.view model.ThirdPage model.Global)
yield p
| _ -> ()
})
.onBackNavigated(NavigationPopped)
)
full app can be viewed on:
https://github.com/reigam/MultiNavPageStashTemplate
(This is an older version, the behavior is the same with the newest version of fabulous and Xamarin).
Thanks for the report @reigam
XF.NavigationPage has always been buggy with Fabulous when popping or pushing several pages at once.
We really need to spend time to fix that.
In your example, the weird issue you're seeing is triggered by Helpers.reshuffle
.
This is because Xamarin.Forms will raise the BackNavigated
event, both if the user clicked the back button in the nav bar and if you popped a page programmatically.
The problem is that you already have updated the PageStash with the expected end state.
But because NavigationPopped
is triggered, Helpers.reshuffle will remove the last page (here "First Page") from the stack leaving it empty when it should not be possible.
Not sure why XF is not straight up throwing an exception here.
As a workaround while waiting for a proper fix, you could flag your PageStash as "ManuallyUpdated", so when NavigationPopped is called you can differentiate between the user clicking the back button of the NavigationPage or clicking the "Close all" button.
In the first case, you can pop a page from the PageStash. In the second case, you can keep the already updated stash.
Ok. I didn't consider, that the 'Close' Message also raises 'NavigationPopped'.
This works fine:
| NavigationPopped ->
if model.Global.PoppedByBackButton
then { model with Global = { PageStash = Helpers.reshuffle model.Global.PageStash
PoppedByBackButton = true }}, Cmd.none
else { model with Global = {model.Global with PoppedByBackButton = true}}, Cmd.none
| Close -> model, { globalModel with PoppedByBackButton = false
PageStash = [AppPages.names.FirstPage] }, Cmd.none
Thank you very much!