讀書筆記『Flutter從0到1構建大前端應用』

第一章 Flutter簡介

P4:Flutter Engine

  • Skia:2D渲染引擎(Android系統自帶,iOS系統不自帶,因此iOS包所占用的存儲空間更大)
  • Dart:Dart運行時
  • Text:文本排版引擎

第二章 Dart 語言入門

P18:Dart是一門強類型語言,在第一次賦值時,如果已經確定了是字符串類型,則不能更改為別的類型。如果真的想要改變,可以使用dynamic關鍵字。
P19:num聲明的變量可以加入的是int型,還可以被改成double型,但是反過來int聲明的變量不能再賦值為double。

num a = 10;
a = 30.2;

P23:使用bool表示布爾值,布爾值只有true和false;可以在debug模式下通過assert斷言判斷布爾值

var a = '';
assert(a.isEmpty);

創建一個不可變數組,使用const[...]

var list = const[1,2,3];

P25:<font color="#dd0000">使用dynamic時會告訴編譯器,我們不用做類型檢測,并且知道自己在做什么。</font>
P26:可以使用as,is關鍵字對類型進行檢測

dynamic obj = <String,int>();
if(obj is Map<String,int>) {
  obj['age'] = 20;
}

<font color="#dd0000">~/ 除法,返回一個整數結果</font>

級聯操作符:有點類似一些語言的鏈式調用。

String s = new StringBuffer()
    ..write('a')
    ..write('b').toString();

P28-29:函數Function
可選參數,可選的命名參數,即不傳這些參數也可以。

void userSettings({int age,String name}) //可選參數使用{}包括

必傳參數,使用@required修飾

void userSettings({@required int age,String name})

可選的位置參數

void userSettings({int age,String name,[String interests]}) {
  if(interests != null) {
    //...
  }
}

默認參數,默認值是編譯時常量

void userSettings({int age = 21,String name='小米'})

P31:一個Future表示一個異步操作產生的的結果。

Future<int> future = getFuture();
future.then((value) => handleValue(value)) //then用于接受異步處理的結果
  .catchError((error) => handleError(error)) //catchError用于捕獲異常
  .whenComplete() => handleComplete(); //whenComplete用于處理執行結束的回調,無論是否異常都會執行

then用于接受異步處理的結果;catchError用于捕獲異常;whenComplete用于處理執行結束的回調,無論是否異常都會執行
當有需要有延遲的運算(async)時,可以將其放到隊列(await)中去,注意:<font color="#dd0000">await必須在async標記的函數中運行,否則會報錯。</font>async和await用于解決嵌套回調等問題:

steps() async {
  try {
    String step1Res = await step1('step1');
    String step2Res = await step2(step1Res);
    String step3Res = await step1(step2Res);
  } catch(e) {}
}

P32Flutter的繼承也是單繼承的(extends),因為Flutter沒有訪問修飾符,所以子類可以訪問父類中所有的變量和方法;<font color="#dd0000">Flutter中每個類都是一個隱式的接口,這個接口包含類中所有的成員變量和方法</font>。當類被當做接口使用時,類中的方法需要在子類中被實現。
<font color="#dd0000">P34:</font>Dart新增的mixins語法特性,作用是可以在類中混入其他功能。具體的講,就是把<font color="#dd0000">自己的方法提供給其他類使用,但卻不需要成為其他類的父類,它以非繼承的方式來復用代碼</font>。使用mixins,需要使用with關鍵字。

abstract class CanFixComputer {
  ...
  void fixComputer() {
    print('軟件工程師修電腦')
  }
}
class ITTeacher with CanFixComputer {
  @override
  void fixComputer() {
    print('IT老師修電腦')
  }
}

第三章 一切皆組件

P40:從布局來看,官方把Flutter布局分為Basic Widget、Single-Child、Multi-Child。
P58:Scaffold是基于
Material庫的一個與路由相關**的模板組件,可以認為是Flutter提供的標準化的布局容器。

Widget build(BuildContext context) {
  return Scaffold(
    AppBar:...
    body:...
    bottomNavigationBar:...
    floatingActionButton:...
    drawer:...
    ...
  )
}

P62:Column是不支持滾動的,如果需要實現滾動功能,則需要考慮使用ListView。
P76:使用Wrap替換Row可以實現自動換行的效果。
P79:Context表示組件上下文的意思。通過Context可以遍歷和查找當前Widget樹
<font color="#dd0000">P80:</font>StatelessWidget即無狀態的Widget,它<font color="#dd0000">無法通過setState設置組件的狀態</font>。對于其內部屬性,應該被聲明為final,防止被意外修改。

class MyStatelessWidget extends StatelessWidget {
  MyStatelessWidget({
    Key key,
    this.parameter,
  }):super(key:Key),
  final parameter;
  @override
  Widget build(BuildContext context){}
}

