Flutter 和 Dart 開發中一些技巧和坑點填坑指南詳解

在這裏插入圖片描述

開篇引入一個 Flutter 的所跨平臺的圖片。圖中大概列了一下 Flutter 未來所跨的平臺, Flutter 有 Google Flutter 技術團隊的不斷迭代開發,相信Flutter將是未來的主流跨平臺開發框架。本節課筆者將會根據近一段時間的 Flutter 的開發實踐和技術研究實踐進行一個總結和整理,將 Flutter 和 Dart 開發中遇到的一些難點、問題、技巧和解決方案給大家分享一下,避免大家遇到相同的問題耽誤時間,降低開發和學習成本,做到事半功倍、提升開發和學習效率。接下來大家一起學習吧。本文主要介紹:

  • Flutter 和 Dart 簡介
  • Flutter 和 Dart 開發實踐中的技巧和難點
  • Flutter 和 Dart 的未來展望

Flutter 和 Dart 簡介

在這裏插入圖片描述
Flutter 是 Google 推出並開源的移動端開發框架(基於 Dart 語言)。Dart 是由 Google 開發的一種面向對象編程的強類型語言,語法有點像 Java 與 JavaScript 的集合體。Flutter 使用 Skia 作爲2D渲染引擎,也就是我們看到的界面是通過 Skia 進行繪製渲染出來的。Skia 是 Google 的一個2D圖形處理函數庫,並且 Skia 是跨平臺的。目前 Google Chrome 瀏覽器和Android均採用Skia作爲其繪圖引擎。由於 Android 系統已經內置了 Skia ,所以 Flutter 在打包 Android 應用時不需要再將 Skia 打入 APK 中,但 IOS 系統並未內置 Skia ,所以構建 IOS 應用時,也必須將 Skia 一起打包,這也是爲什麼 Flutter 應用的 Android 安裝包比IOS的安裝包小的主要原因。

其實目前的鴻蒙系統也是類似 Flutter 和 Fuchsia OS,只不過 Flutter 和 Dart 推出的比較早,技術也比較成熟。未來的 Flutter 將全面跨主流平臺:Android、IOS、Windows(研發中)、Mac(研發中)、Linux(研發中)、Fuchsia OS(研發中)、Web(研發中)、物聯網系統(研發中)、後端、前端等等。
其中很多人期待的Web SDK應該今年內左右可能會發佈一個比較完善的版本。

Flutter 有 Google 團隊的支持,目前開發者數量也指數級在上升。目前很多大公司都有應用 Flutter 進行項目的開發,如 Google 、阿里巴巴、京東、騰訊、 Square 等公司都有應用。Flutter的獨特的優勢已經吸引了大量的開發者和公司進行學習和使用,並且Flutter的社區、文檔、SDK更新頻率等相關資源越來越完善和強大,我們有理由去學習Flutter和Dart。希望大家通過本門課程有很大的提升和收穫,一起努力,一起學習,一起進步。
在這裏插入圖片描述
目前 Flutter 的最新版本是1.7:
在這裏插入圖片描述
Flutter 基礎架構圖:
在這裏插入圖片描述
Flutter 詳細架構圖:
在這裏插入圖片描述
Flutter 跨平臺特點:
目前我們在開發應用時,需要同時兼容 iOS 和 Android 兩種平臺時有兩種技術選擇:走原生開發路線,把界面和邏輯在不同平臺分別實現;抑或用同一套代碼兼容多個平臺,但這往往意味着運行速度和產品體驗的損失。除了原生外,目前跨平臺技術一般是混合開發,如採用H5、React Native、Weex、小程序等技術實現跨平臺應用。不過這些或多或少都能感覺到卡頓和體驗不流暢,並且開發和學習成本非常高,而且都有各自的侷限性。Flutter的出現就是爲我們提供了一套兩全其美的解決方案:既能用原生代碼直接調用的方式來加速圖形渲染和 UI 繪製,又能同時運行在兩大主流移動操作系統上,並且體驗和流暢度和原生基本一致、開發效率也非常高、學習難度和成本低。那麼接下來看下幾種方案的對比情況:

技術 性能 開發效率 渲染方式 學習成本 可擴展性
Flutter 高,接近原生體驗 Skia高性能自繪引擎 低,Widget組件化 高,採用插件化的庫進行擴展
RN/Weex/小程序 有延遲,一般 一般,複雜、效率低 Js驅動原生渲染 高,複雜 一般
原生應用 一般 原生渲染 高,需要學習Android和IOS原生API

從上面的對比可以看出,Flutter的優勢明顯:高體驗度、高開發效率、低學習成本、高可擴展性、未來Google Flutter團隊還將使Flutter支持PC和Web的跨平臺開發等。 在Flutter 1.0正式版本尚未推出之前,已經有成百上千的基於 Flutter 開發的應用在 Apple Store 和 Google Play 上架,相信Flutter將會被越來越多的開發者和公司所採用和接受。

