Flutter學習之入門和體驗

1、前言

1.什麼是Flutter

上週個人一位微信好友問我有沒有學Flutter,我回答說還沒真正學,他說應該要接觸一下。對於新技術的誕生,我始終保持敬畏之心,和另外一位大學舍友聊了當時如何入坑Android的經歷,才發現本身的學習方式和路線有不少的問題,知識點很零亂,知識沒有系統化,很少說了,後面學習新的知識必定要從「碎片化」到「總體化」。2018年2月,在世界移動大會上,Google發佈了Flutter的第一個beta版本,2018年6月11日發佈首個預覽版,在2018年12月05日北京時間凌晨1點45分,在Flutter Live上,谷歌Flutter團隊推出Flutter1.0,Flutter1.0版本是UI工具包的第一個穩定版本,在2019年2月27日世界移動通訊大會上Google推出1.2版本,帶來全新的Web開發工具。另外今日頭條團隊即將開源讓Flutter真正支持View級別的混合開發(上層Flutter Framework引入Widget/LayerTree等概念本身實現了界面描述框架,下層Flutter Engine把LayerTree用OpenGL渲染成用戶界面),閒魚團隊也開源了基於 Redux數據管理的組裝式Flutter應用框架。什麼是Flutter呢?Flutter是一個跨平臺的免費開源的移動UI框架,是Google的移動應用SDK,用於在極短期內在iOS和Android平臺上建立高質量的原生體驗,簡而言之就是在iOS下和Android下共用一套代碼,一套代碼就能在兩個操做系統下運行,其官方編程語言是是Dart,學習這門語言很快就上手。Flutter提供不少豐富的UI組件庫,開發者能夠快速開發出靈活的UI界面,另外Flutter已經加入Material Design組件你們庭中,也就是說Flutter可使用Material Theming和Material 組件,能夠相信將來會有更多的創意設計UI風格會涌現。java

2.Flutter的特性

  1. 快速開發,Flutter的熱重載可幫助快速輕鬆試驗,構建UI,添加功能和快速修復錯誤,在iOS和Android的模擬器和硬件上體驗亞秒級重載同時不會丟失狀態,這裏直接引用官方的圖:
    熱重載圖
  2. 機具表現力和美觀UI,Flutter內置的Material Design和Cupertino(iOS風格)的部件、豐富的手勢API、天然平滑的滑動和不一樣的平臺表現來提高用戶體驗,直接看下圖:
    Flutter美觀UI圖
  3. 現代化響應式框架,使用現代化響應式框架和豐富的平臺、佈局和基礎組件來構建用戶界面,使用功能強大且靈活的API(針對2D,動畫,手勢,動效等)解決複雜的用戶界面設計。
class CounterState extends State<Counter> {
  int counter = 0;

  void increment() {
    // Tells the Flutter framework that state has changed,
    // so the framework can run build() and update the display.
    setState(() {
      counter++;
    });
  }

  Widget build(BuildContext context) {
    // This method is rerun every time setState is called.
    // The Flutter framework has been optimized to make rerunning
    // build methods fast, so that you can just rebuild anything that
    // needs updating rather than having to individually change
    // instances of widgets.
    return new Row(
      children: <Widget>[
        new RaisedButton(
          onPressed: increment,
          child: new Text('Increment'),
        ),
        new Text('Count: $counter'),
      ],
    );
  }
}
複製代碼
  1. 使用平臺原生功能及SDK,經過平臺API,第三方SDK和原生代碼讓英語具備強大的擴展性,Flutter容許重複使用現有的Java,Swift和Object代碼,並訪問iOS和Android上的原生功能和SDK,例以下圖獲取手機電量數值:
Future<Null> getBatteryLevel() async {
  var batteryLevel = 'unknown';
  try {
    int result = await methodChannel.invokeMethod('getBatteryLevel');
    batteryLevel = 'Battery level: $result%';
  } on PlatformException {
    batteryLevel = 'Failed to get battery level.';
  }
  setState(() {
    _batteryLevel = batteryLevel;
  });
}
複製代碼
  1. 統一應用開發體驗:Flutter豐富的工具庫可讓開發者輕鬆在iOS和Android設備上實現本身的想法,能夠充分利用現有的大部分Java、Object-C或者Swift代碼。