StatefulWidget即有狀態Widget。創建一個StatefulWidget組件時,會同時創建一個State對象,并且StatefulWidget通過與State關聯可以達到刷新UI的目的。

class MyStatefulWidget extends StatefulWidget {
  MyStatefulWidget({
    Key key,
    this.color,
  }):super(key:key),
  
  final Color color;
 
  @override 
  _MyStatefulWidgetState createState() => new _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  ...
  @override
  Widget build(BuildContext context) {...}
}

P82:生命周期
initState是State生命周期里第一個被執行的方法,可以在該方法進行一些初始化動作。<font color="#dd0000">在initState里,Framework層還沒有把Context和State關聯在一起,所以還不能訪問Context。初始化方法在生命周期里只會被執行一次。</font>

didChangeDependencies:在執行完initState之后就可以訪問Context了。如果Widget使用了InheritedWidget的數據,并且在InheritedWidget的數據發生改變時,Flutter Framework就會觸發didChangeDependencies的回調。
build:在執行完didChangeDependencies之后,<font color="#dd0000">每次調用setState都會觸發build方法。</font>
dispose:在組件被銷毀時調用。

img

<font color="#dd0000">P88</font>:在Flutter中,每個Widget都有一個唯一標識Key,在創建和渲染時生成,可以手動指定。在某些場景下,需要保存Key,可以保存到GlobalKey、LocalKey、UniqueKey、ObjectKey中,可以根據key找到的對應的Widget。
P89:被InheritedWidget暴露出來的數據,可以高效地在Widget樹中從上往下傳遞和共享,并支持跨級數據傳遞。

第四章 事件處理

P100:原始指針事件,把Listener包裹在需要監聽的組件外來實現。

...
  body:Center(
    child:Listener(
        child:Container()
    )
  )

P102:可以使用IgnorePoiner和AbsorbPointer對事件進行忽略,前者本身可以接受事件,后者本身也不可接收事件。
P105:GestureDetector可以支持更豐富的事件,如縮放、雙擊、垂直、水平的手勢。同樣需要包裹在要監聽的組件外面。

GestureDetector (
    onTap:() {
    print('tap');
  },
  child:Container()...
)

P112:事件競爭和手勢沖突
Flutter加入了手勢競技場的概念。在給同一個組件同時添加水平和垂直回調時,若用戶將指針水平移動超過一定的邏輯像素,則判定水平獲勝。反之判定垂直獲勝。

第五章 動畫

P129:當創建AnimationController時,需要傳入vsync參數,這個參數接受的是TickerProvider類型的對象,<font color="#dd0000">作用是阻止在屏幕鎖屏時執行動畫以避免不必要的資源浪費。</font>
P136:AnimatedWidget對addListener、setState等動畫相關動作進行了封裝,使用起來更方便。
P141:通過Hero,可以在路由之間做出流程的轉場動畫。Hero的組件需要同時定義源組件和目標組件,其中源組件和目標組件被Hero包裹在需要動畫控制的組件外面。

第六章 使用網絡技術和異步編程

P184:在Flutter里,異步使用Futrue來修飾的,并運行在event loop里。Flutter中一個很重要的概念是isolate,通過Flutter Engine層面的一個線程來實現的,而實現isolate的線程又是由Flutter管理和創建的。所有的Dart代碼都運行在isolate上。通常情況下,我們的應用都運行在main isolate中,在有必要時,我們可以創建新的isolate。<font color="#dd0000">多個isolate無法共享內存,必須通過相關的API通信才可以。</font>
P185:event loop

img

  1. 運行APP并執行main方法
  2. 開始ging優先處理microtask queue,知道隊列為空
  3. 當microtask queue為空后,開始處理event queue。如果event queue里面有event,則執行,<font color="#dd0000">每執行一條再判斷此時新的microtask queue是否為空</font>,并且每次只取出一條來執行??梢赃@么理解,在處理所有event之前,我們會做一些市區內給,并且會把這些事情放在microtask queue中。
  4. microtask queue 和 event queue都為空,則APP可以正常退出。
    注意:當處理microtask queue時,event queue會被阻塞,所以microtask queue中應避免任務太多或長時間處理,否則將導致APP的繪制和交互等行為被卡住
    P192:Future表示“將來”一次異步獲取得到的數據,而Stream是多次異步獲取得到的數據;Future將返回一個值,而Stream將返回多次值。

第七章 路由