最後我們回顧下2018年和2019年Flutter的發展情況:

  • 2 月底在世界移動大會 (MWC) 上宣佈了第一個 Beta 版發佈;
  • 5 月的 Google I/O 大會上發佈了 Beta 3;
  • 6 月底的 GMTC 宣佈了首個發佈預覽版;
  • 9 月的谷歌開發者大會 (Google Developer Days) 上,我們宣佈 發佈預覽版 2 發佈。
  • 12 月宣佈發佈正式穩定1.0版;
  • 2019年 2 月宣佈發佈穩定版1.2版本SDK。
  • 2019年 5 月宣佈發佈穩定版1.5版本SDK。

目前Flutter的社區非常活躍,Flutter 在 Github 最受歡迎的開源軟件中排名前 50,國內也有大量的開發者開始使用 Flutter 構建跨平臺 (Android & iOS) 的應用,如:阿里巴巴、騰訊、京東等都使用 Flutter 發佈了自己的應用。Google官方Flutter團隊計劃Flutter未來也將支持Flutter Web和Flutter PC的應用移植開發,讓我們拭目以待吧!

好了,關於 Flutter 的詳細介紹我們就講到這裏,大家應該都對 Flutter 和 Dart 都非常熟悉了。接下來咱們就開門見山,把遇到的一個一個的問題和對應的解決方案進行講解分享。

Flutter 和 Dart 開發實踐中的技巧和難點

本文講解使用的是 Windows 10 開發環境,編輯器 IDE 使用 Android Studio 和 Visual Studio Code 。

1、SDK下載和升級慢?

可以看到主要有dev、beta和stable三個官方分支,這裏正式開發的話可以下載stable穩定版本。遇到SDK升級和下載慢的話,可以使用配置國內鏡像環境變量:
可以將如下的國內下載鏡像地址加入到環境變量中:

變量名:PUB_HOSTED_URL,變量值:https://pub.flutter-io.cn
變量名:FLUTTER_STORAGE_BASE_URL,變量值:https://storage.flutter-io.cn

Flutter SDK環境變量,將Flutter的bin目錄加入環境變量即可:

[你的Flutter文件夾路徑]\flutter\bin

配置完後,可以使用flutter doctor命令,它可以幫助我們檢查Flutter環境變量是否設置成功,Android SDK是否下載以及配置好環境變量等等。如果有相關的錯誤提示,根據提示進行修復和安裝、設置即可。每次運行這個命令,都會幫你檢查是否缺失了必要的依賴。通過運行 flutter doctor 命令來驗證你是否已經正確地設置了,並且可以自動更新和下載相關的依賴。

2、模擬器使用不方便,Android模擬器無法連接安裝應用?

如遇到Error connecting to the service protocol:HttpException: Connection closed before full header was received,uri= http://127.0.0.1:1076/...類似的錯誤的話,一般是由於你使用了Android Q(API 29)的模擬器導致的,目前Android Q模擬器對Flutter支持有些問題,所以建議遇到這個問題的話,使用Anroid Q(API 29)以下的版本的模擬器。

模擬器置頂。
我們的Android原生模擬器是支持置頂的:
在這裏插入圖片描述
勾選就可以置頂了。

模擬器快速啓動。
當我們通過Android Studio的AVD Manager新建了一個模擬器後,我們後續就可以通過建立一個bat文件快速啓動模擬器了。在這個bat文件中寫入啓動模擬器的命令,這樣每次啓動模擬器直接運行這個bat文件即可:

D:\Sdk\emulator\emulator.exe -avd Pixel_XL_API_28

模擬器所在的SDK目錄根據你的實際情況位置修改即可。使用時雙擊這個bat文件就可以運行模擬器了。

3、Flutter 和 Dart 升級出錯?

關於 Flutter 和 Dart 升級的話,我們可以通過flutter upgrade命令進行升級。當遇到提示需要使用 Power Shell 5.0及以上版本的話,說明你的機器是 windows 7系統或者 Power Shell 版本低於5.0,這裏你或者升級Power Shell版本,或者升級 Windows 操作系統到 Windows 10,當然最好是直接去 Flutter 和 Dart 官方分別直接下載最新版本 SDK 的壓縮包解壓覆蓋舊版本即可。

4、遇到Waiting for another flutter command to release the startup lock…?

此時需要打開Flutter SDK的bin目錄:flutter/bin/cache/lockfile,刪除這個文件就行了。

5、Flutter引入資源圖片和字體等的使用?

Flutter的應用內資源圖片和字體等的使用,必須要在pubspec.yaml配置文件裏進行配置纔可以使用。

//項目名稱:要用英文,類似於Android中的包名,如果它修改了整個項目的引入的路徑都要修改
//所以一般確定了就不要修改
name: flutter_samples
//項目描述
description: A new Flutter project.