3.2019年Flutter產品線

2019年一月底,Flutter團隊公佈了2019年Flutter的產品線,爲如下幾點肯定了明確的計劃:android

  1. 核心基礎:Bug修復,性能調優,內存診斷工具優化,改進測試流程。
  2. 生態系統:更好的C/C++庫支持,推動官方開發/維護的Packages達到與核心框架相同質量和完整性。
  3. 動態更新:在國內,這是必不可少的,提供Android上的動態修復:代碼更新從服務器推送到Android應用裏,也就是熱更新。
  4. 支持開發移動端web應用。 而且Flutte UX研究團隊會按期根據用戶反饋來助力打造更優的Flutter,根據用戶反饋來調整開發重點,可見Flutter UX團隊的用心和付出程度,今年拭目以待。

2、Flutter架構

1.架構圖

Flutter框架從到下包含三部分:函數式響應的Framework(Dart),Engine(C++),Embedder(Platform Specific)。下面直接上圖: ios

官方架構圖

  • FrameWork是用Dart實現,提供了Material風格的小部件、Cupertino風格的小部件(用於iOS)、文本圖像按鈕組件、動畫、手勢等。
  • Engine時用C++實現的,上圖詳細了,實際包含三部分:Skia、Dart、Text。Skia是一個開源的2D圖形庫,爲硬件和軟件平臺提供API。Dart包括Dart運行時和垃圾回收。Flutter在調試模式下運行,由JIT(Just in Time,運行時編譯,邊運行邊編譯)支持,若是在發佈模式下,則是經過AOT(Ahead of Time,運行前編譯,普通的靜態編譯)將Dart代碼編譯原生的"arm"代碼。Text是指文本渲染庫。
  • Embedder是指嵌入器,就是可將Flutter嵌入到各類平臺中,它的主要任務是渲染Surface設置、線程設置和插件。

2.渲染過程

渲染過程一
渲染過程二
這裏結合上面兩張圖來看,能夠知道,當須要更新UI的時候,Framework通知Engine,Engine會等到下個Vsync信號到達的時候,會通知Framework,而後Framework會進行Animate,Build,Layout,Paint,最後生成Layer。Compositor 把全部的Layer組合成Scene,再經過Skia進行處理,最後Engine經過GPU GI接口提交數據給GPU,GPU最後通過處理後在顯示器顯示。上面簡單瞭解什麼Flutter,Flutter特性和框架,有了初步的瞭解,下面就開始一步一步實現第一個Flutter應用。

3、Flutter環境搭建

1.獲取Flutter SDK

務必電腦安裝git,本文是在Windows環境下配置的,MAC下配置環境流程是一致的。首先要獲取Flutter SDK,使用git去克隆倉庫而後添加Flutter工具到本身的環境變量,運行flutter doctor來顯示剩下須要安裝的依賴,國內用戶最好配置一下兩個環境變量:git

PUB_HOSTED_URL=https://pub.flutter-io.cn
複製代碼

以下圖所示: github

配置環境變量一

FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
複製代碼

如圖所示: web

配置環境變量二
由於此次我第一次在電腦上安裝Flutter,因此就要克隆這個遠程倉庫,我在電腦F盤下執行下面git命令:

$ git clone -b beta https://github.com/flutter/flutter.git
複製代碼

如圖所示: 編程

git命令下載Flutter SDK
下載比較慢,等了十多分鐘才下載完。下載完看到F盤果真有一個名字叫flutter的文件夾,接着將克隆下來的項目bin目錄配置到環境變量Path去,由於我是下載到F盤,因此下圖路徑是:
flutter_sdk路徑
若是須要更新flutter的sdk,在命令行執行 flutter upgrade命令便可。

2.運行flutter doctor

