疫情剛好靜下心來回歸過去的工作刺下,博主要開始學(xué)習(xí)flutter绑嘹,博主會邊學(xué)習(xí)邊實戰(zhàn)項目連續(xù)連貫實現(xiàn),大約一周會出一篇橘茉,源碼地址,開源不易圾叼,麻煩動手點星,謝謝捺癞,本開源不做商業(yè)使用夷蚊,里面涉及用到api接口資源等只供學(xué)習(xí),項目開發(fā)會分博客文章髓介,請點擊鏈接到對應(yīng)的文章中瀏覽惕鼓,建議其實萬變不離其中,說到底還是離不開原生唐础,學(xué)習(xí)之前往補充原生知識箱歧,本片主要講的是引導(dǎo)頁矾飞,其中涉及到技術(shù)點層疊布局、相對定位呀邢、容器洒沦、定時器、異步數(shù)據(jù)更新价淌,再次感謝大家能耐心觀看申眼,謝謝!
@[TOC](Flutter 開始征途)
1 布局詳解
1.1 Stack層疊布局
在flutter中層疊布局就是疊加效果蝉衣,如果做過安卓的話括尸,那么就知道FrameLayout,其實就依次往上一層view層疊效果
ios 最原始addSubview,其實就是一個層疊布局效果病毡。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView * view1 = [[UIView alloc]initWithFrame:CGRectMake(20, 40, 200, 200)];
view1.backgroundColor=[UIColor redColor];
UIView * view2 = [[UIView alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
view2.backgroundColor=[UIColor greenColor];
[view1 addSubview:view2];
[self.view addSubview:view1];
}
下面是flutter的實現(xiàn)濒翻,children包含子組件數(shù)組,依次視圖疊加效果啦膜,而Positioned是用來做絕對位置的有送,左右頭底,四個點需要自己計算適配哦僧家。
1.2 Align 相對布局詳解
1.2.1 安卓相對布局
在安卓中大家應(yīng)該很熟悉RelativeLayout,這可是我們安卓最常用最無敵布局神器雀摘,再復(fù)雜的業(yè)務(wù)都是浮云。
布局 | 方向 |
---|---|
layout_centerHorizontal | 水平居中 |
layout_alignParentRight | 右對齊 |
layout_alignParentLeft | 左對齊 |
layout_alignParentTop | 頂部對齊 |
layout_alignBottom | 居底部對齊 |
layout_centerInParent | 居中 |
layout_centerVertical | 豎向?qū)R |
1.2.2 ios相對布局
ios我們開發(fā)的時候啸臀,我們用的Autolayout布局届宠,解析如下:
UIView *subView = [[UIView alloc]init];
subView.backgroundColor = [UIColor greenColor];
[self.view addSubview:subView];
subView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1. constant:150]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:0.3 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:0.3 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:subView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1. constant:100]];
上面方法參數(shù)說明:
第一個參數(shù):指定約束左邊的視圖view1
第二個參數(shù):指定view1的屬性attr1
第三個參數(shù):指定左右兩邊的視圖的關(guān)系relation
第四個參數(shù):指定約束右邊的視圖view2
第五個參數(shù):指定view2的屬性attr2
第六個參數(shù):指定一個與view2屬性相乘的乘數(shù)multiplier
第七個參數(shù):指定一個與view2屬性相加的浮點數(shù)constant
依據(jù)的公式是:view1.attr1 = view2.attr2*multiplier +constant
布局 | 方向 |
---|---|
NSLayoutAttributeLeft | 視圖的左邊 |
NSLayoutAttributeRight | 視圖的右邊 |
NSLayoutAttributeTop | 視圖的上邊 |
NSLayoutAttributeBottom | 視圖的下邊 |
NSLayoutAttributeLeading | 視圖的前邊 |
NSLayoutAttributeTrailing | 視圖的后邊 |
NSLayoutAttributeWidth | 視圖的寬度 |
NSLayoutAttributeHeight | 視圖的高度 |
NSLayoutAttributeCenterX | 視圖的中點的X值 |
NSLayoutAttributeCenterY | 視圖中點的Y值 |
NSLayoutAttributeBaseline | 視圖的基準線 |
NSLayoutAttributeNotAnAttribute | 無屬性 |
NSLayoutRelation的類型:
NSLayoutRelationLessThanOrEqual 關(guān)系小于或等于
NSLayoutRelationEqual 視圖關(guān)系等于
NSLayoutRelationGreaterThanOrEqual 視圖關(guān)系大于或等于
這里要說明一下烁落,設(shè)置約束之前必須要求確保子視圖添加到了父視圖上了(如:[self.view addSubview:subView]),并且被約束的視圖的translatesAutoresizingMaskIntoConstraints = NO,不然就會發(fā)生程序crash乘粒。
1.2.3 flutter Align相對布局
Align 組件可以調(diào)整子組件的位置,并且可以根據(jù)子組件的寬高來確定自身的的寬高伤塌,定義如下:
首先你要在外部套一個容器灯萍,或者像安卓要套一個RelativeLayout一樣,區(qū)別目前么發(fā)現(xiàn)如何自適應(yīng)每聪,必須設(shè)置區(qū)域大小旦棉。
布局 | 方向 |
---|---|
Alignment.topLeft | 頭部左對齊 |
Alignment.topRight | 頭部右對齊 |
Alignment.topCenter | 頭部居中 |
Alignment.centerLeft | 居中左對齊 |
Alignment.centerRight | 居中右對齊 |
Alignment.center | 居中對齊 |
Alignment.bottomLeft | 底部左對齊 |
Alignment.bottomCenter | 底部居中 |
Alignment.bottomRight | 底部右對齊 |
new Positioned(
child: Container(
child: Align(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Image.asset('images/splash_confirm.png'),
Text(
"立即開啟",
style: TextStyle(
fontSize: 12,
color: Colors.white,
fontFamily: 'Raleway',
//2.不繼承默認樣式
decorationStyle: TextDecorationStyle.dashed),
)
],
),
alignment: Alignment.bottomCenter,
),
margin: EdgeInsets.only(bottom: 100),
),
height: pageTotalSize > 0 && (pageViewIndex == pageTotalSize - 1)
? height
: 0,
width: width)
alignment : 需要一個AlignmentGeometry類型的值,表示子組件在父組件中的起始位置药薯。
AlignmentGeometry 是一個抽象類绑洛,它有兩個常用的子類:Alignment和 FractionalOffset,我們將在下面的示例中詳細介紹童本。
widthFactor和heightFactor是用于確定Align 組件本身寬高的屬性真屯;
它們是兩個縮放因子,會分別乘以子元素的寬穷娱、高绑蔫,最終的結(jié)果就是Align 組件的寬高运沦。如果值為null,則組件的寬高將會占用盡可能多的空間配深。
2 定時器
啟動頁會有定時器携添,定時幾秒后操作,下面會舉例說明安卓篓叶、ios烈掠、flutter實現(xiàn)不同。
2.1 安卓定時器
第一種方式
Timer timer = new Timer(); //創(chuàng)建一個定時器對象
TimerTask task = new TimerTask()
timer.schedule(task,0,10000); //啟動定時器
第二種方式
//參數(shù)1:計時總時間澜共,參數(shù)2:每次扣除時間數(shù)
CountDownTimer cdt = new CountDownTimer(10000, 100)
{
@Override
public void onTick(long millisUntilFinished)
{
}
@Override
public void onFinish() {
}
};
cdt.start();
其它方式 alarmService向叉、thread、Handler嗦董。
2.2 ios定時器
第一種
NSTimer *timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(test) userInfo:nil repeats:YES];
// 將定時器添加到runloop中母谎,否則定時器不會啟動
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
// 停止定時器
[timer invalidate];
第二種
// 創(chuàng)建displayLink
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(test:)];
// 將創(chuàng)建的displaylink添加到runloop中,否則定時器不會執(zhí)行
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
// 停止定時器
[displayLink invalidate];
displayLink = nil;
2.3 flutter 定時器
Timer定時器記得一定要在initState京革,這個函數(shù)就是初始化狀態(tài)奇唤,你就可以理解為安卓的onCreate()生命周期、ios的initWithCoder狀態(tài)一樣匹摇,build就是一個構(gòu)建widget咬扇,其實就可以理解為安卓xml,但是這里有很大區(qū)別廊勃,就是更新ui懈贺、初始化、initState()坡垫、didUpdateWidget()梭灿、setState()、didChangeDependencies()都會調(diào)用哦冰悠,當(dāng)然布局上更新的數(shù)據(jù)怎么做堡妒,一定是在build構(gòu)建寫對應(yīng)數(shù)據(jù)變量,其實思想就跟mvvm溉卓,數(shù)據(jù)驅(qū)動刷新ui一樣皮迟。
@override
void initState() {
// TODO: implement initState
super.initState();
splashBuilder = fetchPost();
initCountDown();
WidgetsBinding.instance.addObserver(this);
}
initCountDown() {
// 只在倒計時結(jié)束時回調(diào)
Timer.periodic(new Duration(seconds: 1), (timer) {
if (timer.tick == 5) {
setState(() {
nextStr = '跳過';
});
timer.cancel();
print(nextStr);
} else {
setState(() {
nextStr = '${countTime--}s跳過';
});
print(nextStr);
}
});
}
new Positioned(
child: new GestureDetector(
child: new Container(
padding: const EdgeInsets.only(
left: 10, top: 2, right: 10, bottom: 2),
decoration: new ShapeDecoration(
color: !isClicking1 ? Colors.white : Color(0xff898989),
shape: StadiumBorder(
side: BorderSide(
color: Color(0xff898989),
style: BorderStyle.solid,
width: 1))),
child: Text(nextStr),
),
onTap: () {
print('onTap 跳過');
},
onTapUp: (TapUpDetails) {
print('onTapUp 跳過');
upDataButtonState(false);
},
onTapDown: (TapUpDetails) {
print('onTapDown 跳過');
upDataButtonState(true);
},
onTapCancel: () {
print('onTapCancel 跳過');
upDataButtonState(false);
},
),
top: 10 + statebar_height,
right: 10)
3 網(wǎng)絡(luò)異步更新
3.1 網(wǎng)絡(luò)使用詳解
目前在flutter使用最多就是Dio庫,在安卓都是用的是Reftrofit,ios AFNetWork桑寨,好像git有個flutter的reftroft對應(yīng)插件伏尼。dio鏈接
首先引用dio插件,我們要做一個啟動頁就要有網(wǎng)絡(luò)申請尉尾,直接上dio代碼爆阶,get獲取圖片集合,然后網(wǎng)絡(luò)解析加載。
Future<SplashEntityEntity> fetchPost() async {
var dio = Dio();
Response response;
response = await dio
.get(WanAndroidApi.OtherCategory, queryParameters: {"type": 1});
// print(JsonConvert.fromJsonAsT<SplashEntityEntity>(response.data)
// .result
// .elementAt(0)
// .album10001000);
return JsonConvert.fromJsonAsT<SplashEntityEntity>(response.data);
}
3.2 解析數(shù)據(jù)詳解
3.2.1 官方推薦解析
首先扰她,打開JSON to Dart兽掰,JSON to Dar
如下圖所示
Map splashMap = JSON.decode(json);
var splash = new Splash.fromJson(userMap);
print('Howdy, ${splash.code}!');
print('We sent the verification link to ${splash.message}.');
3.2.2 FlutterJsonBeanFactory插件
搜索安卓插件,然后重啟工具徒役,這下可以愉快玩耍哦孽尽。
右鍵會有一個插件,然后讓你一件生產(chǎn)對應(yīng)的dart解析文件哦忧勿。
解析就是一行代碼
JsonConvert.fromJsonAsT<SplashEntityEntity>(response.data)
4 異步
當(dāng)我們訪問接口的時候杉女,那么操作肯定需要異步,一邊訪問請求一邊給個進度條鸳吸,這樣體驗會更好熏挎,異步操作很重要,在flutter中異步很簡單晌砾,就是靠FutureBuilder和future配合坎拐,原生我們都是異步線程,然后刷新ui會切主線程刷新养匈。
Future<SplashEntityEntity> fetchPost() async {
var dio = Dio();
Response response;
response = await dio
.get(WanAndroidApi.OtherCategory, queryParameters: {"type": 1});
// print(JsonConvert.fromJsonAsT<SplashEntityEntity>(response.data)
// .result
// .elementAt(0)
// .album10001000);
return JsonConvert.fromJsonAsT<SplashEntityEntity>(response.data);
}
FutureBuilder<SplashEntityEntity>(
future: splashBuilder,
builder: (BuildContext content, AsyncSnapshot async) {
if (async.connectionState == ConnectionState.done) {
print("success");
return getPageView(async.data);
} else {
print("loading===");
return Container(
height: 100,
width: 100,
alignment: Alignment.center,
child: CircularProgressIndicator(
backgroundColor: Colors.blue,
valueColor: AlwaysStoppedAnimation(Colors.blue),
),
);
return CircularProgressIndicator(strokeWidth: 1);
}
},
)
- future:FutureBuilder依賴的Future哼勇,通常是一個異步耗時任務(wù)。
- initialData:初始數(shù)據(jù)呕乎,用戶設(shè)置默認數(shù)據(jù)积担。
- builder:這里對應(yīng)異步操作的時候?qū)?yīng)ui組件,其實都是wiget,注意網(wǎng)絡(luò)申請要放在初始化中操作猬仁,如果有定時器的話帝璧,setState更新狀態(tài)話,就會一直走build.
5 事件講解
首先這里要講的事件湿刽,在flutter簡直無法理解的烁,不知道創(chuàng)造這個人是怎么想的,萬物都是Widget,事件怎么能是widget叭爱,這真是無限地獄嵌套撮躁,代碼如下:
5.1 Listener事件
Listener(
child: Container(
alignment: Alignment.center,
color: Colors.blue,
width: 300.0,
height: 150.0,
child: Text(_event?.toString()??"",style: TextStyle(color: Colors.white)),
),
onPointerDown: (PointerDownEvent event) => setState(()=>_event=event),
onPointerMove: (PointerMoveEvent event) => setState(()=>_event=event),
onPointerUp: (PointerUpEvent event) => setState(()=>_event=event),
)
事件 | 介紹 |
---|---|
onPointerDown | 按下 |
onPointerUp | 抬起時觸發(fā) |
onPointerCancel | 取消觸摸時觸發(fā) |
onPointerMove | 移動時觸發(fā) |
5.2 手勢事件
手勢這個都是外面包一個漱病,其實這種涉及模式還能理解买雾,需要就外面擴展,不需要就不用寫杨帽,但是點擊事件listener完全不理解嘔吐漓穿。
GestureDetector(
child: CircleAvatar(child: Text("A")),
//垂直方向拖動事件
onVerticalDragUpdate: (DragUpdateDetails details) {
setState(() {
_top += details.delta.dy;
});
}
)
事件 | 介紹 |
---|---|
onTapUp | 點擊抬起 |
onTapDown | 點擊按下 |
onTapCancel | 取消觸摸時觸發(fā) |
onTap | 點擊回調(diào) |
-------- | ----- |
onDoubleTap | 雙擊 |
-------- | ----- |
onPanDown | 指針已接觸屏幕并可能開始移動 |
onPanStart | 指針已經(jīng)接觸屏幕并開始移動 |
onPanUpdate | 與屏幕接觸并移動的指針再次移動 |
onPanEnd | 先前與屏幕接觸并移動的指針不再與屏幕接觸,并且當(dāng)它停止接觸屏幕時以特定速度移動 |
onPanCancel | 先前觸發(fā) onPanDown 的指針未完成 |