第一章 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) {}
}
P32:Flutter的繼承也是單繼承的(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:在組件被銷毀時調用。
<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
- 運行APP并執行main方法
- 開始ging優先處理microtask queue,知道隊列為空
- 當microtask queue為空后,開始處理event queue。如果event queue里面有event,則執行,<font color="#dd0000">每執行一條再判斷此時新的microtask queue是否為空</font>,并且每次只取出一條來執行??梢赃@么理解,在處理所有event之前,我們會做一些市區內給,并且會把這些事情放在microtask queue中。
- microtask queue 和 event queue都為空,則APP可以正常退出。
注意:當處理microtask queue時,event queue會被阻塞,所以microtask queue中應避免任務太多或長時間處理,否則將導致APP的繪制和交互等行為被卡住。
P192:Future表示“將來”一次異步獲取得到的數據,而Stream是多次異步獲取得到的數據;Future將返回一個值,而Stream將返回多次值。
第七章 路由
P197:一個界面跳轉到另一個界面使用Navigator.push方法,返回上一個界面使用Navigator.pop方法。路由分為靜態路由和動態路由。
204:參數回傳
- 等待回傳數據
_navigateAndDisplaySelection(BuildContext context) async { final result = await Navigator.push( context, MaterialPageRoute(builder: (context) => SelectionScreen()), ); }
- 回傳數據
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種
- BasicMessageChannel:用于傳遞字符串和半結構化的信息(在大內存數據塊傳遞的情況使用)
- MethodChannel:用于傳遞方法的調用
- EventChannel:用于數據流(event streams)的通信
P242:Flutter的消息傳遞工具是BinaryMessager,傳遞的消息格式是二進制的。二進制格式的消息通過消息編解碼器(Codec)解碼為能識別的消息,并傳遞給Handler來進行處理。
P253:在Android里每初始化一個FlutterView就會有一個FlutterEngine被初始化,這樣就會有新的線程在Dart上運行。如果每個Activity中都有一個FlutterView,則會創建多個Flutter Engine實例,這會導致每個Flutter Engine實例加載的代碼都獨立運行在ioslate中,這種模式被稱為多引擎模式。存在問題 - 冗余的資源問題。
- 插件注冊問題。插件依賴Messenger傳遞消息,多個FlutterView時,插件的注冊和通信將變得混亂和難以維護。
- Flutter Widget和Native頁面的差異化問題。
- 增加頁面之間通信的復雜度。
P254:FlutterBoost的思想是把Flutter容器做成瀏覽器的樣子,然后填寫一個頁面地址,再由容器去管理頁面的繪制。在Native端,如果初始化容器,就設置容器對應頁面的標志即可。