打開一個新的命令提示符窗口,運行下面命令flutter doctor,看是否須要安裝任何依賴項來完成安裝,這個命令會檢測環境和在終端生成報告,Dart SDK和Flutter捆綁在一塊兒,不必單獨去安裝Dart。初次運行它會下載本身的依賴庫而且自行編譯,可能比較慢。後續運行flutter命令就會很快。這裏注意,若是CMD窗口顯示亂碼問題,下面是解決方案:數組

  • Win+R進入進入CMD命令行輸入regedit進入註冊表
  • 找到 HKEY_CURRENT_USER\Console\%SystemRoot%_system32_cmd.exe 若是該項下已存在CodePage項,則把值改成十進制」65001」,若是不存在,在該項下新建一個 DWORD(32位值),命名爲「CodePage」,值設爲「65001」
  • 重啓cmd後生效
    flutter中文亂碼
    輸入flutter doctor運行結果截圖所示:
    flutter doctor第一次
    注意看到Android toolchain選項 意思是得要運行flutter doctor --android-licenses贊成協議才能夠安裝Android工具鏈。輸入flutter doctor --android-licenses一直按y便可。最後從新輸入flutter doctor,結果以下圖:
    flutter doctor最終結果
    從上面運行結果能夠知道一下信息:
  • Flutter的版本和渠道已經安裝
  • Flutter運行須要的Android工具鏈已安裝
  • Android studio開發工具已經安裝
  • flutter插件和Dart插件沒有安裝(在Android stdio安裝下面會說)
  • 沒有鏈接手機

3.搭建Android Studio開發環境

在Android Studio--File--Settings --plugins搜索Flutter便可,以下圖: 服務器

安裝Flutter插件
點擊安裝的時候會彈出一下提示框:
Dart插件安裝
大概意思是安裝Flutter插件須要Dart插件的支持,就是須要和Dart插件一塊兒安裝,安裝完成後,重啓開發工具,就能夠新建和開發程序了。 注意:若是提示cannot download xxxx的話去 plugins.jetbrains.com/搜索插件名字,下載對應的插件後解壓到對應的位置,以下圖:
插件位置
或者點擊插件本地安裝:
本地安裝插件
最後重啓本身的編譯器便可。

4、建立第一個Flutter程序

1.New Flutter Application

由於如今是要建立應用程序,所以在Create New Flutter Project下選擇Flutter Application微信

選擇應用程序

2.配置項目信息

在下一個頁面輸入項目名字,配置Flutter SDK目錄,項目存放的路徑,項目的描述,公司的域名,項目的包名如圖所示:

項目配置
最後一步
最後點擊finish等待Android Studio完成所要建立的Flutter項目了。

3.項目目錄

項目創建完成後,編輯器給咱們生成的目錄以下:

項目目錄

  • android:Andorid相關代碼目錄,裏面代碼配置和單首創建Andorid項目有些不同
  • ios:iOS相關代碼目錄,存放Flutter與ios原生交互的一些代碼
  • lib:應用源文件,dart文件,核心文件,能夠建立不一樣的文件夾,存放不一樣文件
  • test:測試文件
  • .gitignore:忽略文件,記錄一些不須要關注變動記錄的文件,就是不添加到版本記錄裏面
  • .metadata:記錄一些Flutter project一些基本信息,如版本,項目類型
  • .packages:記錄一些lib文件的路徑
  • .iml:是由IntelliJ IDEA建立的模塊文件,用於開發Java應用程序的IDE。它存儲有關開發模塊的信息,該模塊多是Java,Plugin,Android或Maven組件; 保存模塊路徑,依賴關係和其餘設置
  • pubspec.lock:這是根據當前項目依賴所生成的文件,記錄當前使用的依賴版本
  • pubspec.yaml:包含Flutter應用程序的包數據,這個是配置依賴項的文件,好比配置遠程public倉庫的依賴項,或者本地資源(圖片,音視頻)
  • README.md:根據意思就是「讀我」,這裏會記錄一些項目結構,詳細信息,幫助信息,瞭解一個項目都是一般經過這個文件入手。

