MainAxisAlignment.spaceBetween
將主軸空白位置進(jìn)行均分,排列子元素,首尾子控件距邊緣沒有間隙
MainAxisAlignment.spaceAround
將主軸空白區(qū)域均分域那,使中間各個子控件間距相等,首尾子控件距邊緣間距為中間子控件間距的一半
MainAxisAlignment.spaceEvenly
將主軸空白區(qū)域均分邮偎,使各個子控件間距相等
flutter column row布局的列表自適應(yīng)寬高
mainAxisSize: MainAxisSize.min
通過runtimeType可以獲取當(dāng)前數(shù)據(jù)類型
var e = [12.5,13.1];
print('e 的類型是: ${e.runtimeType}'); // e 的類型是: List<double>
flutter column嵌套listview不能滾動,或者不顯示的問題
因為 listview水平視口的寬度是無限的喉磁。
在listview外面嵌套一個expanded,或者一個container就可以了棕叫,尺寸計算的問題幔荒,expande就是listview有多大就有多大,container就是container多大listview就有多大糊闽,可以滾動
child: Row(
children: <Widget>[
Expanded(
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
physics: BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
itemCount: 120,
itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.all(8.0),
child: new Text('${index}'),
),
),
),
],
),
List 常見用法
List<dynamic> topTitles = ['審批單', '機(jī)票列表', '客服'];
//遍歷list
for(var value intopTitles){
print("---------<>---${value}");
}
topTitles.forEach((item) => {print(item)});
//遍歷得到一個新的List 用此方法 亦可動態(tài)加載Widget
List<dynamic> newList = topTitles.map((item) {
return item + "111";
}).toList();
//list的map方法不能獲取index 用Asmap 轉(zhuǎn)換成map 后
//動態(tài)加載Widget
Row(
children: topTitles
.asMap()
.keys
.map((index) => Expanded(
flex: 1,
child: Column(
children: <Widget>[Text(topTitles[index])],
),
))
.toList(),
),
Map
//遍歷map
mostCare.forEach((k, v) {
// print(k + "==" + v.toString()); //類型不一樣的時候就toString()
});
//map key遍歷生成Widget
Row(
children: mostCare.keys.map((key) {
// print('-----key--${key}');
return Expanded(
flex: 1,
child: Column(
children: <Widget>[Text(mostCare[key])],
),
);
}).toList(),
),
延遲
/* //延遲3秒
Future.delayed(Duration(seconds: 3), () {
});*/
軟鍵盤彈出頂?shù)魞?nèi)容梳玫、防止鍵盤超出屏幕
研究了半天發(fā)現(xiàn)十分簡單,只需兩行代碼右犹。布局中最外層包裹一個SingleChildScrollView組件提澎,然后在Scaffold里增加一個屬性 resizeToAvoidBottomPadding: false,即刻解決鍵盤遮擋問題,
類似于 Android 中的 android:windowSoftInputMode=”adjustResize”念链,控制界面內(nèi)容 body 是否重新布局來避免底部被覆蓋了盼忌,比如當(dāng)鍵盤顯示的時候,重新布局避免被鍵盤蓋住內(nèi)容掂墓。默認(rèn)值為 true谦纱。
Flutter TextField 光標(biāo)和內(nèi)容不能對齊問題
添加該屬性
TextField(
style: TextStyle(textBaseline: TextBaseline.alphabetic),
)
flutter中text設(shè)置overflow還是會超出屏幕解決方法
使用flex控件代替row控件,并且在文字外面包一層Expanded
君编,應(yīng)該是橫向row沒有確定寬度跨嘉,text根據(jù)內(nèi)容來撐開row,所以就會超出吃嘿,換成flex 使text最大寬度能占用剩下的所有寬度祠乃,所以達(dá)到最寬的時候就會顯示省略號。
這個錯誤就好像再column中使用listView一樣兑燥,會出現(xiàn)一個在無限高度的view中使用listView的錯誤,
屏幕適配原理
flutter_screenutil:
說一下適配方案, 比如我們設(shè)計師設(shè)計的UI是根據(jù)Iphone6來做的,我們知道 iPhone6的分辨率是750*1334(px)亮瓷、
又或者是根據(jù)hdpi的設(shè)備來設(shè)計的UI,我們知道hdpi的 Android設(shè)備是 (240 dpi),像素密度是1.5,即hdpi設(shè)備的分辨率寬度是320px, 總之,無論設(shè)計稿的單位是px,或者是dp,我們都能夠轉(zhuǎn)換成px.
那么我們?nèi)绻鶕?jù)px來適配,ios和 android 就都可以兼容了.
假設(shè),我們的設(shè)計稿手機(jī)是10801920 px.
設(shè)計稿上有一個540960 的組件, 即寬度和寬度是手機(jī)的一半. 如果我們直接寫的時候組件的尺寸這么定義,在其他尺寸的設(shè)備上未必是一半,或多,或少. 但是我們可以按比例來看,即我們要實現(xiàn)的寬度是實際設(shè)備的一半.
那么假設(shè)我們設(shè)備的寬度是deviceWidth和deviceHeight , 我們要寫的組件大小為: 寬:(540/1080)deviceWidth,高度: (960/1920)deviceHeight.
通過這個公式我們可以發(fā)現(xiàn),我們要寫的組件寬度就是設(shè)計稿上的尺寸width(deviceWdith/原型設(shè)備寬度).那么每次我們寫ui的時候,只要直接哪來設(shè)計稿的尺寸(deviceWdith/設(shè)備原型)寬度即可
原理就是先獲取,實際設(shè)備與原型設(shè)備的尺寸比例.
構(gòu)造方法
如果沒有自定義構(gòu)造方法,則會有個默認(rèn)構(gòu)造方法
如果存在自定義構(gòu)造方法 默認(rèn)構(gòu)造方法將失效
構(gòu)造方法不能重載
命名構(gòu)造方法
使用命名構(gòu)造方法 可以實現(xiàn)多個構(gòu)造方法
使用 類名.方法 的形式實現(xiàn)
class Person {
string name
int age
final string sex
/**
* 這里 使用this的語法糖可以對final類型賦值
* 但是寫在構(gòu)造方法中就不行了,因為執(zhí)行在構(gòu)造方法之前
*/
Person (string name ,this.age ,this.sex) { // 第二種是語法糖,
this.name = name
print(age)
}
}
var per = new Person('jack', 20)
---------------------------------------
命名構(gòu)造方法
class Person {
string name
int age
final string sex
Person (string name ,this.age ,this.sex) { // 第二種是語法糖,
this.name = name
print(age)
}
Person.withName(string name) {
this.name = name
}
}
<!-- 多個構(gòu)造方法 類似oc -->
new Person('jack', 12, 'man')
new Person.withName('marry')
混合 mixins (with)
除了繼承和接口實現(xiàn)之外,Dart 還提供了另一種機(jī)制來實現(xiàn)類的復(fù)用降瞳,即“混入”(Mixin)嘱支。通過混入,一個類里可以以非繼承的方式使用其他類中的變量與方法挣饥,效果正如你想象的那樣除师。
混合的對象是類
可以混合多個
生命周期
State 的生命周期可以分為 3 個階段。
State 初始化時會依次執(zhí)行 :構(gòu)造方法 -> initState -> didChangeDependencies -> build扔枫,隨后完成頁面渲染馍盟。
Widget 的狀態(tài)更新,主要由 3 個方法觸發(fā):setStState茧吊、didchangeDependencies 與 didUpdateWidget。
Widget 組件銷毀相對比較簡單八毯,系統(tǒng)會調(diào)用 deactivate 和 dispose 這兩個方法搓侄,來移除或銷毀組件。
Widget 渲染過程
- Widget话速、是控件實現(xiàn)的基本邏輯單位讶踪,里面存儲的是有關(guān)視圖渲染的配置信息,包括布局泊交、渲染屬性乳讥、事件響應(yīng)信息等柱查。
- Element :是 Widget 的一個實例化對象,它承載了視圖構(gòu)建的上下文數(shù)據(jù)云石,是連接結(jié)構(gòu)化的配置信息到完成最終渲染的橋梁唉工。
- RenderObject 、主要負(fù)責(zé)實現(xiàn)視圖渲染的對象汹忠、通過控件樹中的每個控件創(chuàng)建不同類型的渲染對象淋硝,組成渲染對象樹。
Fluttter 將視圖樹的概念進(jìn)行了擴(kuò)展宽菜,把視圖數(shù)據(jù)的組織和渲染抽象為三部分谣膳,即 Widget,Element 和 RenderObject铅乡。
渲染對象樹在 Flutter 的展示過程分為四個階段继谚,即布局、繪制阵幸、合成和渲染花履。
布局和繪制在 RenderObject 中完成,F(xiàn)lutter 采用深度優(yōu)先機(jī)制遍歷渲染對象樹侨嘀,確定樹中各個對象的位置和尺寸臭挽,并把它們繪制到不同的圖層上,布局和繪制完成后咬腕,再交給 Skia進(jìn)行合成和渲
為什么需要增加中間的這層 Element 樹呢欢峰?直接由 Widget 命令 RenderObject 去干活兒不好嗎?
因為 Widget 具有不可變性涨共,但 Element 卻是可變的纽帖,實際上,Element 樹這一層將 Widget 樹的變化(類似 React 虛擬 DOM diff)做了抽象举反,可以只將真正需要修改的部分同步到真實的 RenderObject 樹中懊直,最大程度降低對真實渲染視圖的修改,提高渲染效率火鼻,而不是銷毀整個渲染視圖樹重建室囊。
合成和渲染
隨著頁面越來越復(fù)雜、Flutter 的渲染樹層級很多魁索,直接交付給渲染引擎進(jìn)行多圖層渲染融撞,可能會出現(xiàn)大量渲染內(nèi)容的重復(fù)繪制,所以還需要先進(jìn)行一次圖層合成粗蔚,即將所有的圖層根據(jù)大小尝偎、層級、透明度等規(guī)則計算出最終的顯示效果,將相同的圖層歸類合并致扯,簡化渲染樹肤寝,提高渲染效率,合并完成后抖僵,F(xiàn)lutter 會將幾何圖層數(shù)據(jù)交由 Skia 引擎加工成二維圖像數(shù)據(jù)鲤看,最終交由 GPU 進(jìn)行渲染,完成界面的展示裆针。
StatelessWidget與StatefulWidget區(qū)別
分別是組裝控件的容器
StatelessWidget 不帶綁定狀態(tài)刨摩,而 StatefulWidget 帶綁定狀態(tài),其依賴的數(shù)據(jù)在 Widget 生命周期中可能會頻繁地發(fā)生變化世吨,由 State創(chuàng)建視圖澡刹,數(shù)據(jù)驅(qū)動視圖更新。
單線程模型
// 聲明了一個延遲 3 秒返回的 Hello Flutter 的 Future耘婚,
Future<String> fetchContent() async {
await Future.delayed(new Duration(seconds: 3));
return 'Hello Flutter';
}
/**在Dart中罢浇,有await標(biāo)記的運算,其結(jié)果值都是一個Future對象沐祷,
* 對于異步函數(shù)返回的 Future 對象嚷闭,如果調(diào)用者決定同步等待, 則需要在調(diào)用處使用 await 關(guān)鍵字赖临,
* 并且在調(diào)用處的函數(shù)體使用 async 關(guān)鍵字胞锰。
* Dart 中的 await 并不是阻塞等待,而是異步等待兢榨,Dart 會將調(diào)用體的函數(shù)也視作異步函數(shù)嗅榕,
* 將等待語句的上下文放入 Event Queue 中,一旦有了結(jié)果吵聪,Event Loop 就會把它從 Event Queue 中取出凌那,等待代碼繼續(xù)執(zhí)行。
*/
//await 與 async 有效區(qū)間只對調(diào)用上下文的函數(shù)有效吟逝,并不向上傳遞
testAwaitAndAsync() async {
String data = await fetchContent();
print('-----${data}-----'); //等待3秒后打印Hello Flutter 然后打印123
print('----123------');
}
Event Loop 完整版的流程圖
在 Dart 中帽蝶,實際上有兩個隊列,一個事件隊列(Event Queue)块攒,另一個則是微任務(wù)隊列(Microtask Queue)励稳。在每一次事件循環(huán)中,Dart 總是先去第一個微任務(wù)隊列中查詢是否有可執(zhí)行的任務(wù)囱井,如果沒有麦锯,才會處理后續(xù)的事件隊列的流程。
Isolate
Dart 也提供了多線程機(jī)制琅绅,即 Isolate。在 Isolate 中鹅巍,資源隔離做得非常好千扶,每個 Isolate 都有自己的 Event Loop 與 Queue料祠,Isolate 之間不共享任何資源,只能依靠消息機(jī)制通信澎羞,因此也就沒有資源搶占問題髓绽。
Isolate 通過發(fā)送管道(SendPort)實現(xiàn)消息通信機(jī)制。我們可以在啟動并發(fā) Isolate 時將主 Isolate 的發(fā)送管道作為參數(shù)傳給它妆绞,這樣并發(fā) Isolate 就可以在任務(wù)執(zhí)行完畢后利用這個發(fā)送管道給我們發(fā)消息了顺呕。
,在 Isolate 中括饶,發(fā)送管道是單向的:我們啟動了一個 Isolate 執(zhí)行某項任務(wù)株茶,Isolate 執(zhí)行完畢后,發(fā)送消息告知我們图焰。如果 Isolate 執(zhí)行任務(wù)時启盛,需要依賴主 Isolate 給它發(fā)送參數(shù),執(zhí)行完畢后再發(fā)送執(zhí)行結(jié)果給主 Isolate技羔,這樣雙向通信的場景我們?nèi)绾螌崿F(xiàn)呢僵闯?答案也很簡單,讓并發(fā) Isolate 也回傳一個發(fā)送管道即可藤滥。
跨組件通訊
Flutter 與 Android iOS 原生的通信有以下三種方式
BasicMessageChannel 實現(xiàn) Flutter 與 原生(Android 鳖粟、iOS)雙向通信 ,主要是傳遞字符串json等數(shù)據(jù)和一些半結(jié)構(gòu)體的數(shù)據(jù)拙绊,
MethodChannel 實現(xiàn) Flutter 與 原生原生(Android 向图、iOS)雙向通信, 用于傳遞方法調(diào)用
EventChannel 實現(xiàn) 原生原生(Android 时呀、iOS)向Flutter 發(fā)送消息 张漂,用于數(shù)據(jù)流(event streams)的通信
平臺視圖 Flutter端使用原生視圖
Flutter 提供了一個平臺視圖(Platform View)的概念。它提供了一種方法谨娜,允許開發(fā)者在 Flutter 里面嵌入原生系統(tǒng)(Android 和 iOS)的視圖
- 首先航攒,由作為客戶端的 Flutter,通過向原生視圖的 Flutter 封裝類(在 iOS 和 Android 平臺分別是 UIKitView 和 AndroidView)傳入視圖標(biāo)識符趴梢,用于發(fā)起原生視圖的創(chuàng)建請求漠畜;
- 然后,原生代碼側(cè)將對應(yīng)原生視圖的創(chuàng)建交給平臺視圖工廠(PlatformViewFactory)實現(xiàn)坞靶;
- 最后憔狞,在原生代碼側(cè)將視圖標(biāo)識符與平臺視圖工廠進(jìn)行關(guān)聯(lián)注冊,讓 Flutter 發(fā)起的視圖創(chuàng)建請求可以直接找到對應(yīng)的視圖創(chuàng)建工廠彰阴。
class SampleView extends StatelessWidget {
@override
Widget build(BuildContext context) {
//使用Android平臺的AndroidView瘾敢,傳入唯一標(biāo)識符sampleView
if (defaultTargetPlatform == TargetPlatform.android) {
return AndroidView(viewType: 'sampleView');
} else {
//使用iOS平臺的UIKitView,傳入唯一標(biāo)識符sampleView
return UiKitView(viewType: 'sampleView');
}
}
}
我們分別創(chuàng)建了平臺視圖工廠和原生視圖封裝類,并通過視圖工廠的 create 方法簇抵,將它們關(guān)聯(lián)起來
//視圖工廠類
class SampleViewFactory extends PlatformViewFactory {
private final BinaryMessenger messenger;
//初始化方法
public SampleViewFactory(BinaryMessenger msger) {
super(StandardMessageCodec.INSTANCE);
messenger = msger;
}
//創(chuàng)建原生視圖封裝類庆杜,完成關(guān)聯(lián)
@Override
public PlatformView create(Context context, int id, Object obj) {
return new SimpleViewControl(context, id, messenger);
}
}
//原生視圖封裝類
class SimpleViewControl implements PlatformView {
private final View view;//緩存原生視圖
//初始化方法,提前創(chuàng)建好視圖
public SimpleViewControl(Context context, int id, BinaryMessenger messenger) {
view = new View(context);
view.setBackgroundColor(Color.rgb(255, 0, 0));
}
//返回原生視圖
@Override
public View getView() {
return view;
}
//原生視圖銷毀回調(diào)
@Override
public void dispose() {
}
}
protected void onCreate(Bundle savedInstanceState) {
...
Registrar registrar = registrarFor("samples.chenhang/native_views");//生成注冊類
SampleViewFactory playerViewFactory = new SampleViewFactory(registrar.messenger());//生成視圖工廠
registrar.platformViewRegistry().registerViewFactory("sampleView", playerViewFactory);//注冊視圖工廠
}