//版本號,這個會覆蓋對應Android和IOS的應用版本號
//+號前對應Android的versionCode,+號後對應Android的versionName
//+號前對應IOS的CFBundleVersion,+號後對應IOS的CFBundleShortVersionString
version: 1.0.0+1

//表示項目的編譯環境要求爲dart sdk版本號在2.1.0和3.0.0之間
environment:
  sdk: ">=2.1.0 <3.0.0"


//項目的依賴插件庫
//Flutter插件庫在這裏查找引用:https://pub.dartlang.org/flutter
dependencies:
  flutter:
    sdk: flutter
//我們可以在這裏引入插件庫
  cupertino_icons: ^0.1.2
  flutter_webview_plugin: ^0.3.1

dev_dependencies:
  flutter_test:
    sdk: flutter

//flutter相關配置
flutter:
//是否使用material圖標,建議爲true
  uses-material-design: true

  //配置項目文件裏的圖片路徑
  //如果需要使用項目目錄內附帶的圖片、音視頻等資源,必須在這裏配置定義
  assets:
    - images/a_dot_burr.jpeg
    - images/a_dot_ham.jpeg

  //字體文件資源相關配置
  fonts:
    - family: Schyler
      fonts:
        - asset: fonts/Schyler-Regular.ttf
        - asset: fonts/Schyler-Italic.ttf
          style: italic
    - family: Trajan Pro
      fonts:
        - asset: fonts/TrajanPro.ttf
        - asset: fonts/TrajanPro_Bold.ttf
          weight: 700

//下面這幾項一般只有在編寫插件庫發佈到Dart Pub時才寫,一般不用寫  
//作者
authors:
- Natalie Weizenbaum <[email protected]>
- Bob Nystrom <[email protected]>
//主頁
homepage: https://example-pet-store.com/newtify
//文檔地址
documentation: https://example-pet-store.com/newtify/docs
//發佈到
publish_to: none

如果遇到配置完都不可以使用的情況,請注意配置文件的縮進和格式,是不是多了或者少了一個空格導致的,同時也要注意路徑是否正確等。

6、Flutter打包成release版本後,安裝不能聯網和訪問文件?

可能是沒有添加相應的訪問權限,Android應用需要在AndroidManifest.xml裏添加相應的權限,並且注意Android 6.0後部分危險分類內的權限需要主動申請纔可以使用。具體的權限名稱和如何申請權限大家可以自行百度或者使用第三方插件庫實現申請權限功能。

7、Dart中var、dynamic、Object的區別和關係?

Object 是 Dart 所有對象的基類,也就是說所有類型都是 Object 的子類。所以任何類型對象都可以聲明爲 Object 類型,但是一般不這麼用。一般聲明爲 var 或者 dynamic 類型。

var 它可以接收任何類型的變量,但最大的不同是 Dart 中 var 變量一旦賦值,類型便會確定,後面則不能再改變其類型。

dynamic和var相似,只不過它聲明的類型,後續可以進行修改其類型。

8、Dart中final和const的區別和關係?

final 和 const 所聲明的變量只能賦值一次,後續不能重新賦值更改。final 和 const 不是var,也不是一個類型。使用了 final 和 const 修飾的變量類型聲明可以省略,並且不可以與var同時使用。類級別的常量,通常用 static const 來聲明。

兩者區別在於:const 變量是一個編譯時常量,編譯時必須有一個確定的值;final 是運行時常量,運行時有一個確定的值即可。舉個例子:

final dt = DateTime.now();//正確,運行時有確定的值
 
const dt = const DateTime.now();//錯誤,需要編譯時有確定的值

9、Dart中異步操作:Future、Stream及async和await的使用?

跟其他平臺一樣,Flutter 和 Dart 中也有異步操作函數。

返回爲 Future 或者 Stream 對象的函數,這些函數被稱爲異步函數。例如返回的Future對象可以方便我們進行後續的鏈式調用和操作,類似於 RxJava 和 Promise 。舉個例子:

Future.delayed(new Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");
}).then((data){
   //執行成功會走到這裏 
   print(data);
}).catchError((e){
   //執行失敗會走到這裏   
   print(e);
}).whenComplete((){
   //無論成功或失敗都會走到這裏
});

多個異步的操作:

Future.wait([
  // 2秒後返回結果  
  Future.delayed(new Duration(seconds: 2), () {
    return "hello";
  }),
  // 4秒後返回結果  
  Future.delayed(new Duration(seconds: 4), () {
    return " world";
  })
]).then((results){
  print(results[0]+results[1]);
}).catchError((e){
  print(e);
});

當所有任務都執行完畢後才一起返回結果。

Dart 中的 async/await 和 JavaScript 中的 async / await 功能和用法基本是一模一樣的。需要配合一起使用:

task() async {
   try{
    String id = await login("alice","******");
    String userInfo = await getUserInfo(id);
    await saveUserInfo(userInfo);
    //執行接下來的操作   
   } catch(e){
    //錯誤處理   
    print(e);   
   }  
}