整體來看和原生Android的工程結構不同了,由於代碼都是在lib目錄完成的,因此不能用Android多module多lib結構去建立module和lib,除非要用到原生交互的代碼,能夠在android目錄裏面去寫,而後在lib目錄裏面去引用。相對Android開發者而言,多了ios目錄,ios開發者而言,多了android目錄,其餘文件上面有具體詳細說明。由於lib目錄是開發者最主要關注的,打開lib目錄,發現有個後綴是dart的文件,裏面內容都是用dart語法來寫的,還發現有void main() => runApp(MyApp());main()函數對於學過java而言都很是清晰,這個應該是入口函數,另外發現StatelessWidget,StatefulWidget一些小控件,這裏想應該是UI組件吧。這裏先無論那麼多,直接點擊運行圖標,運行效果圖以下:

項目第一次運行效果圖
走到這裏,說明項目環境配置成功,併成功運行第一個簡單的程序。

5、編寫第一個應用

1.編寫「Hello world」

如今仍是按照新手走,先本身擼個「Hello world」出來吧。把main.dart中全部代碼去掉,替換下面代碼,屏幕中心顯示Hello World

import 'package:flutter/material.dart';

//這個是Dart中單行函數或者方法的簡寫
void main() => runApp(MyApp());

//程序繼承StatelessWidget,該應用程序成爲一個widget,在Flutter中,大多數東西都是widget
class MyApp extends StatelessWidget {
  // 這個是應用的根widget
  @override
  Widget build(BuildContext context) {
    //注意:一個app只能有一個MaterialApp
    return MaterialApp(
      //標題欄的名字
      title: 'Hello Flutter',
      //這個是Material library提供的一個widget,它提供了默認的導航欄、標題欄
      //包含主屏幕的widget樹的body屬性
      home:new Scaffold(
        appBar:new AppBar(
          title:const Text("Weclome to Flutter"), ), body:const Center( child:const Text("Hello World"), ), ), );
  }
}
複製代碼

Android模擬器運行效果以下:

Android模擬器效果
iOS模擬器運行效果以下:
iOS效果運行
感受在iOS運行效果比Android上好多了。看到這裏發現Android並非沉浸式狀態欄,而iOS默認就是沉浸式了,那有沒有辦法也能讓Android版本也作成沉浸式呢,答案是確定有的,一開始個人思路是應該是在main方法裏判斷是否是Android版本,若是是就設置,網上找裏找,這種方法果真可行:

  1. 首先先導包:
import 'dart:io';
import 'package:flutter/services.dart';
複製代碼
  1. 在mian方法根據Android版本作設置:
//入口函數
void main() {
  //MaterialApp組件渲染後
  runApp(MyApp());
  //判斷若是是Android版本的話 設置Android狀態欄透明沉浸式
  if(Platform.isAndroid){
    //寫在組件渲染以後,是爲了在渲染後進行設置賦值,覆蓋狀態欄,寫在渲染以前對MaterialApp組件會覆蓋這個值。
    SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle(statusBarColor: Colors.transparent);
    SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
  }
}
複製代碼

還有另一種方法,由於Flutter主入口只有一個MainActivity,也就是說全部的Flutter頁面都會運行這個MainActivity,那咱們只須要在這個主入口判斷一下版本號而後將狀態欄顏色設置成透明,具體位置在android->app->src->main->xxx->MainActivity:

public class MainActivity extends FlutterActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //設置狀態欄透明
    if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP)
    {//API>21,設置狀態欄顏色透明
      getWindow().setStatusBarColor(0);
    }
    GeneratedPluginRegistrant.registerWith(this);
  }
}
複製代碼

最終效果Android和iOS下運行以下:

沉浸式統一
這樣效果好不少了。另外發現,Android下的標題欄是左邊對齊的,那怎麼作成iOS那樣標題欄在中間呢?很簡單,在AppBar加上Center widget就能夠了,代碼以下:

appBar:new AppBar(
          //ios和android標題欄統一在中間
          title:new Center(child :const Text("Weclome to Flutter")), ), 複製代碼