P197:一個界面跳轉到另一個界面使用Navigator.push方法,返回上一個界面使用Navigator.pop方法。路由分為靜態路由和動態路由。
204:參數回傳

  1. 等待回傳數據
      _navigateAndDisplaySelection(BuildContext context) async {
        final result = await Navigator.push(
          context,
          MaterialPageRoute(builder: (context) => SelectionScreen()),
        );
      }
    
  2. 回傳數據
    Navigator.pop(context, 'Flutter');
    

P207:路由棧
類似Android的launchMode功效

  • pushReplacementNamed:替換當前棧頂頁面為新的頁面
  • popAndPushNamed:和上面的一樣,只是多了pop的交互效果
  • pushNamedAndRemoveUntil:添加一個元素,并且移除歷史,直到named對象
  • popUnil:沒有push,移除歷史,直到named對象

第八章 持久化

P215:推薦使用shared_preferences插件,這是一個異步的key-value存儲的插件。

  • 寫入
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString(k,v);
    
  • 讀取
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.getString(k);
    
  • 清除
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.remove(k);
    

P220:sqflite

  • 支持事務和批處理
  • 支持自動version管理
  • 支持增、刪、改、查的Helper工具類
  • 支持Android/iOS后臺線程的運行
    獲取和刪除
var databasesPath = await getDatabasesPath();
String path = join(databasePath,'demo.db');
await deleteDatabase(path);

打開并創建數據庫

Database database = await openDatabase(path,version:1,oncreate:(
    Database db,int version) async {
    await db.execute('CREATE TABLE Test(id INTEGER ...)')
    }
));

插入數據的兩種方式
//方式一,直接使用sql

Future<int> rawInsert(String sql,[List<dynamic> arguments]);
int id = await txn.rawInsert(
    'INSERT INTO Test(name,value,num) VALUES(?,?,?)',['another name',1213,3.1212])
);

//方式二,使用map

Future<int> insert(String table,Map<String,dynamic> values,{String nullColumnHack,ConflictAlgorithm conflictAlgorithm});

修改的兩種方式,和插入一樣,只是方法名變了

Future<int> rawUpdate(String sql,[List<dynamic> arguments]);
Future<int> rawUpdate(String table,Map<String,dynamic> values,{String nullColumnHack,ConflictAlgorithm conflictAlgorithm});

查詢的兩種方式

Future<List<Map<String,dynamic>>> rawQuery(String sql,[List<dynamic> arguments]);
Future<List<Map<String,dynamic>>> Query(String table,{
  bool distinct,List<String> columns,String where,List<dynamic> whereArgs,
  String groupBy,String having,String orderBy,int limit,int offset
});

物理刪除的兩種方式

Future<int> rawDelete(String sql,[List<dynamic> arguments]);
Future<int> delete(String table,{String where,List<dynamic> whereArgs})

計算總記錄數

Sqflite.firstIntValue(await database.rawQuery('SELECT COUNT(*) FROM Test'))

關閉數據庫

await databse.close()

第九章

P238:添加未發布的package,編輯pubspec.yaml

dependencies:
    plugin1:
        path: ../plugin1/

添加git上的依賴

dependencies:
    plugin1:
        git:
            url: git://github.com/flutter/plugin1.git

P239:調用flutter packages get之后,會生成pubspec.lock。該文件確保更新的package不會影響現有代碼。
P240:創建自己的package

flutter create --org com.example --template=plugin -i swift -a kotlin hello

--org:指定包名;-i:指定iOS語言;-a:指定Android語言
P241:Platform Channel
Platform Channel 是Flutter與Platform指定的通信機制,包括3種

  1. BasicMessageChannel:用于傳遞字符串和半結構化的信息(在大內存數據塊傳遞的情況使用)
  2. MethodChannel:用于傳遞方法的調用
  3. EventChannel:用于數據流(event streams)的通信
    P242:Flutter的消息傳遞工具是BinaryMessager,傳遞的消息格式是二進制的。二進制格式的消息通過消息編解碼器(Codec)解碼為能識別的消息,并傳遞給Handler來進行處理。
    P253:在Android里每初始化一個FlutterView就會有一個FlutterEngine被初始化,這樣就會有新的線程在Dart上運行。如果每個Activity中都有一個FlutterView,則會創建多個Flutter Engine實例,這會導致每個Flutter Engine實例加載的代碼都獨立運行在ioslate中,這種模式被稱為多引擎模式。存在問題
  4. 冗余的資源問題。
  5. 插件注冊問題。插件依賴Messenger傳遞消息,多個FlutterView時,插件的注冊和通信將變得混亂和難以維護。
  6. Flutter Widget和Native頁面的差異化問題。
  7. 增加頁面之間通信的復雜度。
    P254:FlutterBoost的思想是把Flutter容器做成瀏覽器的樣子,然后填寫一個頁面地址,再由容器去管理頁面的繪制。在Native端,如果初始化容器,就設置容器對應頁面的標志即可。
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374