async 修飾方法名爲異步,await 爲內部的耗時操作進行標記。

Stream 也是用於接收異步事件數據,和Future 不同的是,它可以接收多個異步操作的結果。 舉個例子:

Stream.fromFutures([
  // 1秒後返回結果
  Future.delayed(new Duration(seconds: 1), () {
    return "hello 1";
  }),
  // 拋出一個異常
  Future.delayed(new Duration(seconds: 2),(){
    throw AssertionError("Error");
  }),
  // 3秒後返回結果
  Future.delayed(new Duration(seconds: 3), () {
    return "hello 3";
  })
]).listen((data){
   print(data);
}, onError: (e){
   print(e.message);
},onDone: (){

});

我們的listen裏會輸出每個任務結束後的結果。如果有3個任務,它就會分3次輸出返回的結果。

10、Android啓動有一個白屏閃現怎麼處理?

在android/app/src/main/res/drawable/launch_background.xml中定義了自定義修改啓動頁splash的方法:

<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@android:color/white" />

    <!-- You can insert your own image assets here -->
    <!-- <item>
        <bitmap
            android:gravity="center"
            android:src="@mipmap/launch_image" />
    </item> -->
</layer-list>

我們可以將白色修改爲透明色,或者更換爲一張圖片都可以。

11、Flutter如何獲取屏幕寬高信息?

Flutter的屏幕寬高等信息都是通過MediaQuery.of(context).size.來獲取的:

double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height;

12、Flutter佈局浸入到了手機狀態欄如何處理?

使用SafeArea包裹一下佈局最外層即可:

SafeArea(top: true,
    child: MaterialApp(
        home: ,
    ),);

13、Flutter如何全屏和取消全屏?

全屏:

SystemChrome.setEnabledSystemUIOverlays([]); 

取消全屏:

SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.top, SystemUiOverlay.bottom]);

14、Flutter如何設置屏幕支持的方向?

// 設置豎屏
SystemChrome.setPreferredOrientations([
          DeviceOrientation.portraitUp,
          DeviceOrientation.portraitUp,
        ]);
// 設置橫屏
SystemChrome.setPreferredOrientations([
          DeviceOrientation.landscapeLeft,
          DeviceOrientation.landscapeLeft,
        ]);

15、Flutter如何取消和設置標題欄左側的圖標?

設置左側圖標:

appBar: AppBar(
        leading: Icon(Icons.menu),
        automaticallyImplyLeading: true,)

取消左側圖標:

appBar: AppBar(
        leading: null,
        automaticallyImplyLeading: false,)

16、Flutter佈局或者文字超過邊界被裁剪了怎麼處理?

如果控件超出屏幕範圍後想自動換行,可以嘗試使用Wrap組件進行包裹使用。
如果不行的話,例如Row裏的兩個子控件,我們可以嘗試將Row裏的子控件用Expanded包裹起來,這樣就可以實現超過屏幕自動換行不被裁剪了。

17、Flutter右上角有個debug標誌,如何去掉?

這個是調試模式下默認自帶的,當正式打成release包時就沒有了。如果你想在開發時候也不顯示這個標誌,只需要在main.dart裏配置一個屬性即可:

MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.teal,
        ),
        home: ShowAppPage(),
        routes: <String, WidgetBuilder>{
          '/buttonpage': (BuildContext context) => ButtonSamples(),
          '/routepage': (BuildContext context) => RouteSamples(),
        },
        // 設置爲false就不現實debug標誌了
        debugShowCheckedModeBanner: false,
      )

18、Flutter如何動態控制顯示和隱藏某個佈局或組件?

可以使用Offstage組件,動態控制其offstage屬性值即可實現這個效果。

19、Flutter如何禁止GridView類似的列表組件禁止滾動或添加滾動回彈效果?

Flutter 中禁用 GridView 的滾動,可以使用 physics 屬性,取值爲NeverScrollableScrollPhysics()
如果添加滾動回彈效果依然是設置 physics 屬性,取值爲BouncingScrollPhysics()

20、Flutter如何監聽某個組件已經渲染完畢?

監聽某個組件是否已經渲染完成,使用 WidgetsBinding ,方法是在 initstate 或者 build 中註冊回調:

WidgetsBinding.instance.addPostFrameCallback((callback){
      print("complete");
    });

21、Flutter如何設置定時任務,定時器?

    // 開始計時
   _startTimer(){
   var _timer = Timer.periodic(new Duration(seconds: 1), (timer){
       // 編寫自己的邏輯
      setState(() {});
    });
  }
    // 取消計時
  _cancleTimer(){
    _timer?.cancel();
  }

22、Flutter如何監聽按鍵?

Flutter監聽按鍵用RawKeyboardListener:

const RawKeyboardListener({
    Key key,
    @required this.focusNode,//焦點結點
    @required this.onKey,//按鍵接收處理事件
    @required this.child,//接收焦點的子控件
  })

舉個例子:

FocusNode focusNode0 = FocusNode();

... ...

RawKeyboardListener(
      focusNode: focusNode0,
      child: Container(
        decoration: getCircleDecoration(color0),
        child: Padding(
          child: Card(
            elevation: 5,
            shape: CircleBorder(),
            child: CircleAvatar(
              child: Text(''),
              backgroundImage: AssetImage("assets/icon_tv.png"),
              radius: radius,
            ),
          ),
          padding: EdgeInsets.all(padding),
        ),
      ),
      onKey: (RawKeyEvent event) {
        if (event is RawKeyDownEvent && event.data is RawKeyEventDataAndroid) {
          RawKeyDownEvent rawKeyDownEvent = event;
          RawKeyEventDataAndroid rawKeyEventDataAndroid = rawKeyDownEvent.data;
          print("keyCode: ${rawKeyEventDataAndroid.keyCode}");
          switch (rawKeyEventDataAndroid.keyCode) {
            case 19: //KEY_UP
              FocusScope.of(context).requestFocus(_focusNode);
              break;
            case 20: //KEY_DOWN
              break;
            case 21: //KEY_LEFT
              FocusScope.of(context).requestFocus(focusNode4);
              break;
            case 22: //KEY_RIGHT
              FocusScope.of(context).requestFocus(focusNode1);
              break;
            case 23: //KEY_CENTER
              break;
            case 66: //KEY_ENTER
            break;
            default:
              break;
          }
        }
      },
    )

23、Flutter如何設置焦點控制?

Flutter Widget 獲取焦點的處理通過 FocusScope 這個 Widget 處理,配合FocusNode。

FocusNode focusNode0 = FocusNode();
... ...
//主動獲取焦點
FocusScope.of(context).requestFocus(focusNode0);
//自動獲取焦點
FocusScope.of(context).autofocus(focusNode0);

這樣就可以進行焦點獲取處理了。FocusNode 這個類也很重要,負責監聽焦點的工作。

焦點的移動我們用最新的 DefaultFocusTraversal 進行自動指定方向,搜索下一個焦點:

FocusScope.of(context)
                    .focusInDirection(TraversalDirection.up);
// 或者像下面這樣使用
DefaultFocusTraversal.of(context).inDirection(
                    FocusScope.of(context).focusedChild, TraversalDirection.up);

DefaultFocusTraversal.of(context)
                    .inDirection(_focusNode, TraversalDirection.right);

支持上下左右四個方向。 如果想手動指定下一個焦點是哪個的話,可以像下面這樣用:

FocusScope.of(context).requestFocus(focusNode);

24、Flutter如何監聽一些生命週期事件?

我們先了解下生命週期的概念,也就是一個頁面對象從創建到銷燬的整個狀態管理。我們看下Flutter的State生命週期的示意圖:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ep8pSO4C-1569849191792)(images/flutter_all/state.jpg)]
可以看到我們的一個頁面在加載創建時需要執行:
構造函數 -> initState -> didChangeDependencies -> build方法,然後纔會渲染爲一個頁面。

當銷燬關閉時:
deactivate -> dispose

內部的前後臺頁面狀態變化主要有:

enum AppLifecycleState {
  // 恢復可見
  resumed,
  // 不可見,後臺運行,無法處理用戶響應
  inactive,
  // 處在並不活動狀態,無法處理用戶響應。例如來電,畫中畫,彈框
  paused,
  // 應用被立刻暫停掛起,ios上不會回調這個狀態
  suspending,
}

當頁面更新是會執行:
didUpdateWidget -> build
可能會調用多次。

那麼接下來通過代碼實例來看下Flutter的生命週期:

import 'package:flutter/material.dart';

class StateSamples extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return StateSamplesState();
  }
}