最終效果以下:

ios和Android統一
這樣真正作到了iOS和Android版本統一了。上面代碼app繼承了StatelessWidget,這樣自己也成爲了widget,在Flutter中, 不少時候一切都看做是widget,layout和padding等等。上面例子結構很清晰明瞭,就是StatelessWidget類包含了(應用欄)Appbar,和構成主頁面widget樹結構的body屬性。而body的widget又包含了一個Center widget,Center widget又包含一個Text子widget,Center widget能夠將子widget對齊到屏幕中心。

2.使用外部package

如今,經過在pubspec.yaml文件配置依賴項,去依賴一個提供英文單詞的包,在pub.dartlang.org/flutter/這個網站能夠找到不少開源軟件包,能夠搜索指定的軟件包查看對應版本。

開源軟件包

  1. pubspec文件管理着Flutter應用程序的靜態資源文件,那在pubspec.yaml文件,將english_words(3.1.5版本)添加到依賴項,這裏要注意:和^之間是有空格的,不能會出錯。以下所示:
dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2
  english_words: ^3.1.5
複製代碼
  1. 這時候點擊編輯器的右上角的Packages get,就是把package拉取到項目中去,而且能夠看到控制檯輸出:Runningflutter packages getin flutter_demo...
    packages獲取
    控制檯輸出
  2. 在lib/main.dart下,將english_words導入,顯示灰色證實你導入的庫沒有使用,以下圖所示:
    english_words導入
  3. 改用英文單詞的package來生成文本,代碼改成以下:
