前言
在上一篇文章Flutter系列:2.實現一個簡單的登入介面通過一個簡單的登入頁面帶入了Flutter中頁面構建的方式以及一些簡單控制元件的使用;在開發一個app前首要的任務往往是搭建app需要的基礎結構,比如底部選單,路由導航,網路請求以及一些常用的顏色、圖示、按鈕、toast元件等。
本次的demo將實現一個簡單的app所需的基礎結構,實現一個簡單的app,基於底部TabBar的方式模組切分,實現網路層呼叫豆瓣api展示電影列表,任意介面登入驗證,app如下圖。
TabBar選單
目前app設計中大部分app都是由底部TabBar選單+頂部導航資訊的方式構建的,在iOS開發中UITabBarController 和 UINavigationController 幾乎是APP的標配, 同樣在Flutter中基於Scaffold的構建方式也直接提供了appBar+body+bottomNavigationBar的方式來切分導航欄、內容和底部選單,所以我們只需要在首頁的Scaffold構造中傳入bottomNavigationBar即可。
在Flutter中為我們提供了material design風格的BottomNavigationBar和iOS風格的CupertinoTabBar,我們只需要選擇其一稍作封裝即可,本demo選擇CupertinoTabBar,並封裝到BottomNavWidget中,相關細節請看原始碼。
body的切換
雖然Scaffold提供了appBar+body+bottomNavigationBar的組合,但是並沒有實現bottomNavigationBar點選切換body頁面顯示功能,所以需要開發者自己去處理bottomNavigationBar的點選回撥來動態切換body中的內容。
不同的bottomNavigationBarItem對應著不同的顯示頁面,電影tabBar對應顯示電影列表頁面,發現tabBar對應顯示發現頁面... 他們都在body中,他們之間有著頻繁的切換但同時只能顯示一個頁面;基於此使用Stack佈局的方式來實現,每個頁面組成一個陣列成為Stack Widget的children並快取避免重複建立,使用Offstage元件來包裝每個tab頁面,並將bottomNavigationBar當前選擇的index對應的頁面的offstage設定為false, 這樣只有當前選擇的tab對應的頁面顯示在body中,而其他的介面並不會顯示也不會接收事件佔用空間。
路由導航
路由導航也是app常見的基礎功能,伺服器通過下發路由資訊可以實現動態的控制app的頁面跳轉,常用於動態頁面,push和web跳轉。
Flutter中的導航有點類似iOS的方式,都是通過棧的方式來管理路由頁面。Navigator就是Flutter中管理導航路線的Widget,注意Navigator管理的是頁面導航的路線,稱為Route的東西而不是像iOS中直接管理的controller,而每個Route(CupertinoPageRoute)則可以通過builder來指定顯示的Widget,同時Navigator也提供了對Route 棧操作的方法,push和pop。
Navigator管理的物件是Route,Flutter提供了MaterialPageRoute和iOS風格的CupertinoPageRoute,MaterialPageRoute是根據手機平臺自動調整頁面的出現動畫,本Demo選用CupertinoPageRoute以從右到左的頁面出現動畫,然後指定其builder即可實現頁面的跳轉。
MaterialApp內建了一個頂層的導航器Navigator,routes屬性支援配置靜態的路由表,如果在routes中找不到對應的路由配置時則呼叫onGenerateRoute來支援動態的路由跳轉,它的定義如下:
所以我們需要通過一個函式來實現MaterialApp的onGenerateRoute就可以根據RouteSettings中的路由資訊動態的生成頁面的Route,同時以Uri的方式來指定Route的名稱就可以實現動態傳參了,具體詳見Demo原始碼中RouteManager類。登入註冊
登入註冊頁面可能在app的任何頁面推出,同時可能不支援返回需要強制登入的情況,在iOS中常常以present的方式出現,所以在Flutter中需要指定CupertinoPageRoute的fullscreenDialog屬性為true即可
頁面的跳轉
在iOS的開發中基於UITabBarController 和 UINavigationController的構建方式中頁面跳轉是在UINavigationController內跳轉的,同時通過設定Controller的hidesBottomBarWhenPushed屬性支援動態的顯示和隱藏底部的TarBar, 每個TabBar對應的是一個獨立控制的UINavigationController,他們各自有自己路由的導航棧,在Flutter中提供的CupertinoTabScaffold通過為每個TabBar指定顯示為CupertinoTabView來實現了同樣的機制。
往往在開發中進入二級介面後底部的導航欄都是隱藏的,所以我們完全可以只使用MaterialApp內建的頂層Navigator來實現我們的導航控制,本Demo也是如此。
網路請求
移動端的網路環境是千變萬化的,所以app的網路請求應該是一個非同步的過程,不能阻塞主執行緒,本Demo是基於Dart的第三方Http網路請求庫dio。
dio是一個強大的Dart Http請求庫,支援Restful API、FormData、攔截器、請求取消、Cookie管理、檔案上傳/下載、超時等...
網路層實現了通過dio請求到網路資料然後反序列為Model物件,dart中的json反序列化要比其他語言麻煩,藉助的是json_annotation這個庫。
從請求api到回撥再到反序列為Model物件這個過程都應該是一個非同步的過程,所以他們返回的都是一個Futrure物件,使用Completer就可以很方便的生成一個Future, 然後在恰當的時候傳入資料或者錯誤來結束這個Future。
列表
列表的展示是基於FutureBuilder的方式,因為其依賴api請求返回的future,當future的狀態變更時FutureBuilder會接收到最新的快照資訊AsyncSnapshot,通過其當前快照來控制ListView或者CircularProgressIndicator的顯示。
其他
App開發中還有許多其他的基礎模組,比如和原生通訊元件(channel)、圖片元件、日誌元件、其他公共的彈窗、上下拉重新整理元件等,本Demo還來不及一一實現,隨著學習的深入以後再慢慢總結吧,有不妥的地方還望指正。
Demo原始碼地址:[GitHub原始碼傳送]