Python版Flutter「flet」の画面遷移が謎すぎた

# fletの画面遷移
pythonのGUI超新星(？)であるfletのappbarを利用した画面遷移を備忘録として残します。

[fletのdocsはこちら](https://flet.dev/docs/)

## めちゃくちゃわかりにくい
公式でもルーティングの例とか挙げてくれてるんやけども、なんせ分かりづらい。
[わかりにくい例](https://flet.dev/docs/guides/python/navigation-and-routing#building-views-on-route-change)

```python:main.py

    def route_change(route):
        page.views.clear()
        page.views.append(
            View(
                "/",
                [
                    AppBar(title=Text("index")),
                    ElevatedButton("Visit Doutonbori", on_click=lambda _: page.go("/doutonbori")),
                ],
            )
        )
        if page.route == "/doutonbori" or page.route == "/doutonbori/takoyaki":
            page.views.append(
                View(
                    "/doutonbori",
                    [
                        AppBar(title=Text("Doutonbori")),
                        ElevatedButton("Search Takoyaki", on_click=lambda _: page.go("/doutonbori/takoyaki")),
                    ],
                )
            )

        if page.route == "/doutonbori/takoyaki":
            page.views.append(
                View(
                    "/doutonbori/takoyaki",
                    [
                        AppBar(title=Text("Takoyaki")),
                        Text("たこ焼き食べたい"),
                    ],
                )
            )
        page.update()

    def view_pop(view):
        page.views.pop()
        top_view = page.views[-1]
        page.go(top_view.route)

    page.on_route_change = route_change
    page.on_view_pop = view_pop
    page.go(page.route)
```

ちょっと分かり難く（やばさを分かりやすく）してみた
`page.go(page.route)`で`route_change`を呼んでるわけですけど、
`page.views.clear()`で全部消してから指定されたルートまで再構築してるんですよね。

で、なんでこうなるかというと、`pop()`で取り払う時に
`page.views.pop()`で`page.views`で最後のviewを取り払い、
`top_view = page.views[-1]`で最後のviewを取得して、
`page.go(top_view.route)`で最後のページに飛ぶ
という動作になっていて、
`view_pop`では、１つ手前のページのrouteを取得したに過ぎないんですね。
この状態で`page.views.clear()`をしないとページがダブってしまうようです。

`page.views.pop()`で最後のページ取り払ってるんじゃ？
って思ったんですが、どうやら`page.update()`をしないと、popしたことが反映されていない模様。

`page.update()`をすると、その時点で1つ前にもどるので、
`page.views.clear()`で全部取っ払わなくてもいいんじゃないかと思いまして。

じゃあ、`route_change`で指示されたViewを開くだけでいいんじゃないかと

```python:main.py

    def route_change(route):
        if page.route == '/':
            page.views.append(
                View(
                    "/",
                    [
                        AppBar(title=Text("index")),
                        ElevatedButton("Visit Doutonbori", on_click=lambda _: page.go("/doutonbori")),
                    ],
                )
            )
        if page.route == "/doutonbori":
            page.views.append(
                View(
                    "/doutonbori",
                    [
                        AppBar(title=Text("Doutonbori")),
                        ElevatedButton("Search Takoyaki", on_click=lambda _: page.go("/doutonbori/takoyaki")),
                    ],
                )
            )

        if page.route == "/doutonbori/takoyaki":
            page.views.append(
                View(
                    "/doutonbori/takoyaki",
                    [
                        AppBar(title=Text("Takoyaki")),
                        Text("たこ焼き食べたい"),
                    ],
                )
            )
        page.update()

    def view_pop(view):
        page.views.pop()
        page.update()

    page.on_route_change = route_change
    page.on_view_pop = view_pop
    page.go(page.route)
```

うんうん、やっぱりこっちのほうがスムーズ！
・・・あれ？indexのappbarにも戻るボタンがあるぞ・・・
・・・ポチ

全Viewが消せる仕様が爆誕したのでした！

じゃあ、メインビューはあらかじめpageに用意しておかないといけないってことね。
とうことで、

```python:main.py
    page.appbar = AppBar(title=Text("index"))
    page.controls.append(ElevatedButton("Visit Doutonbori", on_click=lambda _: page.go("/doutonbori")))


    def route_change(route):
        if page.route == "/doutonbori":
            page.views.append(
                View(
                    "/doutonbori",
                    [
                        AppBar(title=Text("Doutonbori")),
                        ElevatedButton("Search Takoyaki", on_click=lambda _: page.go("/doutonbori/takoyaki")),
                    ],
                )
            )

        if page.route == "/doutonbori/takoyaki":
            page.views.append(
                View(
                    "/doutonbori/takoyaki",
                    [
                        AppBar(title=Text("Takoyaki")),
                        Text("たこ焼き食べたい"),
                    ],
                )
            )
        page.update()

    def view_pop(view):
        page.views.pop()
        page.update()

    page.on_route_change = route_change
    page.on_view_pop = view_pop
    page.go(page.route)
```

おお！これで完成ではないか！
必要な分だけ追加して、不要なものだけ取り除く！
なんだったんだ、あの分かり難い例は！
わっはっは！



## はい、こっから泥沼
完成したかと思いきや、
最後のページまで行って最初に戻る、問題ない。
だけど、1ページだけ進んで戻ると、ボタンが押せない。。。押せないいいい！

どういうわけか、1ページだけ進んで戻ると、進むために押したボタンが効かなくなってしまう模様。
ボタンを複数用意したら違うボタンは機能する（ただし戻ると、今度はそのボタンがロックされる）
特にエラーログも吐いてないから、対処の仕様がない。

この不具合を理解できず、いろんなところにprint置いて動作を検証しながら四苦八苦
結局理由は分からんので、結果だけ載せると、

```diff_python
     def view_pop(view):
         page.views.pop()
+        if len(page.views) > 1:
             page.update()
+        else : page.go('/')
```

`page.views`が2枚以上あるときは`page.update()`
残1枚のときは`page.go('/')`で矯正する荒治療で解決しました。
こんなんでええんやろか・・・
というか、そもそもこんな標準的な機能は元から搭載してほしいよ？
まさかこんなところでハマると思ってなかったので、備忘録として残しました。
もし、訪れた方がいたら温かい目で見てください。


全文
```python:main.py
from flet import *

def main(page: Page):
    page.title = "Osaka ni Oide"

    page.appbar = AppBar(title=Text("index"))
    page.controls.append(ElevatedButton("Visit Doutonbori", on_click=lambda _: page.go("/doutonbori")))


    def route_change(route):
        if page.route == "/doutonbori":
            page.views.append(
                View(
                    "/doutonbori",
                    [
                        AppBar(title=Text("Doutonbori")),
                        ElevatedButton("Search Takoyaki", on_click=lambda _: page.go("/doutonbori/takoyaki")),
                    ],
                )
            )

        if page.route == "/doutonbori/takoyaki":
            page.views.append(
                View(
                    "/doutonbori/takoyaki",
                    [
                        AppBar(title=Text("Takoyaki")),
                        Text("たこ焼き食べたい"),
                    ],
                )
            )
        page.update()

    def view_pop(view):
        page.views.pop()
        if len(page.views) > 1:
            page.update()
        else : page.go('/')

    page.on_route_change = route_change
    page.on_view_pop = view_pop
    page.go(page.route)


if __name__ == '__main__':
    app(target=main)
```