//程序繼承StatelessWidget,該應用程序成爲一個widget,在Flutter中,大多數東西都是widget
class MyApp extends StatelessWidget {
  // 這個是應用的根widget
  @override
  Widget build(BuildContext context) {
    //隨機生成函數
    var wordPair = new WordPair.random();
    return MaterialApp(
      //標題欄的名字
      title: 'Hello Flutter',
      //這個是Material library提供的一個widget,它提供了默認的導航欄、標題欄
      //包含主屏幕的widget樹的body屬性
      home:new Scaffold(
        appBar:new AppBar(
          //ios和android標題欄統一在中間
          title: new Center(child: const Text("Weclome to Flutter")), ), body:new Center( //使用隨機生成的英文單詞 爲何不能const呢 由於 內容發生變化 const是用來修飾常量的 child:new Text(wordPair.asPascalCase), ), ), );
  }
}
複製代碼

注意的是Center widget 和 Text widget要用new 來建立,由於內容修改了,並非常量了,這時候按保存就能夠看到新的單詞出如今屏幕中間文本了。

3.添加有狀態的widget

上面MyApp是繼承StatelessWidget,StatelessWidget是無狀態的也是不可控的,意思是其屬性是不能改變的,全部的值都是最終的。在平時開發中,不少控件都須要根據特定場景改變自身的狀態,那麼Flutter有沒有提供有狀態的widget呢?答案確定是有的,Statefulwidget是有狀態的,在其生命週期保持的狀態可能會變化,實現一個有狀態的widget至少須要兩個類:StatefulWidgets類和State類。

  1. 添加RandomWordsWidget類,這個類繼承StatefulWidget類,這個類添加到main.dart文件最底下:
//建立有狀態的widget
class RandomWordsWidget extends StatefulWidget{

  @override
  State<StatefulWidget> createState() {
    return new RandomWordsState();
  }
}
複製代碼
  1. 添加RandomWordsState了,這個類會保存RandomWordsWidget的狀態,後面會保存一些特殊不一樣狀態下的詞組,而且在裏面添加build方法,否則會報錯,把生成單詞的代碼放到這個build類中去,以下圖:
//用來保存RandomWords widget的狀態
class RandomWordsState extends State<RandomWordsWidget>{
  @override
  Widget build(BuildContext buildContext){
    var wordPair = new WordPair.random();
    return new Text(wordPair.asPascalCase);

  }
}
複製代碼
  1. MyApp下改爲建立RandomWordsWidget便可:
//程序繼承StatelessWidget,該應用程序成爲一個widget,在Flutter中,大多數東西都是widget
class MyApp extends StatelessWidget {
  // 這個是應用的根widget
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      //標題欄的名字
      title: 'Hello Flutter',
      //這個是Material library提供的一個widget,它提供了默認的導航欄、標題欄
      //包含主屏幕的widget樹的body屬性
      home:new Scaffold(
        appBar:new AppBar(
          //ios和android標題欄統一在中間
          title: new Center(child: const Text("Weclome to Flutter")), ), body:new Center( child:new RandomWordsWidget() ), ), );
  }

複製代碼

這時候運行的效果仍是跟以前同樣,不過實現方式不同。

4.建立ListView

下面建立一個滑動組件ListView,開發者接觸最多應該是這個滑動組件,下面實現當用戶滑動列表的時候,ListView會不斷增加,不斷顯示新的單詞。

  1. 在RandomWordsState這個類建立一個數組列表,用來保存詞組,這個變量如下劃線爲開頭,Dart語言中下劃線前綴是表示強制私有
final _normalWords = <WordPair>[];
複製代碼
  1. 在RandomWordsState類添加_buildNormalWords方法,用於構建一個ListView,ListView提供了itemBuilder屬性,這是一個工廠builder做爲匿名函數進行回調,這個函數須要傳入兩個參數,一個是BuildContext上下文和行迭代器。對於ListView每一行都會執行這個函數調用,這裏想一想好像是平時Android開發中ListView建立添加item的方法。
//建立填充單詞的ListView
  Widget _buildNormalWorlds() {
    //內容上下16dp
    return new ListView.builder(
        padding: const EdgeInsets.fromLTRB(0, 16, 0, 16),
        //每一個單詞都會條約一次itemBuilder,而後將單詞添加到ListTile中
        itemBuilder: (context, i) {
          //首先建立10條單詞
          if (i >= _normalWords.length) {
            //接着再生成10個單詞,添加到列表上
            _normalWords.addAll(generateWordPairs().take(10));
          }
          return _buildItem(_normalWords[i]);
        });
  }
複製代碼
  1. 由於ListView是須要設置Item項的樣式,這裏一樣在RandomWordsState裏添加_buildItem方法,用來加載指定數據,這裏指定內容居中顯示:
//設置每一個item項的內容和樣式
  Widget _buildItem(WordPair pair) {
    return new ListTile(
      title: new Text(
        pair.asPascalCase,
        textAlign: TextAlign.center,
      ),
    );
  }
複製代碼
  1. 更改RandomWordsState的build方法,增長_buildNormalWords方法的調用,不是直接用單生成庫,代方法以下:
Widget build(BuildContext buildContext) {
// var wordPair = new WordPair.random();刪掉
// return new Text(wordPair.asPascalCase);刪掉
      return new Scaffold(
        appBar: new AppBar(
          title: new Center(child: const Text("Weclome to Flutter")), ), body:_buildNormalWorlds(), );
  }
複製代碼
  1. 更改MyApp的build方法,由於標題的設置放在了RandomWordsState裏了,因此不須要再額外添加,並將home變成RandomWords widget,代碼一會兒簡潔不少以下:
//程序繼承StatelessWidget,該應用程序成爲一個widget,在Flutter中,大多數東西都是widget
class MyApp extends StatelessWidget {
  // 這個是應用的根widget
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home:new RandomWordsWidget(),
    );
  }
}
複製代碼

最終效果如圖所示

flutter第一個列表

  1. 下面添加分割線,讓UI更美觀,這裏經過由於計算機裏循環開始都是從0開始的,我_buildNormalWorlds方法裏判斷是奇數項的話就構造出分割線添加到ListView,這裏注意分割線也是一項,那麼生成單詞的時候要稍微處理下,具體代碼以下:
//建立填充單詞的ListView
  Widget _buildNormalWorlds() {
    //內容上下16dp
    return new ListView.builder(
        padding: const EdgeInsets.fromLTRB(0, 16, 0, 16),
        //每一個單詞都會條約一次itemBuilder,而後將單詞添加到ListTile中
        itemBuilder: (context, i) {
          //添加分割線
          //奇數行,會添加一個分割線的widget,分割上下單詞
          //偶數行,就正常構造添加ListTitle row
          //構造一像素的分割線 也能夠本身去定義寬高
          //i.isOdd判斷是否奇數,奇數行添加分割線
          if(i.isOdd){
            //注意:這裏執行後會跳出循環
            return new Divider();
          }

          //這裏對2求商,就是計算出ListView中減去分割線後的實際單詞數量
          //如 i爲 1,2,3,4,5,那麼商結果是0,1,1,2,2
          //1 除以 2商是0 2除以2商是1 以此類推
          final int index = i ~/2;

          //首先建立10條單詞
          if (index >= _normalWords.length) {
            //接着再生成10個單詞,添加到列表上
            _normalWords.addAll(generateWordPairs().take(10));
          }
          return _buildItem(_normalWords[index]);
        });
  }
複製代碼

實際效果以下:

添加分割線後的ListView

5.添加交互

  1. 在RandomWordsState裏添加一個_collected的Set集合,這個集合用來存放收藏後的單詞,用Set的緣由是Set自己的特性不容許元素有重複值:
final Set<WordPair> _collected = new Set<WordPair>();
複製代碼
  1. _buildItem方法中添加isCollected來檢查單詞是否添加到收藏裏
final bool isCollected = _collected.contains(pair);
複製代碼
  1. _buildItem之後置屬性來添加一個❤️圖標到ListTiles,matrial包下有默認的❤️圖標,這裏就設置下顏色就能夠,代碼以下:
return new ListTile(
      title: new Text(
        pair.asPascalCase,
        textAlign: TextAlign.center,
      ),
      //trailing 是後置圖標屬性
      trailing: new Icon(
        //material 包下 icons.dart
        isCollected ? Icons.favorite : Icons.favorite_border,
        //圖標顏色設置
        color:isCollected ? Colors.red : null,
      ),
    );
複製代碼

運行結果後發現❤️型圖標添加到每一行最右邊處。

  1. 下面增長點擊事件,當點擊列表時,若是單詞沒被收藏,那麼就添加收藏,並將心形圖標改成收藏後的圖標,若是單詞被收藏了,那麼就移除處收藏,並將心形圖標變爲默認的心形圖標,代碼以下:
//trailing 是後置圖標屬性
      trailing: new Icon(
        //material 包下 icons.dart
        isCollected ? Icons.favorite : Icons.favorite_border,
        //圖標顏色設置
        color:isCollected ? Colors.red : null,
      ),

      //item的點擊事件屬性
      onTap:(){
        //狀態設置
        setState(() {
          //若是收藏了
          if(isCollected){
            //那就將set集合裏移除
            _collected.remove(pair);
          } else {
            //添加
            _collected.add(pair);
          }
        });

      }
複製代碼

效果以下:

增長點贊後效果

  1. 下面實現跳轉新頁面,首先添加一個收藏單詞頁面,在Flutter中,導航器管理應用程序的路由棧,什麼是路由棧呢?路由棧就是用來管理路由(頁面)。就是我如今要打開一個新的頁面,那麼這個新的頁面就會壓入路由棧中,若是我如今在新的頁面,點擊返回到上一個頁面,那麼路由棧會彈出這個新頁面路由,將顯示返回到前一個頁面,這裏想一想和Android原生用棧管理頁面的方法同樣,首先在首頁面增長跳轉新頁面圖標,代碼以下:
Widget build(BuildContext buildContext) {
      return new Scaffold(
        appBar: new AppBar(
          title: new Center(child: const Text("Weclome to Flutter")), //增長圖標和點擊事件動做 Icons.list是icon的類型 onPressed添加點擊事件 點擊會執行_collectWordsPage方法 actions:<Widget>[ new IconButton(icon: const Icon(Icons.list),onPressed: _collectWordsPage), ], ), body:_buildNormalWorlds(), );
  }
複製代碼

實際運行後,AppBar導航欄最右邊的位置添加了一個圖標,這時候點擊沒有任何反應,由於_collectWordsPage沒有任何代碼,下面添加跳轉到新頁面的實現。

  1. 在_collectWordsPage添加Navigator.push,這個簡單易懂,就是會把頁面入棧
//跳轉新頁面開始
  void _collectWordsPage(){
       Navigator.of(context).push(
         
         
       );
  }
複製代碼
  1. 下面添加MaterialPageRoute和builder,添加構建ListTile行的代碼,並添加項與項之間的分割線,最後經過toList來轉換。MaterialPageRoute是一種模態路由,能夠經過平臺自適應來切換屏幕,Android而言,頁面推送過渡向上滑動頁面,淡入淡出,彈出過渡則是向下滑動頁面。IOS而言,頁面從右側滑入,反向彈出,當另外一個頁面進入覆蓋時,該頁面向左移動。
//跳轉新頁面開始
  void _collectWordsPage() {
    Navigator.of(context).push(
      new MaterialPageRoute<void>(
        builder: (BuildContext context) {
          //傳遞收藏後的單詞
          final Iterable<ListTile> tiles = _collected.map((WordPair pair) {
            return new ListTile(
              title: new Text(
                pair.asPascalCase, //單詞
              ),
            );
          },
          );
        },

      ),
    );
  }
複製代碼
  1. 建立Scaffold,其中包含標題欄,body由ListView組成,每個Item項之間有一條分割線,運行發現,新的路由會自動添加返回按鈕,這是由於Navigator會在應用欄自動添加一個「返回」按鈕,不須要調用Navigator.pop,點擊返回按鈕會返回到主界面,代碼以下:
//跳轉新頁面開始
  void _collectWordsPage() {
    Navigator.of(context).push(
      new MaterialPageRoute<void>(
        builder: (BuildContext context) {
          //傳遞收藏後的單詞
          final Iterable<ListTile> tiles = _collected.map((WordPair pair) {
            return new ListTile(
              title: new Text(
                pair.asPascalCase, //單詞
              ),
            );
          },
          );
          //添加分割線開始
          final List<Widget> divided = ListTile.divideTiles(
            tiles: tiles,
            context:context,
          ).toList();
          return new Scaffold(
            appBar: new AppBar(
              title: new Center(child: const Text("Collect Words")), ), body: new ListView(children: divided), );
        },

      ),
    );
  }
複製代碼

最終運行效果發現標題並非居中對齊,由於默認的有邊距,這時候須要自定義AppBar就能夠,代碼以下:

return new Scaffold(
            appBar: new AppBar(
              titleSpacing: 0.0,
              //MaterialPageRoute這個自帶了返回鍵 下面的屬性設置取消返回鍵
              automaticallyImplyLeading:false,
              title: new Container(decoration: new BoxDecoration(color: new Color(0x00000000),
              ),
              child:new Stack(
                children: <Widget>[
                  new Container(
                    //左邊位置
                    alignment: Alignment.centerLeft,
                    child:new IconButton(
                      //圖標樣式
                      icon:new Icon(Icons.arrow_back),
                      //點擊事件
                      onPressed: (){
                        Navigator.pop(context);
                      }
                    ),
                  ),
                  new Center(child:new Text("Collect Words")),
                ],
              ),)
            ),
            body: new ListView(children: divided),
          );
複製代碼

最終運行效果以下,左邊是IOS,右邊是Android:

最終演示版

6、總結

入門Flutter的第一天,簡單知道了一下幾點:

  1. Flutter的特性和框架構成,它和大多數構建移動應用的工具不同,它是用本身的渲染引擎來繪製Widget,它中間層只有C/C++代碼,Flutter使用Dart語言實現系統的絕大部分功能(佈局,動畫,手勢)。
  2. 開發環境的搭建。
  3. 體驗Flutter添加UI的步驟。
  4. 路由(頁面)的跳轉。

如有錯誤,歡迎指正~

相關文章
相關標籤/搜索