class StateSamplesState extends State<StateSamples>
    with WidgetsBindingObserver {
  //插入渲染樹時調用,只調用一次
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  //構建Widget時調用
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('LifeCycleState'),
      ),
      body: Center(
        child: Column(
          children: <Widget>[],
        ),
      ),
    );
  }

  //state依賴的對象發生變化時調用
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
  }

  //組件狀態改變時候調用,可能會調用多次
  @override
  void didUpdateWidget(StateSamples oldWidget) {
    super.didUpdateWidget(oldWidget);
  }

  //當移除渲染樹的時候調用
  @override
  void deactivate() {
    super.deactivate();
  }

  //組件即將銷燬時調用
  @override
  void dispose() {
    super.dispose();
    WidgetsBinding.instance.removeObserver(this);
  }

  //APP生命週期監聽
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
      //恢復可見
    } else if (state == AppLifecycleState.paused) {
      //處在並不活動狀態,無法處理用戶響應
      //例如來電,畫中畫,彈框
    } else if (state == AppLifecycleState.inactive) {
      //不可見,後臺運行,無法處理用戶響應
    } else if (state == AppLifecycleState.suspending) {
      //應用被立刻暫停掛起,ios上不會回調
    }
    super.didChangeAppLifecycleState(state);
  }

  //其他方法

  //熱重載時調用
  @override
  void reassemble() {
    super.reassemble();
  }

  //路由彈出
  @override
  Future<bool> didPopRoute() {
    return super.didPopRoute();
  }

  //新的路由
  @override
  Future<bool> didPushRoute(String route) {
    return super.didPushRoute(route);
  }

  //系統窗口相關改變回調,例如旋轉
  @override
  void didChangeMetrics() {
    super.didChangeMetrics();
  }

  //文字縮放大小變化
  @override
  void didChangeTextScaleFactor() {
    super.didChangeTextScaleFactor();
  }

  //本地化語言變化
  @override
  void didChangeLocales(List<Locale> locale) {
    super.didChangeLocales(locale);
  }

  //低內存回調
  @override
  void didHaveMemoryPressure() {
    super.didHaveMemoryPressure();
  }

  //當前系統改變了一些訪問性活動的回調
  @override
  void didChangeAccessibilityFeatures() {
    super.didChangeAccessibilityFeatures();
  }

  //平臺色調主題變化時
  @override
  void didChangePlatformBrightness() {
    super.didChangePlatformBrightness();
  }
}

25、Flutter如何監聽返回按鍵?

我們看下返回鍵的監聽,返回鍵監聽攔截在Flutter中比較不一樣。
是單獨使用一個組件:WillPopScope。
接下來就通過一個實例來看下Flutter中實現連按兩次返回鍵退出的效果:

class KeyListenerState extends State<KeyListenerSamples> {
  int last = 0;
  int index = 0;

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    // 要用WillPopScope包裹
    return WillPopScope(
      // 編寫onWillPop邏輯
      onWillPop: _onWillPop,
      child: Scaffold(
          appBar: AppBar(
            title: Text('KeyListener Demo'),
          ),
          body: Center(
            child: Text("按鍵監聽"),
          )),
    );
  }
  
  // 返回鍵攔截執行方法
  Future<bool> _onWillPop() {
    int now = DateTime.now().millisecondsSinceEpoch;
    print(now - last);
    if (now - last > 1000) {
      last = now;
      // showToast("再按一次返回鍵退出");
      return Future.value(false); //不退出
    } else {
      return Future.value(true); //退出
    }
  }
}

26、Flutter如何JSON編解碼?

我們這裏只講解最基礎的編解碼,複雜的需要創建Model的建議藉助三方庫。如Flutter官方提供了一個插件庫:json_serializable。

當我們去請求網絡數據接口或者緩存某些結構數據時,一般都會用到JSON數據交換格式。JSON在移動端、後端、前端中應用都非常廣泛。在Flutter中JSON格式的解析使用 'dart:convert’裏的函數類進行編解碼處理。
我們看一個最簡單的編解碼使用的例子:

// JSON解碼
// 定義一個JSON格式字符串
String _jsonString = '{"name": "Flutter Book","author": "Google"}';

// 使用json.decode進行解碼
Map<String, dynamic> book = json.decode(_jsonString);

// 解碼後調用獲取值
Column(
    children: <Widget>[
        Text('Book Name:${book['name']}'),
        Text('Book Author:${book['author']}'),
    ],
));

// 再看下JSON編碼
// 使用json.encode將實體對象編碼爲JSON字符串
String _bookJson = json.encode(book);

怎麼樣,用起來是不是很簡單,這些只是最簡單的例子。實際開發中可能會遇到更加龐大、複雜嵌套的JSON結構。

// 如果是一個List集合的JSON字符串的話
String _jsonListString =
      '[{"name": "Flutter Book","author": "Google"},{"name": "Dart Book","author": "Google"}]';

// 解碼成List
List books = json.decode(_jsonListString);

// 調用取值
print(books[0]["name"]);

27、Flutter Hero動畫是做什麼的?

Flutter Hero動畫是專門用來做頁面跳轉效果的,例如一個頁面跳轉到另一個頁面,可以使用一些過渡動畫和效果。

Hero動畫主要用於頁面跳轉切換時的某個Widget的過渡跳轉動畫效果,也叫共享元素過渡動畫。用戶從頁面中選擇一個元素(通常是一個圖像),然後打開所選元素的詳情頁面。這個過程中元素和頁面執行的動畫就是Hero共享元素過渡動畫。

例如我的一個頁面有一個頭像,點擊頭像跳到另一個頁面,頭像有一個動畫,新頁面打開也有一個過渡動畫。

我們先看下Hero動畫的基本使用方式:

  • 先要在頁面A和頁面B分別定義一個Hero Widget,並且設置相同的tag值,這樣纔可以匹配;
  • 路由裏配置從頁面A跳轉到頁面B;
  • 點擊跳轉執行動畫。

