1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

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

Last updated at Posted at 2023-05-10

fletの画面遷移

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

fletのdocsはこちら

めちゃくちゃわかりにくい

公式でもルーティングの例とか挙げてくれてるんやけども、なんせ分かりづらい。
わかりにくい例

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では、1つ手前のページのrouteを取得したに過ぎないんですね。
この状態でpage.views.clear()をしないとページがダブってしまうようです。

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

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

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

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に用意しておかないといけないってことね。
とうことで、

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置いて動作を検証しながら四苦八苦
結局理由は分からんので、結果だけ載せると、

     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('/')で矯正する荒治療で解決しました。
こんなんでええんやろか・・・
というか、そもそもこんな標準的な機能は元から搭載してほしいよ?
まさかこんなところでハマると思ってなかったので、備忘録として残しました。
もし、訪れた方がいたら温かい目で見てください。

全文

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)
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?