為您解碼網(wǎng)站建設(shè)的點點滴滴
發(fā)表日期:2018-12 文章編輯:小燈 瀏覽次數(shù):4320
國慶后面兩天在家學習整理了一波flutter,基本把能擼過能看到的代碼都過了一遍,此文篇幅較長,建議保存(star)再看。傳送門: Nealyang personal blog
畢竟前端出生,找(qi)到(shi)了(bing)感(mei)覺(ru)后(men),其實就是一個UI框架,只不過他的引擎基于C++,底層基于Skia渲染,DartVM虛擬機以及Text and so on...
2018年6月21日Google發(fā)布Flutter首個release預(yù)覽版,作為Google baba大力推出的一種全新的響應(yīng)式,跨平臺,高性能的移動開發(fā)框架,勢必會火一波~沒別的,就是因為Google baba,當然,從目前看來也的確越來越火了。
Questions tagged [flutter]
本文我們從介紹flutter基本概念到梳理常用Widget到常用app demos編寫到放棄,希望可以幫助每一個像我一樣的初學者。有誤地方還望大神不吝賜教~
img國際慣例,吹一波先~
直接移步Flutter官宣ppt
作為Flutter入門文章,Dart必然少不了,當然,作為Flutter入門篇,Dart預(yù)發(fā)基礎(chǔ)必然不會過多介紹。
Dart入門傳送門:Dart or Dart2,或者從Dart中文網(wǎng)中學習也不錯其實.
這里我們說說為啥是Dart。
許多語言科學家認為,一個人說的自然語言會影響他們的思維方式。早起Flutter團隊評估了十多種語言最終選擇了Dart,因為它符合他們構(gòu)建用戶界面的方式。
具體選擇Dart的原因,以及向了解Dart的,移步為什么Flutter會選擇 Dart
剛開始接觸flutter心中難免會有疑惑,不是已經(jīng)有RN、Weex等各種跨平臺移動開發(fā) 了,flutter優(yōu)勢在哪呢? 看我從網(wǎng)上盜的圖!
img圖片來源
有一種說法認為函數(shù)式語言和命令式語言的不同在于命令式語言是給計算機下達指令而函數(shù)式語言是向計算機描述邏輯。這種思路在Flutter UI中得到了體現(xiàn)。Flutter不提倡去操作UI,它當然也基本不會提供操作View的API,比如我們常見的類似TextView.setText(),Button.setOnClick()這種是不會有的。對界面的描述是可以數(shù)據(jù)化的(類似XML,JSON等),而對界面的操作是很難數(shù)據(jù)化的,這很重要,響應(yīng)式需要方便可持續(xù)的將數(shù)據(jù)映射成界面。
在Flutter中用Widget來描述界面,Widget只是View的“配置信息”,編寫的時候利用Dart語言一些聲明式特性來得到類似結(jié)構(gòu)化標記語言的可讀性。Widget根據(jù)布局形成一個層次結(jié)構(gòu)。每個widget嵌入其中,并繼承其父項的屬性。沒有單獨的“應(yīng)用程序”對象,相反,根widget扮演著這個角色。在Flutter中,一切皆為Widget,甚至包括css樣式。
<div class="greybox"> Lorem ipsum </div>.greybox { background-color: #e0e0e0; /* grey 300 */ width: 320px; height: 240px; font: 900 24px Georgia; }
在flutter中我們編寫為
var container = new Container( // grey box child: new Text( "Lorem ipsum", style: new TextStyle( fontSize: 24.0 fontWeight: FontWeight.w900, fontFamily: "Georgia", ), ), width: 320.0, height: 240.0, color: Colors.grey[300], );
可以看到我們css樣式中的font定義的樣式,在flutter中,需要new TextStyle
,TextStyle就是一個Widget,并且樣式必須作用與Container中的child:text上,不存在web中樣式的繼承。
剛開始接觸的同學就類比于react中扯的,一切皆為組件吧,其實widget是對頁面UI的一種描述。他功能類有點似于android中的xml,react中的jsx。widget在渲染的時候會轉(zhuǎn)化成element。Element相比于widget增加了上下文的信息。element是對應(yīng)widget,在渲染樹的實例化節(jié)點。由于widget是immutable的,所以同一個widget可以同時描述多個渲染樹中的節(jié)點。但是Element是描述固定在渲染書中的某一個特定位置的點。簡單點說widget作為一種描述是可以復(fù)用的,但是element卻跟需要繪制的節(jié)點一一對應(yīng)。那element是最終渲染的view么?抱歉,還不是。element繪制時會轉(zhuǎn)化成rendObject。RendObject才是真正經(jīng)過layout和paint并繪制在屏幕上的對象。在flutter中有三套渲染相關(guān)的tree,分別是:widget tree, element tree & rendObject tree。三者的渲染流程如下:
img有沒有一種 jsx -> virtual Dom -> real dom滴感覺呢~
咳咳,后面會介紹基礎(chǔ)常用的Widget配合一些demo,大家可能對這個體會就會更加清晰一些。
Flutter中很多借鑒了react的思想,甚至包括后面會說到的state。
Widget本身通常由許多更小的、單一的小小widget組成,甚至小到它單一下來并沒有什么作用的感覺,這些Widget幾幾組合形成一個強大的自定義的大大Widget。
比如一個Container,對于Web FE來說可能就是個div,而他就是由很多的widget組成,這些widget負責布局、繪制、定位、大小等。我們可以使用各種姿勢來組合他們而不是繼承他們。類層次結(jié)構(gòu)很淺且很寬,可以最大限度的增加可能組合的數(shù)量
img上面的圖片是Flutter分層框架結(jié)構(gòu)圖,對大部分開發(fā)者而言,最常用的是Widgets層,屏幕上可見與不可見的元素都由Widgets層實現(xiàn),這些元素被稱為Widget。在Widgets層在上層,有兩個現(xiàn)成的Widget庫,Material庫即Material Design的Widget庫,Material Design是Google I/O 2014發(fā)布的設(shè)計語言,目前成為統(tǒng)一Android Mobile、Android Table、Desktop Chrome等平臺的設(shè)計語言規(guī)范。Cupertino庫則是一個模仿iOS設(shè)計風格的Widget庫。
底層是Flutter Engine虛擬機,在這一層次中需要了解一下的是Skia,Skia是Google研發(fā)的包括圖形、文本、圖像、動畫等多方面的圖形引擎,不僅用于Google Chrome瀏覽器,Android系統(tǒng)也采用Skia作為繪圖處理引擎。
GPU渲染:
state生命周期:
作為初學者看上面的圖有點云里霧里的,且先做到心里有數(shù)~
關(guān)于Flutter環(huán)境問題這里不再贅述
此后~大量代碼來襲
國際慣例,hello world
import 'package:flutter/material.dart';class MyAppBar extends StatelessWidget{ MyAppBar({this.title});// final Widget title;@override Widget build(BuildContext context){ return new Container( height: 56.0, padding: const EdgeInsets.symmetric(horizontal:8.0), decoration: new BoxDecoration( color:Colors.blue[400] ), child: Row( children: <Widget>[ new IconButton( icon:new Icon(Icons.menu), tooltip:'Navigation menu', onPressed: (){ print('點擊Menu'); }, ), new Expanded( child:new Center( child:title ) ), new IconButton( icon:Icon(Icons.search), tooltip:'Search', onPressed: (){ print('點擊搜索按鈕'); }, ) ], ), ); } }class MyScaffold extends StatelessWidget{ @overrideWidget build(BuildContext context){ return Material( child: new Column( children:<Widget>[ new MyAppBar( title:new Text( 'Hello World', style:Theme.of(context).primaryTextTheme.title), ), new Expanded( child:new Center( child:Text('Hello World!!!') ) ) ] ), ); } }void main(){ runApp( new MaterialApp( title:'My app', home:new MyScaffold() ) ); }
img 這個UI的確有些對不起人了,上面的title被擋住了。且先不去適配,后面我們使用Material提供的Scaffold即可
第一個例子,重點說下代碼(用過的Widget記?。?/p>
new
可有可無。String title
然后在類中自己去new Title(title)
import 'package:flutter/material.dart';void main() => runApp(new MyApp());class MyApp extends StatelessWidget { // app的根Widget @override Widget build(BuildContext context) { return new MaterialApp( title: 'Flutter Demo', theme: new ThemeData( // 這是設(shè)置的app主題 // 運行后你可以看到app有一個藍色的toobar,并且在不退出app的情況下修改代碼會熱更新 primarySwatch: Colors.blue, ), home: new MyHomePage(title: 'Flutter Demo Home Page'), ); } }class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key);// 這是應(yīng)用中一個基類,繼承自StateFulWidget,意味著這個類擁有一個state對象,該對象里的一些字段會影響app的UI // 這個類是state的一些配置項。通過構(gòu)造函數(shù)來獲取值,這個值一般在State中消費,并且使用final關(guān)鍵字。其實類似于react中的defaultPropsfinal String title;@override _MyHomePageState createState() => new _MyHomePageState(); }class _MyHomePageState extends State<MyHomePage> { int _counter = 0;void _incrementCounter() { setState(() { // setState方法告訴Flutter,這個State中有些值發(fā)生了變化,以便及時將新值更新到UI上, // 如果我不通過setState更改_count字段,那么Flutter并不會調(diào)用build匿名函數(shù)去更新界面 _counter++; }); }@override Widget build(BuildContext context) { // build方法會在每次setState的時候重新運行,例如上面的_incrementCounter方法被調(diào)用 //Flutter已經(jīng)被優(yōu)化了重新構(gòu)建的方法,所以你只會去更新需要去更新的部分,不必去單獨更新里面的一些更細小的widget,類似于React中diff return new Scaffold( appBar: new AppBar( // 這里我們使用從App.build方法中初始化MyHomePage時候傳入的title值來設(shè)置我們的title title: new Text(widget.title), ), body: new Center( // Center是一個布局Widget,他只有一個child(區(qū)分row or cloumn等是children),并且會將child的widget居中顯示 child: new Column( // Column也是一個布局widget,他可以有多個子widget // Column 有很多的屬性去控制他的大小以及子widget的位置,這里我們使用mainAxisAlignment來讓children在垂直線上居中, // 這里的主軸就是垂直的,因為Column就是垂直方向的,這里可以大概想象為display:flex,flex-directions:column,align-item,justifyContent。。。 mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ new Text( 'Hello World!', style:TextStyle( fontSize:24.0, color: Colors.redAccent, decorationStyle:TextDecorationStyle.dotted, fontWeight: FontWeight.bold, fontStyle: FontStyle.italic, decoration: TextDecoration.underline ) ), new Text( 'You have pushed the button this many times:', ), new Text( '$_counter', style: Theme.of(context).textTheme.display1, ), ], ), ), floatingActionButton: new FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: new Icon(Icons.add), ),//最后這個逗號有利于格式化代碼 ); } }
img 注釋上基本已經(jīng)加了,這里重點說下,StatefulWidget和StatelessWidget.
還有關(guān)于key的部分這里就不做介紹了,其實就類似與react中key的概念,便于diff,提高效率的。
具體可以查看 Key
OK,強化下編寫界面,咱再來些demo吧~
自己寫的后,發(fā)現(xiàn)跟官網(wǎng)實現(xiàn)方式不同,代碼地址
具體實現(xiàn)可以參照官網(wǎng)教程
這里不再贅述,下面我們說下對于布局的理解和感受以及常用布局widget。
從一個前端的角度來說,說到畫界面,可能還是對布局這塊比較敏感
img[圖片上傳失敗...(image-1f7c51-1543900799550)])
當然,這里我們還是說下目前常用的flex布局,基本拿到頁面從大到小拆分后就是如上圖。
所以Widget布局其實也就是Row和Column用的最多,然后由于Flutter一切皆為組件的理念,可能會需要用到別的類css布局的Widget,譬如:Container。其實咱就理解為塊元素吧!
下面簡單演示下一些常用的Widget,這里就不在贅述Row和Column了。傳送門:布局Widget
可以添加padding、margin、border、background color、通常用于裝飾其他Widget
img代碼鏈接 Nealyang/flutter
class MyHomePage extends StatelessWidget{ @override Widget build(BuildContext context){ Container cell (String imgSrc){ return new Container( decoration: new BoxDecoration( border:Border.all(width:6.0,color:Colors.black38), borderRadius: BorderRadius.all(const Radius.circular(8.0)) ), child: Image.asset( 'images/$imgSrc', width: 180.0, height: 180.0, fit: BoxFit.cover, ), ); }return Container( padding: const EdgeInsets.all(10.0), color: Colors.grey, child: new Column( mainAxisSize: MainAxisSize.min, children:<Widget>[ new Container( margin: const EdgeInsets.only(bottom:10.0), child: new Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children:<Widget>[ cell('1.jpg'), cell('2.jpg') ] ), ), new Container( margin: const EdgeInsets.only(bottom:10.0), child: new Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children:<Widget>[ cell('3.jpg'), cell('4.jpg') ] ), ), ] ), ); } }
該布局中每個圖像使用一個Container來添加一個圓形的灰色邊框和邊距。然后使用容器將列背景顏色更改為淺灰色。
可滾動的網(wǎng)格布局,理解為display:grid
GridView提供兩個預(yù)制list,當GridView檢測到內(nèi)容太長時,會自動滾動。如果需要構(gòu)建自定義grid,可是使用GridView.count
或 GridView.extent
來指定允許設(shè)置的列數(shù)以及指定項最大像素寬度。
代碼鏈接 Nealyang/flutter
List<Container> _buildGridTileList(int count) {return new List<Container>.generate( count, (int index) => new Container(child: new Image.asset('images/${index+1}.jpg'))); }Widget buildGrid() { return new GridView.extent( maxCrossAxisExtent: 150.0, padding: const EdgeInsets.all(4.0), mainAxisSpacing: 4.0, crossAxisSpacing: 4.0, children: _buildGridTileList(10)); }class MyHomePage extends StatelessWidget{ @override Widget build(BuildContext context){ returnnew Center( child: buildGrid(), ); } }
如上是指定maxCrossAxisExtent
,我們可以直接去指定列數(shù),例如官網(wǎng)的代碼實例:
new GridView.count( primary: false, padding: const EdgeInsets.all(20.0), crossAxisSpacing: 10.0, crossAxisCount: 3, children: <Widget>[ const Text('He\'d have you all unravel at the'), const Text('Heed not the rabble'), const Text('Sound of screams but the'), const Text('Who scream'), const Text('Revolution is coming...'), const Text('Revolution, they...'), ], )
通過crossAxisCount
直接指定列數(shù)。
層疊布局,position為absolute的感jio~
使用Stack來組織需要重疊的widget。widget可以完全或部分重疊底部widget。子列表中的第一個widget是base widget; 隨后的子widget被覆蓋在基礎(chǔ)widget的頂部。Stack的內(nèi)容不能滾動。有點類似于weex中的設(shè)置了absolute的感覺。底部組件永遠在上面組件的上面。
可滾動的長列表,可以水平或者垂直。
Material風格組件,卡片,AntD啥的組件庫經(jīng)常會出現(xiàn)的那種組件。
在flutter中,Card具有圓角和陰影,更改Card的elevation屬性可以控制陰影效果。
Material風格組件,我理解為常用的列表Item的樣式,最多三行文字,可選的行前、行尾的圖標
img代碼鏈接 Nealyang/flutter
從目前我個人淺薄的Flutter技能來說,最大的困難可能是找不到合適的Widget去實現(xiàn)想要的布局或者效果,甚至包括css樣式作用于那個Widget,譬如Opacity是一個widget而不是一個css樣式~
所以對于Flutter,我們還是要多折騰,多些demo,類似網(wǎng)上很多仿xxxApp等~
對于畫界面,更多的還可以參看下官網(wǎng)教程:Flutter for Web開發(fā)者
Flutter一切基于Widget,搞定widget就好比,搞定英語單詞一樣,單詞、詞組都賊6了還怕英語?
別急別急,借用張晟哥的圖來給大家消消火氣~
widgets所以說,F(xiàn)lutter有一個龐大的組件體系,需要花費非常多的時間去梳理。
!更重要的是:多實踐
本來最后一章是自己寫的一個demo的講解~
可惜時間評估不準確,漏評估了假期惰性。。。考慮篇幅,后面補上仿XXX的Demo吧~~
imgDemo 推薦
日期:2018-10 瀏覽次數(shù):7247
日期:2018-12 瀏覽次數(shù):4319
日期:2018-07 瀏覽次數(shù):4868
日期:2018-12 瀏覽次數(shù):4168
日期:2018-09 瀏覽次數(shù):5491
日期:2018-12 瀏覽次數(shù):9915
日期:2018-11 瀏覽次數(shù):4798
日期:2018-07 瀏覽次數(shù):4574
日期:2018-05 瀏覽次數(shù):4851
日期:2018-12 瀏覽次數(shù):4315
日期:2018-10 瀏覽次數(shù):5132
日期:2018-12 瀏覽次數(shù):6206
日期:2018-11 瀏覽次數(shù):4453
日期:2018-08 瀏覽次數(shù):4585
日期:2018-11 瀏覽次數(shù):12623
日期:2018-09 瀏覽次數(shù):5570
日期:2018-12 瀏覽次數(shù):4823
日期:2018-10 瀏覽次數(shù):4178
日期:2018-11 瀏覽次數(shù):4522
日期:2018-12 瀏覽次數(shù):6057
日期:2018-06 瀏覽次數(shù):4002
日期:2018-08 瀏覽次數(shù):5427
日期:2018-10 瀏覽次數(shù):4453
日期:2018-12 瀏覽次數(shù):4516
日期:2018-07 瀏覽次數(shù):4355
日期:2018-12 瀏覽次數(shù):4494
日期:2018-06 瀏覽次數(shù):4376
日期:2018-11 瀏覽次數(shù):4369
日期:2018-12 瀏覽次數(shù):4242
日期:2018-12 瀏覽次數(shù):5275
Copyright ? 2013-2018 Tadeng NetWork Technology Co., LTD. All Rights Reserved.