Hero 動畫執行過程: Flutter框架會根據這兩個Hero Widget計算出一個補間矩形 ,將這個補間矩形作爲一箇中間的遮罩層作爲動畫過渡。在跳轉過程中,頁面A的Hero Widget會跳轉到中間遮罩層,然後進入到頁面B。

28、Flutter 可以開發TV應用嗎?

勉強可以,不過非常麻煩,開發效率低,焦點處理非常不方便。並且開發出來的應用性能很差,在某些機頂盒上非常卡頓,消耗很大資源。建議使用原生Anroid進行機頂盒開發,有Google Android TV官方支持,也有相應的庫支持。

29、Flutter 根目錄的main.dart可以改名或者移動到其他目錄嗎?

不可以,這個是 Flutter 入口文件類,固定的名稱和內部邏輯。

30、Flutter 如何實現複製到剪貼板?

  ///複製到剪貼板
  void setClipData(String text) {
    Clipboard.setData(ClipboardData(text: text));
  }

31、Flutter 和Dart 目前還可以做哪些方面的開發?

Flutter目前是移動Android和IOS端的應用研發,基礎功能都可以實現,一些需要原生支持的可以使用插件。實在插件都沒有的話,需要自己的編寫插件庫進行實現原生的功能。

Flutter 的 Web 開發目前已經有官方示例了:flutter.github.io/samples

正式版本Flutter Web SDK應該很快了。

Dart 的話目前可以開發後端服務器了,可以寫接口及相關的後端和服務器邏輯,已經測試。

Dart 目前還可以替代 JS 編寫 Dart 版本的 JS 邏輯。

32、Flutter 時間類如何使用?

使用DateTime這個類。

var now = DateTime.now();
  String time = now.year.toString() +
      "-" +
      now.month.toString() +
      "-" +
      now.day.toString() +
      "  " +
      now.hour.toString() +
      ":" +
      now.minute.toString() +
      ":" +
      now.second.toString();

還有很多方法,具體用法大家可以進行API調用查看,非常簡單。

33、Flutter 如何設置狀態欄顏色和圖標顏色?

可以通過 AppBar 的 brightness 或者 ThemeData 去設置狀態欄顏色。

但是如果不想用 AppBar ,那麼我們還可以嵌套 AnnotatedRegion 去設置狀態欄樣式,通過 SystemUiOverlayStyle 就可以快速設置狀態欄和底部導航欄的樣式。

同時還可以通過 SystemChrome.setSystemUIOverlayStyle 去設置,前提是沒有使用 AppBar 。需要注意的是,所有狀態欄設置是全局的,如果在 A 頁面設置後,B 頁面沒有手動設置或者使用 AppBar ,那麼這個設置將直接呈現在 B 頁面。

34、如何查看某個應用或某個頁面是否是使用 Flutter 寫的?

目前Android 平臺的應用通過開啓開發者模式裏的:開發者選項 -> 顯示佈局邊界 分辨出來。原生應用的每個控件都會用邊框分割出來,而 Flutter 的應用頁面無邊界分割,是個整體的SurfaceView。

Flutter 和 Dart 的未來展望

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-XwiWWhyP-1569849191793)(images/flutter_all/reflectly-hero-600px.png)]
Google公司於2018年12月5日發佈了Flutter 1.0正式版,大半年的時間過去了,Flutter最新版本已經到了V1.8.4了,更加的完善和穩定。Dart的最新版本已經到了v2.5.0版本。Flutter和Dart的更新頻率很快,並且官方維護的一些插件庫和開發者提交的插件庫已經越來越多,相關的文檔、資源也越來越多,Flutter生態也已經逐步完善。

Flutter目前發展

Flutter從1.0正式版發佈的大半年的時間裏,開發者數量、插件庫數量等都在指數級增長,吸引了來自全球各個國家的開發者和科技公司,目前Flutter已經成爲最熱門的開源項目之一了。當然,Flutter在國內的發展也非常的迅猛。在 StackOverflow 2019 年的全球開發者問卷調查中,Flutter 被選爲最受開發者歡迎的框架之一,超過了 TensorFlow 和 Node.js。
在這裏插入圖片描述
全球已經有很多大家熟悉的公司採用了 Flutter進行研發,包括很多國內的知名公司。比如阿里巴巴、騰訊、京東、美團等。
在這裏插入圖片描述
來自丹麥的 Reflectly應用,已經率先採用了Flutter進行重寫了客戶端,一套Flutter代碼編寫了Android和IOS端Reflectly應用。
在這裏插入圖片描述
在國內,Flutter的開發者和社區非常的活躍,其中最令人激動的就是:在今年Google I/O 前舉辦的全球 Flutter Create 大賽中,來自中國廣東的胡澤標憑藉一個特別精緻的羅盤應用摘得了Flutter Create全球大獎。
在這裏插入圖片描述
獲獎證書:
在這裏插入圖片描述
更多參賽作品及源碼可以在:https://flutter.dev/create 這裏進行查看和學習。

