Flutter實戰之實現一個簡單的新聞閱讀器

catsuo發表於2018-03-18

Introduction

紙上談兵終覺淺,絕知此事要躬行。

catsao(音:cat騷),我擼的第一個Flutter Application Demo,目前階段它主要仿造目前主流的新聞資訊類app佈局,實現了簡單的新聞瀏覽。我還要繼續豐富它,讓它越來越sao。

Points

  • TabBar實現:
 @override
 Widget build(BuildContext context) {
   final List<Text> tabTexts = <Text>[
     new Text(
         StringResources.TAB_TOP_CN,
         style: new TextStyle(
             fontSize: 20.0
         )
     ),
     new Text(
         StringResources.TAB_SH_CN,
         style: new TextStyle(
             fontSize: 20.0
         )
     ),
     new Text(
         StringResources.TAB_GN_CN,
         style: new TextStyle(
             fontSize: 20.0
         )
     ),
     new Text(
         StringResources.TAB_GJ_CN,
         style: new TextStyle(
             fontSize: 20.0
         )
     ),
     new Text(
         StringResources.TAB_YL_CN,
         style: new TextStyle(
             fontSize: 20.0
         )
     ),
     new Text(
         StringResources.TAB_TY_CN,
         style: new TextStyle(
             fontSize: 20.0
         )
     ),
     new Text(
         StringResources.TAB_JS_CN,
         style: new TextStyle(
             fontSize: 20.0
         )
     ),
     new Text(
         StringResources.TAB_KJ_CN,
         style: new TextStyle(
             fontSize: 20.0
         )
     ),
     new Text(
         StringResources.TAB_CJ_CN,
         style: new TextStyle(
             fontSize: 20.0
         )
     ),
     new Text(
         StringResources.TAB_SS_CN,
         style: new TextStyle(
             fontSize: 20.0
         )
     )
   ];
   final List<Tab> tabs = [];
   for (int i = 0 ; i < tabTexts.length ; i++) {
     tabs.add(
       new Tab(
         child: tabTexts[i],
       )
     );
   }

   return new DefaultTabController(
       length: tabs.length,
       child: new Scaffold(
         appBar: new AppBar(
           title: new Text(
               widget.title
           ),
           bottom: new TabBar(
             isScrollable: true,
             tabs: tabs,

           ),
         ),
         body: new TabBarView(
             children: tabTexts.map((Text tab) {
               return new Center(
                 child: new NewsPage(
                   tabName: tab.data,
                 )
               );
             }).toList()
         ),
         drawer: new DrawerPage(),
       )
   );
 }
複製程式碼

主要通過DefaultTabController + TabBar + TabBarView三個Widget配合實現類似Android中ViewPager + Indicator的效果。

build方法返回的最外層是一個DefaultTabController Widget,正如它的名字那樣,它是一個控制器,用來控制TabBar和TabBarView的聯動。要在介面上繪製一個TabBar和其對應的內容頁使用TabBar和TabBarView兩個Widget來實現的。

通過配置Scaffold的appbar引數為介面新增一個標題欄,再配置AppBar的bottom引數為標題欄新增一個顯示在其底部的Widget,在這裡傳遞給bottom引數的是一個TabBar Widget,所以在標題欄下方會顯示一個TabBar。TabBar的每個Tab預設是固定位置的,這裡配置其isScrollable引數為true使其可滾動,tabs引數配置TabBar中的每個Tab Widget。

通過配置Scaffold的body引數為其新增主內容介面,這裡的內容介面需要配合TabBar的切換而切換,所以配置一個TabBarView Widget。最終通過外層的DefaultTabController使得TabBar和TabBarView聯動起來。

關於實現類似效果的更多詳細資訊可參閱DefaultTabControllerTabBarTabBarView

  • Drawer實現:
	return new DefaultTabController(
       length: tabs.length,
       child: new Scaffold(
         appBar: new AppBar(
           title: new Text(
               widget.title
           ),
           bottom: new TabBar(
             isScrollable: true,
             tabs: tabs,

           ),
         ),
         body: new TabBarView(
             children: tabTexts.map((Text tab) {
               return new Center(
                 child: new NewsPage(
                   tabName: tab.data,
                 )
               );
             }).toList()
         ),
         drawer: new DrawerPage(),
       )
   );
複製程式碼

Drawer的實現也很簡單,Flutter都已經封裝好了,直接配置Scaffold的drawer引數來實現一個Drawer介面。

  • 資料快取:

利用shared_preferences和sqflite外掛分別實現SharePreference和DB的資料本地快取,在專案的pubspec.yaml中配置依賴的外掛:

dependencies:

  ......
  sqflite: "^0.8.3"
  shared_preferences: "^0.4.0"
  ......
  
複製程式碼

然後就可以在使用的地方import相應的package來使用對應功能。

  • Column問題

使用Column時會遇到一個there are children with non-zero flex but the vertical constraints are unbounded的異常,大致意思是說Column內部有flex值不為0的children,但是當前Column在垂直方向的高度約束是無限制的。 簡單解釋一下,Column的children預設flex值是0,可以理解為flex為0的元件佔用的空間是靜態的。當一個元件的flex值不為0時,意味著它會去佔用父容器佈局完其他flex為0的元件後剩下的空間,佔用多少跟flex的值有關。這有點類似Android中LinearLayout的weight。所以這個異常的意思其實是說因為當前的Column的垂直方向的高度約束是沒有任何限制的,此時內部如果有flex非0的children的話它無法確定父容器的剩餘空間有多大。

當一個Column的children中有一個或多個Expanded或Flexible元件,並且這個Column巢狀在另一個Column或ListView的內部,就會遇到這個問題。

解決的思路其實就是檢查為什麼自己的Column此時在垂直方向的高度約束是無限制的,大部分情況下可能是由於當前Column巢狀在另一個Column或ListView等容器中導致的。

關於該問題的詳細描述可以參閱官方文件

Effect

Flutter實戰之實現一個簡單的新聞閱讀器

Flutter實戰之實現一個簡單的新聞閱讀器

Flutter實戰之實現一個簡單的新聞閱讀器

由於聚合資料只有獲取新聞列表的介面(也可能我沒找到單條新聞詳情的介面),所以目前點選新聞的跳轉是直接用瀏覽器開啟了其對應的url。另外聚合資料獲取新聞列表的介面貌似也沒有支援分頁引數...我還升級了聚合資料的高階會員,略坑。

Todo

  • 下拉重新整理
  • 增加不同頻道Route
  • 圖片本地快取

End

天氣介面:和風天氣。

新聞介面:聚合資料。

部分圖示:Iconfont-阿里巴巴向量圖示庫。

原始碼地址:Catsao

關於Flutter專案的編譯和執行可以參考官方文件

相關文章