前面我們介紹過,Flutter會支持大部分的主流平臺,一套語言、一套邏輯就可以實現跨多平臺。如:Android、IOS、Web、PC、Fuchsia OS、物聯網等主流平臺。
在這裏插入圖片描述

Fuchsia OS

除了Flutter外,Google的Fuchsia OS也已經成爲了一個未來的熱門操作系統,雖然還沒有推出正式版本,但是它的目標和特點已經吸引了一大批開發者和學習愛好者。Flutter和Dart開發的應用是Fuchsia OS默認支持的。早在2016年,Google祕密研發Fuchsia操作系統的就被首次曝光。Fuchsia OS是一套可運行在手機、平板、PC等平臺的跨平臺系統,放棄Linux內核,而是基於Zircon微核,採用Flutter引擎+Dart語言編寫。預測可能在2020~2021年Fuchsia OS正式版將會推出使用,或許會替代Android系統。據傳,Google已經聘請了有着10多年Mac OS開發經驗的資深蘋果系統開發工程師Bill Stevenson來操盤Fuchsia,目標是推向成熟市場。華爲的很多設備也已經很早就配合Flutter和Fuchsia OS進行了測試。我們也期待Fuchsia OS可以早日推出。

Flutter Web

在這裏插入圖片描述
除了Flutter移動平臺外,可能最引入矚目的就是Flutter Web(https://flutter.dev/web) 的支持了,雖然Web SDK正式版還沒有發佈,不過通過預覽測試版我們就可以有理由相信Flutter Web將會大大簡化我們開發Web頁面的成本。無需編寫繁雜的CSS和JS、HTML,一套Flutter代碼就輕鬆搞定一個Web頁面系統。

目前,Flutter for Web 的示例應用在桌面瀏覽器基本能達到每秒 60 幀的渲染速度。但是在移動瀏覽器,特別是在低端機型上還有很大的優化空間。

Flutter Web官方的測試預覽例子可以在:flutter.github.io/samples 進行學習和體驗,目前通過這幾個例子來看,效果非常的不錯。
在這裏插入圖片描述
Flutter for Web 目前處在技術預覽階段,相信很快會推出正式版本。

Flutter 桌面和嵌入式

Flutter 也將支持桌面PC平臺。目前處於研發實驗階段。未來可以用Flutter開發Mac、Windows 和 Linux 、Chrome OS 、Fuchsia OS上運行的 Flutter 應用。

Flutter桌面的實驗性項目:https://github.com/google/flutter-desktop-embedding
Flutter 桌面的早期說明:https://github.com/flutter/flutter/wiki/Desktop-shells

Flutter未來也將支持在嵌入式設備商進行開發和運行,例如在Raspberry Pi 等小型設備上運行 Flutter 應用。

Flutter嵌入式示例:https://medium.com/flutter-io/flutter-on-raspberry-pi-mostly-from-scratch-2824c5e7dcb1

Flutter嵌入式 API:https://github.com/flutter/flutter/wiki/Custom-Flutter-Engine-Embedders

目前項目處於實驗測試階段。

Flutter 遊戲

在 Google I/O’19 期間,Flutter 團隊和 2Dimensions 聯合發佈了一款運營 / RPG 遊戲: Flutter Developer Quest。除了作爲遊戲本身在遊戲性上毫不縮水外,代碼也完全開源。這是一項Flutter在遊戲開發上的新的嘗試和應用拓展。

遊戲源代碼地址:https://github.com/2d-inc/developer_quest

Flutter Developer Quest,是一款完全由 Flutter 開發構建的遊戲,遊戲已經在 App Store 和 Google Play 上進行免費下載體驗。Flutter Developer Quest 是一款基於屏幕進行交互的 RPG 類遊戲,遊戲展示了許多最新的 Flutter 功能。

這是一個新的拓展和嘗試,大家可以自行進行遊戲源碼的閱讀和學習。

Flutter 近期展望

Flutter的近期動態已經在FlutterGithub主頁的Github wiki 上進行了公開。當然我們也可以關注:谷歌開發者這個微信公衆賬號獲取更多更新的動態消息。
在這裏插入圖片描述
地址:https://github.com/flutter/flutter/wiki/Roadmap ;
https://github.com/dart-lang/language

“accepted” 目錄中的爲工程實施階段,“working” 目錄中的爲設計階段,大家可以持續關注。
Flutter和Dart都在按照計劃進行加緊研發中。相信不久我們便可以看到一些關於Flutter和Dart的新的東西。

總結

關於 Flutter 和 Dart 開發相關的分享就暫時這麼多,後續遇到了相關問題繼續更新和分享。也歡迎廣大讀者反饋問題及解決方案,一起進步、一起學習、一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章