flutter學習筆記

[TOC]javascript

實例 : 計數器

import 'package:flutter/material.dart';
//此行代碼做用是導入了Material UI組件庫。Material是一種標準的移動端和web端的視覺設計語言, Flutter默認提供了一套豐富的Material風格的UI組件。

//定義一個Main方法。Main方法中用簡單RunApp就能夠執行咱們定義的Widget
void main() => runApp(new MyApp());
/* Flutter寫成的都是UI主鍵。主要分 StatelessWidget 和 StatefulWidget 總體的入口能夠寫成 StatelessWidget */
class MyApp extends StatelessWidget {
  //這個Widget就是咱們App的最基層的Widget了。
  //傳入BuildContext給咱們使用。
  @override
  Widget build(BuildContext context) {
//這樣就能夠直接使用Flutter爲咱們封裝好的MaterialApp這個主題的了.從源碼能夠看到這個是個StatefulWidget
    //[home], [routes], [onGenerateRoute], or [builder] 這個主題下的這些方法不能都爲空!!
    return new MaterialApp(
      debugShowCheckedModeBanner:false ,  //去掉debug圖標

      title: 'Flutter Demo',
      //定義主題
      theme: new ThemeData(
        //主題顏色
      	primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: '首頁面'),
    );
  }
}
//---------------------------------------------------------------------------------
//statefulWidget ,由於Widget都是無狀態的,因此若是須要有狀態的話,`state`這個類來進行維持
class MyHomePage extends StatefulWidget {
  // 不管你接收不接收數據,MyHomePage({Key key}) : super(key : key);固定死的
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}
//---------------------------------------------------------------------------------
//flutter中狀態的持有類
class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    //這點和小程序很相似。調用setState進行狀態同步和刷新。若是不調用這個方法,只是改變值,界面不會發生變化
    setState(() {
      _counter++;
    });
  }
  //state中的`build`方法,會自動在`setState`方法後調用。
  @override
  Widget build(BuildContext context) {
  //Scaffold能夠理解成至關於一個html 它的body就是主要的內容。 
    //同時它實際上是知足MD的。因此它還能提供對應的組件
    //好比 AppBar .Drawer floatingButton等等
    return new Scaffold(
      appBar: new AppBar(
        //這裏,咱們從由App.build方法建立的my主頁對象中獲取值,並使用它設置appbar標題
        title: new Text(widget.title),
        centerTitle: true, //標題居中
      ),
      //這裏能夠初步的看到,若是想要佈局居中顯示,能夠先包裹一層`Center`
      body: new Center(
       //由於是豎直的佈局,因此再次包裹一層Column
        child: new Column(
          //使用這個屬性,讓元素在垂直線上居中豎直排列
          mainAxisAlignment: MainAxisAlignment.center,
         //children中傳入其餘組件
          children: <Widget>[
            new Text(
              '點了一次又一次:',
            ),
            new Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
       //右下角的一個小加號圓形按鈕
      floatingActionButton: new FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: new Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

複製代碼

1、前置知識

一、StatelessWidget類

不須要可變狀態的小部件。php

無狀態小部件是一個小部件,它經過構建一系列其餘小部件來更加具體地描述用戶界面,從而描述用戶界面的一部分。構建過程以遞歸方式繼續進行,直到用戶界面的描述徹底具體(例如,徹底由 RenderObjectWidget 組成,它描述具體的 RenderObject。html

當您描述的用戶界面部分不依賴於對象自己中的配置信息和其中構件被誇大的 BuildContext 時,無狀態小部件頗有用。對於能夠動態改變的組合,例如因爲具備內部時鐘驅動狀態,或取決於某些系統狀態,請考慮使用 StatefulWidget 。前端

無狀態小部件的構建方法一般只在如下三種狀況下調用:第一次將小部件插入樹中,第一次在小部件的父級更改其配置時以及第二次使用 InheritedWidget 時,它依賴於更改。vue

二、StatefulWidget類

具備可變狀態( state)的Widget(窗口小部件).java

狀態( state) 是能夠在構建Widget時同步讀取時 和 在Widget的生命週期期間可能改變的信息android

Widget實現者的責任就是 在狀態改變時經過 State.setState. 當即通知狀態ios

當您描述的用戶界面部分不依賴於對象自己中的配置信息和其中構件被誇大的 BuildContext 時,無狀態小部件頗有用。對於能夠動態改變的組合,例如因爲具備內部時鐘驅動狀態,或取決於某些系統狀態,請考慮使用StatefulWidget 。git

StatefulWidget 實例自己是不可變的,並將其可變狀態存儲在由 createState 方法建立的獨立狀態對象中 ,或者存儲在該狀態訂閱的對象中,例如 Stream 或 ChangeNotifier 對象,其引用存儲在 StatefulWidget 的最終字段中自己。github

該框架只要調用一個 StatefulWidget 就 調用 createState,這意味着若是該小部件已經插入到多個位置的樹中,那麼多個 State 對象可能與同一個 StatefulWidget 關聯。一樣,若是 StatefulWidget 從樹中移除,後來在樹再次插入時,框架將調用 createState 再建立一個新的目標,簡化的生命週期狀態的對象。

Stateful widget至少由兩個類組成:

  • 一個StatefulWidget類。
  • 一個 State類; StatefulWidget類自己是不變的,可是State類中持有的狀態在widget生命週期中可能會發生變化。若是須要主動改變State的狀態,須要經過setState()方法進行觸發,單純改變數據是不會引起UI改變的。

_MyHomePageState類是MyHomePage類對應的狀態類。看到這裏,讀者可能已經發現:和MyApp 類不一樣, MyHomePage類中並無build方法,取而代之的是,build方法被挪到了_MyHomePageState方法中。

三、widget

  • 在Flutter中,大多數東西都是widget(後同「組件」或「部件」),包括對齊(alignment)、填充(padding)和佈局(layout)等,它們都是以widget的形式提供,使用時,須要new ,但也能夠省略
  • Flutter在構建頁面時,會調用組件的build方法,widget的主要工做是提供一個build()方法來描述如何構建UI界面(一般是經過組合、拼裝其它基礎widget)。

四、簡單快捷鍵

stl: StatelessWidget

class extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      
    );
  }
}
複製代碼

stf:StatefulWidget

class extends StatefulWidget {
  @override
  _State createState() => _State();
}

class _State extends State<> {
  @override
  Widget build(BuildContext context) {
    return Container(
      
    );
  }
}
複製代碼

五、小知識點

**一、InkWell **

可讓一個不能點擊的組件變成能夠點擊的組件

例:

children: <Widget>[
          InkWell(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text("2019年09月09日"),
                Icon(Icons.arrow_drop_down)
              ],
            ),
            onTap: _showDataPicker,
          )
        ],
複製代碼

二、Divider 水平分割線

//寫在組件與組件之間
Divider(height:10,indent:0,color:Colors.red),
//height:分割線Widget的高,不是分割線自己效果的高,能夠達到兩個Widget 之間margin的效果
//indent:分割線左邊縮進長度
//color:分割線的顏色
複製代碼

**三、CircularProgressIndicator **

Material Design風格的循環進度條,旋轉以指示應用程序正忙。好比Http異步請求的時候表明正在獲取的狀態顯示。

/* * 圓形進度條 * 能夠在外面包一層SizedBox,間接改變進度條的大小 *const CircularProgressIndicator({ Key key, double value,//0~1的浮點數,用來表示進度多少;若是 value 爲 null 或空,則顯示一個動畫,不然顯示一個定值 Color backgroundColor,//背景顏色 Animation<Color> valueColor,//animation類型的參數,用來設定進度值的顏色,默認爲主題色 this.strokeWidth = 4.0,//進度條寬度 String semanticsLabel, String semanticsValue, }) */
複製代碼

2、項目結構and一些語法

建立項目:flutter create myapp

  • android android平臺相關的代碼 ios ios平臺相關的代碼 lib flutter相關的代碼,咱們寫的代碼主要放在lib下面,相似於vue項目中的src文件夾 test 測試 vue項目裏面也有test文件夾
    pubspec.yaml 配置文件 通常放一些第三方依賴 相似於vue項目中的paceage.json文件

  • 在lib下面有一個main.dart,是入口文件,相似於vue中的main.js文件。

  • runApp後面要跟上一個組件。vue中也有一個render(h) h(App)

  • 在flutter中,一切都是組件,在vue中,一切也是組件,vue中的組件中.vue文件。在flutter中一個組件就是一個類,須要一個組件就是須要new這個類,一般能夠把new省略不寫。

  • 在flutter中,結構和樣式寫在一塊兒,固然它不像vue中分結構和樣式,它是把樣式也定義成了組件。

  • 若是一個組件中包含了另外一個組件,那麼這個組件內部須要寫一個child

一、future

簡單來講,future就是一個Future對象,當執行return await......的時候,實際上返回的是一個延遲計算的Future對象,這個Future對象是Dart內置的,有本身的隊列策略,它將要操做的事件放入EventQueue中,在隊列中的事件按照 先進先出 的原則去逐一處理事件,當事件處理完成後,將結果返回給Future對象。

在這個過程當中涉及到了異步和等待:

  • 異步:就是不用阻塞當前線程,來等待該線程任務處理完成再去執行其餘任務。
  • 等待:await,聲明運算爲延遲執行

簡單示例:

void testFuture(){
  Future future = new Future(() => null);
  future.then((_){
    print("then");
  }).then((){
    print("whenComplete");
  }).catchError((_){
    print("catchError");
  });
} 		//執行結果爲 then whenComplete
複製代碼

future裏面有幾個函數: then:異步操做邏輯在這裏寫。 whenComplete:異步完成時的回調。 catchError:捕獲異常或者異步出錯時的回調。

在咱們平時開發中咱們是這樣用的,首先給咱們的函數後面加上async關鍵字,表示異步操做,而後函數返回值寫成Future,而後咱們能夠new一個Future,邏輯前面加上一個await關鍵字,而後可使用future.then等操做。下面是一個示例操做,爲了方便演示,這裏的返回值的null。平時開發中若是請求網絡返回的是json,咱們能夠把泛型寫成String;泛型也多是實體類(entity/domain),不過要作json轉實體類相關操做。

建立多個Future,執行順序和和建立Future的前後順序有關,若是隻是單獨的調用then,沒有嵌套使用的話,和調用then的前後順序無關。若是有多個then嵌套執行,先執行外面的then,而後執行裏面的then。

3、組件

一些經常使用的組件:

  • Text - 用於簡單地在屏幕上顯示文本的小部件。
  • Image - 用於顯示圖像。
  • Icon - 用於顯示Flutter的內置Material和Cupertino圖標。
  • Container - 在Flutter中,至關於div。容許在其中進行添加填充,對齊,背景,力大小以及其餘東西的加載。空的時候也會佔用0px的空間,這很方便。
  • TextInput - 處理用戶反饋。
  • Row, Column- 這些小部件顯示水平或垂直方向的子項列表。
  • Stack - 堆棧顯示一個孩子的列表。這個功能很像CSS中的'position'屬性。
  • Scaffold - 爲應用提供基本的佈局結構。它能夠輕鬆實現底部導航,appBars,後退按鈕等。

## 一、Scaffold(腳手架)

Scaffold 是 Material組件庫中提供的一個組件,它提供了默認的導航欄、標題和包含主屏幕widget樹(後同「組件樹」或「部件樹」)的body屬性。組件樹能夠很複雜。

  • AppBar,一般顯示在使用appBar屬性的應用頂部。

  • BottomNavigationBar,一般使用bottomNavigationBar 屬性在應用程序底部顯示,此功能向圖中所示同樣,沒法作定製,只能以圖片和文本形式存在。

  • BottomAppBar,一般使用bottomNavigationBar屬性顯示在應用程序的底部,用來構建定製化的底部導航欄或者佈局。

  • FloatingActionButton,圓形按鈕,一般使用floatingActionButton屬性顯示在應用程序的右下角。

  • SnackBar,一般顯示在應用程序底部附近的臨時通知。

## 二、MaterialApp

MaterialApp 是Material庫中提供的Flutter APP框架,經過它能夠設置應用的名稱、主題、語言、首頁及路由列表等。MaterialApp也是一個widget。通常寫在最頂層

三、Center

Center 能夠將其子組件樹對齊到屏幕中心

此例中, Center 子組件是一個Column 組件,Column的做用是將其全部子組件沿屏幕垂直方向依次排列;

## 四、floatingActionButton

floatingActionButton是頁面右下角的懸浮按鈕,它的onPressed屬性接受一個回調函數,表明它被點擊後的處理器。

五、Text

顯示單一靜態(不可編輯)的文本,相似Android中的TextView,IOS中的label。Flutter中Text提供了兩個構造函數,首先介紹下上面代碼中的new Center

  1. 文本對齊方式:TextAlign
  • center:居中
  • left:左對齊
  • right:右對齊
  1. 設置最多顯示行數:maxLines
  2. 文本溢出處理方式:overflow
  • clip:直接切斷
  • ellipsis:省略號
  • fade:漸變消失的效果,上下漸變

new Center:將子組件放置在本身的中心位置。

body:Center(
	child:Text(
		"123djasidhasuidsahyfgyugfdifgisadsadasd",
		textAlign:TextAlign.center,//對齊方式,還有 left、right
        maxLines:1,	//最多行數設置
        overflow:TextOverflow.ellipsis,//多於的內容如何處理,ellipsis是用省略號代替,fade是表示若是內容多餘會從上到下顏色漸漸變淡(灰)
        style:TextStyle(
        	fontSize:25,//或許寫爲25.0
            fontWeight: FontWeight.bold, //字體粗細,也能夠用w600等
            color:Color.fromRGBO(r,g,b,o),	//o表明透明度
            decoration:TextDecoration.underline,	//下劃線
            decorationStyle:TextDecorationStyle.solid,	//下劃線的樣式設置
            letterSpacing:5, //字體之間的間距
        ),
	),
),
複製代碼

六、Icon

new Icon(Icons.add)			是一個小加號圖標
複製代碼

在代碼中咱們會使用到不少的系統圖標,這些圖標的類型都是IconData,經過Icons便可獲取。

七、Image

Flutter中獲取圖片的方式提供瞭如下幾種:

加入圖片的方式
Image.asset: 加載項目資源目錄中的圖片,加入圖片後會增大打包的體積,使用相對路徑。
Image.network: 加載網絡資源圖片。
Image.file: 加載本地文件中的圖片,使用絕對路徑。
Image.memory: 加載Uint8List資源圖片。

fit屬性
fill 全圖顯示,充滿整個容器
contain 顯示圖片原比例
cover 保持圖片不變形的前提下,充滿整個容器
fitWidth 以寬度爲基準充滿容器
fitHeight 以高度爲基準充滿容器
scaleDown 效果和contain差很少,可是此屬性不容許顯示超過源圖片大小,可小不可大

圖片與color的混合模式(colorBlendMode)
color:要混合的顏色,單設置color的值無效
colorBlendMode:混合模式,設置混合的樣式

圖片的重複 (ImageRepeat)
repeat 鋪滿整個畫布
repeatX 橫向重複
repeatY 縱向重複
複製代碼

目前 Flutter的Image支持如下圖像格式:JPEG,PNG,GIF,動畫GIF,WebP,動畫WebP,BMP和WBMP。

color: Color.fromRGBO(2, 0, 200, 1),	//設置圖片顏色,爲了設置蒙版
colorBlendMode: BlendMode.xxx,		//設置蒙版,與上面設置的顏色相關
height:50,	
width:100,	//設置圖片的高度,寬度
repeat: ImageRepeat.repeatX ,  //設置當圖片小於空間時圖片的重疊方式
fit: BoxFit.fill,	//設置圖片的填充方式
複製代碼

使用本地圖片步驟

一、在project文件夾中新建一個images文件夾

二、在將要使用的圖片放入images文件夾

三、在pubspec.yaml文件中找到assets設置語句,添加以下內容

assets:
   - images/mao.jpg			//mao.jpg是添加圖片名
複製代碼

四、在頁面中使用

child: Image.asset('images/mao.jpg'),
複製代碼

八、Row 和 Column

Row:橫向佈局,Column:縱向佈局。這裏和Android中的LinearLayout(線性佈局)的 horizontal(橫向)與 vertical(縱向)是同樣的效果。

水平佈局Row

  1. 非靈活排列 根據Row子元素的大小進行佈局,若子元素不足,它會留有空隙;若子元素超出,它會警告。 例如:RaisedButton
  2. 靈活排列 使用Expanded解決有空隙的問題,在按鈕外加入Expanded就能夠了。

垂直佈局column

Column屬性與Row基本相同

  1. 主軸和副軸的辨識
  • 主軸(main軸):column組件主軸是垂直 row組件主軸是水平
  • 副軸(cross軸):column組件副軸是水平 row組件副軸是垂直
  1. mainAxisAlignment(主軸對齊方式)

  2. crossAxisAlignment(副軸對齊方式)

  3. 水平方向相對於屏幕居中 在child外層嵌套Center組件

處理富餘空間

MainAxisAlignmentCrossAxisAlignment簡介

MainAxisAlignment(主軸)和CrossAxisAlignment(交叉軸)經常使用於Row和Column控件中,主要是用來控制子控件排列的位置,並能夠配合textDirectionverticalDirection屬性來控制子控件排列的方向及改變MainAxisAlignmentCrossAxisAlignment的起始位置。 MainAxisAlignment(主軸)就是與當前控件方向一致的軸,而CrossAxisAlignment(交叉軸)就是與當前控件方向垂直的軸

mainAxisAlignment: MainAxisAlignment.xxx,

MainAxisAlignment {
  start,  //將子控件放在主軸的開始位置
  end,   //將子控件放在主軸的結束位置
  center,   //將子控件放在主軸的中間位置
  spaceBetween, //將主軸空白位置進行均分,排列子元素,手尾沒有空隙
  spaceAround, //將主軸空白區域均分,使中間各個子控件間距相等,首尾子控件間距爲中間子控件間距的一半
  spaceEvenly,  //將主軸空白區域均分,使各個子控件間距相等
}

CrossAxisAlignment {
  start,   //將子控件放在交叉軸的起始位置
  end,   //將子控件放在交叉軸的結束位置
  center,   //將子控件放在交叉軸的中間位置
  stretch,  //使子控件填滿交叉軸
  baseline,  //將子控件放在交叉軸的上,而且與基線相匹配(不經常使用)
}
複製代碼

示例:

child: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          new Icon(Icons.search,color:Colors.red),
          new Icon(Icons.home,color:Colors.blue),
          new Icon(Icons.select_all,color:Colors.lightBlue),
          new Icon(Icons.send,color:Colors.red),
        ],
      ),
複製代碼

九、container

Container:能夠添加padding、margin、border、background color、一般用於裝飾其餘Widget

  1. child的對齊方式: Alignment
  • 主要經過 left 、right、top、bottom、center、等進行組合,拼接位置,
  • 例如:bottomCenter、botomLeft、center、centerRight、topRight、topCenter、
  1. height:高度 width:寬度 color:顏色
  2. margin: 內邊距 EdgeInsets.all() EdgeInsets.fromLTRB()
  3. padding: 外邊距
  4. decoration: ( container 的修飾器,主要的功能是設置背景和邊框) decoration: BoxDecoration() border: container的邊框 border:Border.all(width:2.0,color:Colors.red)
body:Center(
	child:Container(
    	child:new Text("hello world",style:TextStyle(fontSize:40.0)),
        alignment:Alignment.center, //例如bottomLeft,topRight,centerLeft等等
        width:500,
        height:400,
        //color:Color.lightBlue, //背景顏色設置
        ///padding:const EdgeInsets.all(10),//all表示四邊都是10
        padding:const EdgeInsets.fromLTRB(10,20,30,40),//這是分別設置四邊
        margin:const EdgeInsets.all(10),
        //margin:EdgeInsets.only(top:10)
        decoration:new BoxDecoration(	//設置漸變色,但與color衝突,不能同時設置
        	gradient:const LineraGradient(
            	colors:[Colors.lightBlue,Colors.greenAccent,Colors.purple]
            ),
            border:Border.all(width:2.0,color:Colors.red),	//邊框設置
            borderRadius:BorderRadius.all(Radius.circular(100)),	//圓角設置
            
        )
    )
)
複製代碼

十、ClipOval

實現圓形組件

ClipOval例子(默認全圓角):
new ClipOval(
            child: Container(
              width: 100,
              height: 100,
              color: Colors.red,
            ),
          ),
複製代碼

十一、ListView

ListView內部的children使用了widget數組,由於是一個列表,因此它接受一個數組。

  1. listTite列表項
  • leading 列表前的icon圖標
  • title 列表標題
  1. 設置ListView的滾動方向(scrollDirection)
  • Axis.horizontal:橫向滾動或者叫水平方向滾動
  • Axis.vertical:縱向滾動或者叫垂直方向滾動

一、ListView.builder 循環顯示

scrollDirection: Axis.vertical, //設置滑動方向 Axis.horizontal 水平 默認 Axis.vertical 垂直
padding: EdgeInsets.all(10.0),   //內間距 
reverse: false,  //是否倒序顯示 默認正序 false 倒序true
primary: true,   //false,若是內容不足,則用戶沒法滾動 而若是[primary]爲true,它們老是能夠嘗試滾動。
itemExtent: 50.0,  //肯定每個item的高度 會讓item加載更加高效
shrinkWrap: true,  //內容適配
itemCount: list.length,  //item 數量,必選
physics: new ClampingScrollPhysics(),  //滑動類型設置
cacheExtent: 30.0,   //cacheExtent 設置預加載的區域 
controller ,   //滑動監聽

//要顯示的數據
itemBuilder: (BuildContext context,int i){
	return Text('${_list[i]["data"]["description"]}');
},
複製代碼

二、ListTile

  1. 利用了ListTile實現內部列表,任何容器組件其實均可以使用ListTile
  2. Divider( ) 用來生成一條分割線
this.leading,              // item 前置圖標
this.title,                // item 標題
this.subtitle,             // item 副標題
this.trailing,             // item 後置圖標
this.isThreeLine = false,  // item 是否三行顯示
this.dense,                // item 直觀感覺是總體大小
this.contentPadding,       // item 內容內邊距
this.enabled = true,
this.onTap,                // item onTap 點擊事件
this.onLongPress,          // item onLongPress 長按事件
this.selected = false,     // item 是否選中狀態
複製代碼

十二、GridView

  1. GridView默認構造函數能夠類比於ListView默認構造函數,適用於有限個數子元素的場景,由於GridView組件會一次性所有渲染children中的子元素組件;
  2. GridView.builder構造函數能夠類比於ListView.builder構造函數,適用於長列表的場景,由於GridView組件會根據子元素是否出如今屏幕內而動態建立銷燬,減小內存消耗,更高效渲染;
  3. GridView.count構造函數是GrdiView使用SliverGridDelegateWithFixedCrossAxisCount的簡寫(語法糖),效果徹底一致;
  4. GridView.extent構造函數式GridView使用SliverGridDelegateWithMaxCrossAxisExtent的簡寫(語法糖),效果徹底一致。
return GridView.count(
      crossAxisCount: 2, // 一行多少個 
      crossAxisSpacing: 10,  // 列與列的間隔
      mainAxisSpacing: 10, // 行與行的間隔
      padding: EdgeInsets.all(10),
      childAspectRatio: 1, // 設置寬高比
      children: this._getData(),
    );
複製代碼

1三、bottomNavigationBar 底部導航欄

一個完整的底部導航頁面:點擊底部導航,簡單的頁面跳轉

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

import '../views/Home.dart';
import '../views/search.dart';
import '../views/Setting.dart';


class Tabss extends StatefulWidget {
  @override
  _TabssState createState() => _TabssState();
}

class _TabssState extends State<Tabss> {
  int _currentIndex = 0;  
  List _pageList = <Widget>[		//底部跳轉的三個頁面
    Home(),
    Search(),
    Setting()
  ];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: this._pageList[this._currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        type: BottomNavigationBarType.fixed,  //默認只能寫三個,加上這句話能夠配置多個
        fixedColor: Colors.red, //選中時的顏色設置
        iconSize: 20, //icon圖標大小
        currentIndex: this._currentIndex,   //設置顯示導航的索引
        onTap: (int index){     //onTap點擊事件
          setState(() {
           this._currentIndex = index; 
          });
        },
        items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.home),   //這裏icon和title缺一不可。。。
            title: Text("首頁")
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.category),
            title: Text("分類")
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.search),
            title: Text("搜索")
          )
        ],
      ),
    );
  }
}
複製代碼

1四、按鈕

通常經常使用的 Button 是 MaterialButton、IconButton、FloatingActionButton。

注意:寫按鈕時都要加上onpress事件,不然可能會出現一些問題

一、MaterialButton

MaterialButton 是一個 Materia 風格的按鈕。

new MaterialButton(
    color: Colors.blue,
    textColor: Colors.white,
    child: new Text('點我'),
    onPressed: () {
        // ...
    },
)
複製代碼

二、RaisedButton

RaisedButtonMaterialButton 相似。

new RaisedButton(
    child: new Text('點我'),
    onPressed: () {},
)
複製代碼

三、FlatButton

FlatButtonMaterialButton 相似,不一樣的是它是透明背景的。若是一個 Container 想要點擊事件時,可使用 FlatButton 包裹,而不是 MaterialButton。由於 MaterialButton 默認帶背景,而 FlatButton 默認不帶背景。

四、IconButton

IconButton 顧名思義就是 Icon + Button 的複合體,當某個 Icon 須要點擊事件時,使用 IconButton 最好不過。

new IconButton(
    icon: new Icon(Icons.volume_up),
    tooltip: 'Increase volume by 10%',
    onPressed: () {
        // ...
    },
)
複製代碼

其外,還有已經定義好的 Icon Button:CloseButton、BackButton。他們都有導航返回的能力。

五、FloatingActionButton

FloatingActionButton 是一個浮動在頁面右下角的浮動按鈕。

new Scaffold(
    // ...
    floatingActionButton: new FloatingActionButton(
        onPressed: () {},
        child: new Icon(Icons.add_a_photo),
        elevation: 10,		//陰影設置
        highlightElevation: 2.0,
        backgroundColor: Colors.red,        // 紅色
    ),
)
複製代碼

在 Scaffold 裏使用的時候,它是一個浮動狀態的按鈕,在其餘地方使用,就不會浮動了。

六、ButtonBar

ButtonBar 是一個佈局組件,可讓 Button 排列在一行。

new ButtonBar(
    children: <Widget>[
        new CloseButton(),
        new BackButton(),
    ],
)
複製代碼

七、樣式

/*-------------------------------------按鈕內部屬性----------------------------------*/
child: Text("陰影按鈕"),	//按鈕文字
color: Colors.blue,		//按鈕的背景顏色
textColor: Colors.white,	//按鈕文字顏色
elevation: 20,		//陰影距離
//按鈕的點擊事件
onPressed: () {		
	 print("顏色按鈕");
},
//設置按鈕的圓角
shape: RoundedRectangleBorder(		
	borderRadius: BorderRadius.circular(20)
),
//將按鈕變爲圓形,並設置一個red顏色的邊
shape: CircleBorder(
	side: BorderSide(color: Colors.red)
),

/*-----------------------------其餘位置的按鈕設置----------------------------------*/
//帶有圖標的按鈕
RaisedButton.icon(
	icon: Icon(Icons.search),
	label: Text("圖標按鈕"),
	onPressed: () {
		print("圖標按鈕");
	},
),
SizedBox(height: 5),	//這個可用來設置按鈕之間的距離 (寫在按鈕與按鈕之間)
//設置按鈕的寬高須要在按鈕外設置
Container(
width: 200,
height: 50,
child: RaisedButton(
	child: Text("自定義按鈕"),
	onPressed: () {},
	),
),
/*-----------------------------------------------------------------------------------*/
//靠左的按鈕
leading: IconButton(	
	icon: Icon(Icons.menu),
	onPressed: (){},
),
//靠右的按鈕
actions: <Widget>[
	IconButton(
	icon: Icon(Icons.settings),
	onPressed: (){},
)
複製代碼

示例:

有點多餘的代碼,不要在乎

class main11 extends StatefulWidget {
  @override
  _main11State createState() => _main11State();
}

class _main11State extends State<main11> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
              
            RaisedButton(				//藍底白字的按鈕
              child: Text("變一下"),
              color: Colors.blue,
              textColor: Colors.white,
              textTheme: ButtonTextTheme.primary,
              onPressed: (){
                print("11111");
              },
            ),
          ],
        ),
      ),
    );
  }
}
複製代碼

1五、表單 TextField

child: new TextField(
  autocorrect: false, // 是否自動校訂
  autofocus: false, //自動獲取焦點
  enabled: true, // 是否啓用
  inputFormatters: [], //對輸入的文字進行限制和校驗
  keyboardType: TextInputType.text, //獲取焦點時,啓用的鍵盤類型
  maxLines: 2, // 輸入框最大的顯示行數
  maxLength: 3, //容許輸入的字符長度
  maxLengthEnforced: false, //是否容許輸入的字符長度超過限定的字符長度
  obscureText: true, // 是否隱藏輸入的內容
  //onChanged事件,在輸入內容發生變化的時候觸發
  onChanged: (newValue) {
      // print(newValue); // 當輸入內容變動時,如何處理
  },
  //onSubmitted事件,則是在輸入結束,點擊完成的時候觸發。
  onSubmitted: (value) {
      // print("whar"); // 當用戶肯定已經完成編輯時觸發
  },
  style: new TextStyle(
      color: new Color(Colors.amberAccent.green)	// 設置字體樣式
  ), 
  textAlign: TextAlign.center, //輸入的內容在水平方向如何顯示
  decoration: new InputDecoration(
      labelText: "城市",	//在輸入框上的左上角的文字,默認顯示在輸入框中,點擊後移到左上角
      icon: new Icon(Icons.location_city),	//在輸入框左側的圖標
      border: new OutlineInputBorder(), // 邊框樣式
      helperText: 'required',	//必須輸入
      hintText: '請選擇你要投保的城市', //默認文字,相似於placeholder
      prefixIcon: new Icon(Icons.android), //輸入框內左側的圖標
      prefixText: 'Hello' //輸入框左側內部的文字,默認顯示,但不會消失
  ), 
  //取消輸入框的下劃線
  TextField(
    decoration: InputDecoration(
      border: InputBorder.none,
    ),
)
複製代碼

TextFormField中沒有這onChange,onSubmitted兩個事件,取而代之的是validator,onSaved,onFieldSubmitted 他們都接受三個函數,而且將其值做爲參數傳遞到函數裏面

  • validator,若是開啓autovalidate: true,那麼將會自動檢驗輸入的值,若是沒有則會在表單提交的時候檢驗 該函數只容許返回驗證失敗的錯誤信息以及驗證經過時返回null。
  • onSaved, 當調用FormState.save方法的時候調用。
  • onFieldSubmitted, 與onSubmitted同樣,則是在輸入結束,點擊完成的時候觸發。

獲取輸入框的值:

  1. 給每一個輸入框聲明一個變量
String _text1 = "";
String _text2 = "";		
複製代碼
  1. 給每一個輸入框加上一個onChange事件,
onChanged: (value){
	setState(() {
		this._text2=value; 
});
複製代碼
  1. 給登陸按鈕一個onPress點擊事件
onPressed: (){
	print(this._text1);
	print(this._text2);
},
複製代碼

輸入框的初始值

//首先聲明變量
var _text2 = new TextEditingController(); //初始化的時候給表單賦值
  @override			//這裏的@override時多加的一個,原來的下面還要寫
  void initState() {
    super.initState();
    _text2.text = '初始值';
  }

//在輸入框設置onChanged
TextField(
	onChanged: (value) {
		setState(() {
		this._text2.text = value;
		});
	},
),
複製代碼

1六、GestureDetector 手勢控制

GestureDetector({
    Key key,
    this.child,
    this.onTapDown,//可能致使點擊的指針已聯繫到屏幕的特定位置
    this.onTapUp,//觸發點的指針已中止在特定位置與屏幕聯繫
    this.onTap,//發生了點擊。
    this.onTapCancel,//觸發onTapDown的指針取消觸發
    this.onDoubleTap,//雙擊
    this.onLongPress,//長按
    this.onLongPressUp,//長按結束
    this.onVerticalDragDown,//
    this.onVerticalDragStart,//指針已經接觸到屏幕,並且可能開始垂直移動。
    this.onVerticalDragUpdate,//與屏幕接觸並垂直移動的指針沿垂直方向移動
    this.onVerticalDragEnd,//之前與屏幕接觸並垂直移動的指針再也不與屏幕接觸,而且當其中止接觸屏幕時以特定速度移動。
    this.onVerticalDragCancel,//
    this.onHorizontalDragDown,//
    this.onHorizontalDragStart,//
    this.onHorizontalDragUpdate,//
    this.onHorizontalDragEnd,//
    this.onHorizontalDragCancel,//

// onPan能夠取代onVerticalDrag或者onHorizontalDrag,三者不能並存
    this.onPanDown,//指針已經接觸屏幕並開始移動
    this.onPanStart,//與屏幕接觸並移動的指針再次移動
    this.onPanUpdate,//先前與屏幕接觸並移動的指針再也不與屏幕接觸,而且當它中止接觸屏幕時以特定速度移動
    this.onPanEnd,//先前觸發 onPanDown 的指針未完成
    this.onPanCancel,//

// onScale能夠取代onVerticalDrag或者onHorizontalDrag,三者不能並存,不能與onPan並存
    this.onScaleStart,//
    this.onScaleUpdate,//
    this.onScaleEnd,//
    this.behavior,
    this.excludeFromSemantics = false
    })

複製代碼

1七、TabBar導航欄

//一些基本屬性
  const TabBar({
    Key key,
    @required this.tabs,//子標籤
    this.controller,//控制器
    this.isScrollable = false,//可否滑動,false:tab寬度則等比,true:tab寬度則包裹item
    this.indicatorColor,//指示器顏色
    this.indicatorWeight = 2.0,
    this.indicatorPadding = EdgeInsets.zero,
    this.indicator,
    this.indicatorSize,//TabBarIndicatorSize.label:indicator與文字同寬,TabBarIndicatorSize.tab:與tab同寬
    this.labelColor,//選中標籤顏色
    this.labelStyle,//選中標籤樣式
    this.labelPadding,
    this.unselectedLabelColor,//未選中標籤顏色
    this.unselectedLabelStyle,
    this.dragStartBehavior = DragStartBehavior.down,
    this.onTap,//點擊事件
  })

複製代碼

配置步驟:

一、在appBar中配置基本的Tab選項

appBar: AppBar(
        title: TabBar(
          controller: _controller,	//這裏設置控制器
          //appbar的屬性設置都在這裏
          indicatorColor: Colors.red,
          isScrollable: true,
          tabs: <Widget>[
            Tab(icon: Icon(Icons.directions_bike)),
            Tab(icon: Icon(Icons.directions_car)),
            Tab(icon: Icon(Icons.directions_subway)),
            Tab(icon: Icon(Icons.directions_bike)),
            Tab(icon: Icon(Icons.sort)),
            Tab(icon: Icon(Icons.star)),
            Tab(icon: Icon(Icons.shuffle)),
            Tab(icon: Icon(Icons.category)),
          ],
        )
      ),
複製代碼

二、一些配置

//後面加上的這個 with SingleTickerProviderStateMixin 是用來實現切換動畫的
class _MyTopBarState extends State<MyTopBar> with SingleTickerProviderStateMixin{
  // TabBar組件須要有一個控制器 
  TabController _controller;
    
  // 定義一個鉤子,其中 @override 是鉤子函數的
  @override
  void initState() {
    // 給導航控制器進行初始化
    // 須要在這個勾子函數中對TabBar進行初始化
    // _controller可以將頁面和導航綁定到一塊兒,最終實現切換的效果
    // length 導航/頁面的個數 vsync動畫效果異步匹配
    _controller = TabController(length: 3,vsync: this);
    super.initState();
  }

複製代碼

完整頁面代碼

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class MyTopBar extends StatefulWidget {
  @override
  _MyTopBarState createState() => _MyTopBarState();
}

// extends表示繼承 
// SingleTickerProviderStateMixin 對咱們的組件進行配置,主要是配置動畫切換效果
class _MyTopBarState extends State<MyTopBar> with SingleTickerProviderStateMixin{
  // TabBar組件須要有一個控制器 
  TabController _controller;
  var tabs = <Tab>[];	//這個是用來自動適應tabbar的數量的

  @override
  void initState() {
    // 給導航控制器進行初始化
    // 須要在這個鉤子函數中對TabBar進行初始化
    // _controller可以將頁面和導航綁定到一塊兒,最終實現切換的效果
    // length 導航/頁面的個數 vsync動畫效果異步匹配
    _controller = TabController(length: tabs.length,vsync: this);
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: TabBar(
          controller: _controller,
          tabs: <Widget>[
            Tab(icon: Icon(Icons.directions_bike)),
            Tab(icon: Icon(Icons.directions_car)),
            Tab(icon: Icon(Icons.directions_subway)),
            Tab(icon: Icon(Icons.directions_bike)),
            Tab(icon: Icon(Icons.sort)),
            Tab(icon: Icon(Icons.star)),
            Tab(icon: Icon(Icons.shuffle)),
            Tab(icon: Icon(Icons.category)),
          ],
        )
      ),
      body: TabBarView(
        controller: _controller,
        children: <Widget>[
          SelfHomePage(page:1),
          SelfHomePage(page:2),
          SelfHomePage(page:3),
          SelfHomePage(page:4),
          SelfHomePage(page:5),
          SelfHomePage(page:6),
          SelfHomePage(page:7),
          SelfHomePage(page:8),
        ],
      ),
    );
  }
}
class SelfHomePage extends StatelessWidget {
  int page;
  // 類的一個重寫,重寫的目的就是類能夠傳遞參數
  SelfHomePage({Key key, @required this.page}):super(key:key);
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text("page $page",style: TextStyle(fontSize: 40),),
    );
  }
}
複製代碼

1八、時間日期

//方法詳解 
now()	//命名構造,獲取當前時間
millisecondsSinceEpoch	//DateTime轉時間戳
fromMillisecondsSinceEpoch	//時間戳轉DateTime
parse(string)	//字符串轉DateTime
isBefore(date)	//時間比較---在以前
isAfter(date)	//時間比較---在以後
isAtSameMomentAs(date)	//時間比較---相等
compareTo(date)	 //大於返回1;等於返回0;小於返回-1
add(Duration)	//時間增長
subtract(Duration)	//時間減小
difference(date)	//時間差 兩個時間相差 小時數
timeZoneName	//本地時區簡碼
timeZoneOffset	//返回UTC與本地時差 小時數
year、month、day、hour、minute、second、millisecond、microsecond
//返回   年、月、日、時、分、秒、毫秒、微妙
weekday	//返回星期幾
複製代碼

簡單應用:使用了 date_format 組件

//獲取當前時間
DateTime _dateTime = DateTime.now();	
//設置默認時間
DateTime _nowDate = DateTime(1989, 2, 21);  // 1989-02-21 00:00:00.000
//格式化年月日時間(前提要安裝(內置的) date_format: ^1.0.6 並導入)
DateTime _nowDate = DateTime(1989, 2, 21);
var r = formatDate(_nowDate,[yyyy, '-', mm, '-', dd]); // 1989-02-21
//格式化時分秒(一樣要安裝 date_format: ^1.0.6 並導入)
DateTime _nowDate = DateTime.now();
var r = formatDate(_nowDate,[HH, ':', nn, ':', ss]);  // 02:45:33
//獲取時間戳 (_dateTime爲當前日期)
var date1 = _dateTime.millisecondsSinceEpoch; 	//1569124347564
//時間戳轉爲日期
var date2 = DateTime.fromMillisecondsSinceEpoch(date1);	//2019-09-22 11:52:27.564


//Flutter DateTime日期轉換
    var today = DateTime.now();
    print('當前時間是:$today');
    var date1 = today.millisecondsSinceEpoch;
    print('當前時間戳:$date1');
    var date2 = DateTime.fromMillisecondsSinceEpoch(date1);
    print('時間戳轉日期:$date2');
    //拼接成date
    var dentistAppointment = new DateTime(2019, 6, 20, 17, 30, 20);
    print(dentistAppointment);
    // 字符串轉date
    DateTime date3 = DateTime.parse("2019-06-20 15:32:41");
    print(date3);
    // 時間比較
    print(today.isBefore(date3)); // 在以前
    print(today.isAfter(date3)); // 在以後
    print(date3.isAtSameMomentAs(date3)); // 相同
    print(date3.compareTo(today)); // 大於返回1;等於返回0;小於返回-1。
    // print(DateTime.now().toString());
    // print(DateTime.now().toIso8601String());

    //時間增長
    var fiftyDaysFromNow = today.add(new Duration(days: 5));
    print('today加5天:$fiftyDaysFromNow');
    //時間減小
    DateTime fiftyDaysAgo = today.subtract(new Duration(days: 5));
    print('today減5天:$fiftyDaysAgo');
    //時間差 兩個時間相差 小時數
    print('比較兩個時間 差 小時數:${fiftyDaysFromNow.difference(fiftyDaysAgo)}');
    print('本地時區簡碼:${today.timeZoneName}');
    print('返回UTC與本地時差 小時數:${today.timeZoneOffset}');
    print(
        '獲取年月日:${today.year}'); //month、day、hour、minute、second、millisecond、microsecond
    print('星期:${today.weekday}'); // 返回星期幾
複製代碼

1九、彈框設置

這裏只介紹了一些flutter自己的彈框,還有一些第三方的彈框可自行去探索使用

showDialog 基本格式

固然,這都須要咱們在界面中點擊觸發才行

//彈框的基本格式
void _alertdialog() async {				//通常都是異步操做,使用async await
    var result = await showDialog(
        barrierDismissible: true,	 	// 當點擊遮罩層時,是否把彈出框消化掉
        context: context, 				//必寫,大概是一個聯繫上下文的東西
        builder: (context) {			//builder,如今還不知道爲何這樣寫,之後再來補🖤💙⭐
          return AlertDialog(			//彈框的樣式,這裏使用的是AlertDialog
            title: Text("提示信息"),	 //彈框的標題
            content: Text("彈框內容33333"),		  //彈框的內容
            // backgroundColor: Colors.red, //這裏還能夠設置一些其餘的屬性
            actions: <Widget>[			//設置底部按鈕,如取消、確認等 
              FlatButton(			
                child: Text("取消"),
                onPressed: () {
                  Navigator.pop(context,"sss");		//這個的做用是讓咱們點擊按鈕後彈框消失,而且能夠傳輸一個值,這裏爲sss,這個值就是一開始咱們聲明的 result,下面的確認按鈕功能同樣,另外,若是不寫"sss",則會返回一個null
                },
              ),
              FlatButton(
                child: Text("肯定"),
                onPressed: () {
                  Navigator.pop(context,"aaa");
                },
              )
            ],
          );
        });
    print(result);		//這裏能夠打印結果
  }
複製代碼

一、AlertDialog

AlertDialog 是一個用於向用戶傳遞信息的彈出層。通常是隻有兩個確認取消按鈕的彈框

actions -> List<Widget>		//底部可選操做集,好比‘確認’、‘取消’按鈕等;
backgroundColor → Color		//dialog背景顏色
content → Widget			//diaolog主題內容,能夠放在weiget中
contentPadding → EdgeInsetsGeometry		//內容的位置,距離父邊距padding
contentTextStyle → TextStyle	//內容文字風格
elevation → double		//dialog的懸浮高度,跟底部陰影有關
shape → ShapeBorder		//dialog邊框的圓角
title → Widget		//dialog標題
titlePadding → EdgeInsetsGeometry		//標題的區域的padding
titleTextStyle → TextStyle		//標題的文字風格

//注意:因爲AlertDialog一般使用child的大小來調整自身大小,因此使用一些widget沒法正常工做;
複製代碼

二、SimpleDialog

SimpleDialog 是一個用於向用戶傳遞肯定信息並提供選項的彈出層。這個是帶有選項的對話框,經過選擇不一樣選項,返回不一樣內容,屬性與AlertDialog大體相同

三、底部彈框showModalBottomSheet

void _showModalBottomSheet() async {				//方法
    var result = await showModalBottomSheet(		//這裏就直接使用showModalBottomSheet,表明底部彈框
      context: context,
      builder: (context){
        return Container(
          height: 130,			//這要根據彈框的高度需求來設置高度
          child: Column(		//這裏就是彈框的主要內容,可是是使用Column配合ListTile建立的
            children: <Widget>[
              ListTile(
                title: Text("分享到朋友圈1"),
                onTap: (){									//點擊事件
                  Navigator.pop(context,"分享到朋友圈1");		//一樣的傳值
                },
              ),
              Divider(),					//這是一個組件,是一道水平分割線
              ListTile(
                title: Text("分享到朋友圈2"),
                onTap: (){
                  Navigator.pop(context,"分享到朋友圈2");
                },
              ),
            ],
          ),
        );
      }
    );
    print(result);
  }
複製代碼

20、旋轉圈圈 加載動畫

// 這是一個小圈圈的組件
  Widget _getMoreWidget(){
    return Center(
      child: Padding(
        padding: EdgeInsets.all(10.0),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            Text("加載中....",style: TextStyle(fontSize: 16),),
            CircularProgressIndicator(
              strokeWidth: 1.0,
            )
          ],
        ),
      ),
    );
  }
}
複製代碼

4、佈局

一、stack 層疊佈局

大概能夠理解爲 在層疊基礎上的 position,且在stack中的元素是有前後順序之分的,在靠前位置的元素不會被以後的框架元素所束縛

stack

******          Stack

alignment		//佈局定位 默認 AlignmentDirectional.topStart,也能夠直接傳入參數,自定義位置(值在1與-1之間),如:alignment: Alignment(0.5,0.5),
textDirection	//正反排序
TextDirection.ltr
TextDirection.rtl
fit				默認StackFit.loose
overflow		默認Overflow.clip
children		子元素
複製代碼

Align

******          Align
Align控件即對齊控件,能將子控件所指定方式對齊,並根據子控件的大小調整本身的大小。

alignment		佈局定位
widthFactor		若是爲非null,則將其高度設置爲子高度乘以此係數。必須爲正數
heightFactor	若是爲非null,則將其寬度設置爲子寬度乘以此係數。必須爲正數
child			子元素
複製代碼

Positioned

******          Positioned
用來在 Stack 組件中輔助子組件定位的組件,建議在有三個及以上子組件的複雜佈局時使用,它的經常使用屬性有:

left			左邊距
top				上邊距
right			右邊距
bottom			下邊距
width			子元素寬
height			子元素高
child			子元素
複製代碼

簡單示例:

class PosionDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        height: 400,
        width: 300,
        color: Colors.red,
        child: Stack(
          children: <Widget>[
            Positioned(
              bottom: 20,
              left: 20,
              child: Icon(Icons.home),
            ),
            Positioned(
              top: 30,
              right: 30,
              child: Icon(Icons.search),
            ),
            Positioned(
              bottom: 0,
              right: 0,
              child: Icon(Icons.settings),
            )
          ],
        ),
      ),
    );
  }
}
複製代碼

二、Expanded

能夠按比例「擴伸」 RowColumnFlex子組件所佔用的空間。

const Expanded({
  int flex = 1, 
  @required Widget child,
})
複製代碼

flex參數爲彈性係數,若是爲0或null,則child是沒有彈性的,即不會被擴伸佔用的空間。若是大於0,全部的Expanded按照其flex的比例來分割主軸的所有空閒空間

三、drawer 抽屜

Drawer 左側抽屜
endDrawer: Drawer(),  右側抽屜
elevation	背景高度
child	子組件
semanticLabel	標籤
UserAccountsDrawerHeader
decoration	頭部裝飾
margin	外邊距  默認8.0
currentAccountPicture	主圖像
otherAccountsPictures	附圖像
accountName	標題
accountEmail	副標題
onDetailsPressed	點擊監聽
DrawerHeader   抽屜頭部
複製代碼
  1. DrawerHeader

一般用於在抽屜中在頂部展現一些基本信息;其包含以下屬性:

  • decorationheader區域的decoration,一般用來設置背景顏色或者背景圖片
  • durationcurve:若是decoration發生了變化,則會使用 curve設置的變化曲線和duration設置的動畫時間來作一個切換動畫
  • child: Header裏面所顯示的內容控件
  • padding: Header裏面內容控件的padding值,若是child爲null,則這個值無效
  • margin:Header四周的間隙

若是想在DrawerHeader中顯示用戶帳戶信息,好比相似於 Gmail 的 聯繫人頭像、用戶名、Email 等信息,則可使用 UserAccountsDrawerHeader這個特殊的DrawerHeader

  1. UserAccountsDrawerHeader

UserAccountsDrawerHeader能夠設置用戶頭像、用戶名、Email 等信息,顯示一個符合MD規範的 drawer header。其經常使用屬性以下:

  • margin:Header四周的間隙
  • decoration:header區域的decoration,一般用來設置背景顏色或者背景圖片
  • currentAccountPicture:用來設置當前用戶的頭像
  • otherAccountsPictures:用來設置當前用戶的其餘帳號的頭像(作多顯示三個)
  • accountName:當前用戶的名字
  • accountEmail:當前用戶的 Email
  • onDetailsPressed: 當 accountName 或者 accountEmail 被點擊的時候所觸發的回調函數,能夠用來顯示其餘額外的信息

示例:使用DrawerHeader設置抽屜頭部樣式

Widget header = DrawerHeader(
  padding: EdgeInsets.zero,
  /* padding置爲0 */
  child: new Stack(children: <Widget>[
    /* 用stack來放背景圖片 */
    new Image.asset(
      'images/mao.jpg',
      fit: BoxFit.fill,
      width: double.infinity,
    ),
    new Align(
      /* 先放置對齊 */
      alignment: FractionalOffset.bottomLeft,
      child: Container(
        height: 70.0,
        margin: EdgeInsets.only(left: 12.0, bottom: 12.0),
        child: new Row(
          mainAxisSize: MainAxisSize.min,
          /* 寬度只用包住子組件便可 */
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            new CircleAvatar(
              backgroundImage: AssetImage('images/mao.jpg'),
              radius: 35.0,
            ),
            new Container(
              margin: EdgeInsets.only(left: 6.0),
              child: new Column(
                crossAxisAlignment: CrossAxisAlignment.start, // 水平方向左對齊
                mainAxisAlignment: MainAxisAlignment.center, // 豎直方向居中
                children: <Widget>[
                  new Text(
                    "此處",
                    style: new TextStyle(
                        fontSize: 20.0,
                        fontWeight: FontWeight.w400,
                        color: Colors.white),
                  ),
                  new Text(
                    "有肥貓",
                    style: new TextStyle(fontSize: 14.0, color: Colors.white),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    ),
  ]),
);

複製代碼

四、AspectRatio

AspectRatio

一個widget,試圖將子widget的大小指定爲某個特定的長寬比

這是一個比率控件,按照寬度和比率來計算高度。通常會設置父容器的寬度,而後設置**AspectRatio的aspectRatio,那麼AspectRatio**就會按照指定比例來顯示;

child: AspectRatio(
			aspectRatio: 16 / 9,	//此處設置寬高比例
            child: Swiper(........)
            )
複製代碼

五、wrap

屬性解析

direction:主軸(mainAxis)的方向,默認爲水平。

alignment:主軸方向上的對齊方式,默認爲start。

spacing:主軸方向上的間距。

runAlignment:run的對齊方式。run能夠理解爲新的行或者列,若是是水平方向佈局的話,run能夠理解爲新的一行。

runSpacing:run的間距。

crossAxisAlignment:交叉軸(crossAxis)方向上的對齊方式。

textDirection:文本方向。

verticalDirection:定義了children擺放順序,默認是down,見Flex相關屬性介紹。

一種遍歷數組的方式(簡單例子):

Wrap(
	spacing: 10,		//間距
	children:this._history.map((value){		//this._history是遍歷的數組對象,value是每一項
	return Container(
		child: RaisedButton(
			child: Text(value),				
			onPressed: (){print(value);}
		),
	);
}).toList(),
複製代碼

六、屏幕適配

一、安裝與引用

flutter_screenutil: ^0.6.0
import 'package:flutter_screenutil/flutter_screenutil.dart';
複製代碼

二、封裝 --不封裝也能夠引入定義後使用,不過太繁瑣,封裝一下更方便

import 'package:flutter_screenutil/flutter_screenutil.dart';
class ScreenAdaper{
  //初始化
  static init(context){
    ScreenUtil.instance = ScreenUtil(width: 750, height: 1334)..init(context);
  }
  //高度
  static height(double value){
     return ScreenUtil.getInstance().setHeight(value);
  }
  //寬度
  static width(double value){
      return ScreenUtil.getInstance().setWidth(value);
  }
  //獲取屏幕高度
  static getScreenHeight(){
    return ScreenUtil.screenHeightDp;
  }
  //獲取屏幕寬度
  static getScreenWidth(){
    return ScreenUtil.screenWidthDp;
  }

  static getScreenPxHeight(){
    return ScreenUtil.screenHeight;
  }
  static getScreenPxWidth(){
    return ScreenUtil.screenWidth;
  }
  // ScreenUtil.screenHeight 
}

// ScreenAdaper
複製代碼

5、路由

路由(Route)在移動開發中一般指頁面(Page),這跟web開發中單頁應用的Route概念意義是相同的,Route在Android中一般指一個Activity,在iOS中指一個ViewController。所謂路由管理,就是管理頁面之間如何跳轉,一般也可被稱爲導航管理。Flutter中的路由管理和原生開發相似,不管是Android仍是iOS,導航管理都會維護一個路由棧,路由入棧(push)操做對應打開一個新頁面,路由出棧(pop)操做對應頁面關閉操做,而路由管理主要是指如何來管理路由棧。

一、簡單示例:

  1. 建立一個新路由,命名「NewRoute」

    class NewRoute extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("New route"),
          ),
          body: Center(
            child: Text("This is new route"),
          ),
        );
      }
    }
    複製代碼

    新路由繼承自StatelessWidget,界面很簡單,在頁面中間顯示一句"This is new route"。

  2. _MyHomePageState.build方法中的Column的子widget中添加一個按鈕(FlatButton) :

    Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
          ... //省略無關代碼
          FlatButton(
             child: Text("open new route"),
             textColor: Colors.blue,
             onPressed: () {
              //導航到新路由 
              Navigator.push( context,
               MaterialPageRoute(builder: (context) {
                  return NewRoute();
               }));
              },
              //路由返回
              onTap: (){
                // pop後面能夠跟上參數
              	Navigator.of(context).pop("旺財 北京市昌平區沙河鎮北京科技經營管理學院");
           },
             ),
           ],
     )
    複製代碼

    咱們添加了一個打開新路由的按鈕,並將按鈕文字顏色設置爲藍色,點擊該按鈕後就會打開新的路由頁面。

二、Navigator

Navigator是一個路由管理的組件,它提供了打開和退出路由頁方法。Navigator經過一個棧來管理活動路由集合。一般當前屏幕顯示的頁面就是棧頂的路由。Navigator提供了一系列方法來管理路由棧,在此咱們只介紹其最經常使用的兩個方法:

push和pop, push 是將元素添加到堆棧的頂部,pop是從同一堆棧中刪除頂部元素。

在Flutter的狀況下,當咱們導航到另外一個屏幕時,咱們使用Navigator.push方法將新屏幕添加到堆棧的頂部。固然,這些pop方法會從堆棧中刪除該屏幕

context:表明上下文,也就是相似windows中的句柄,指的是當前的這個頁面窗口。

Navigator.pop(context);
複製代碼

Navigator.pop(context):pop在javascript中用於刪除數組的最末一個元素,這就明白了,就是刪除當前頁面返回到Navigator中的前一個頁面。

Navigator.push(BuildContext context, Route route)
//等價於
Navigator.of(context).push(Route route)
複製代碼

三、MaterialPageRoute

咱們能夠直接使用 MaterialPageRoute建立路由,它是一種模態路由,能夠經過平臺自適應的過渡效果來切換屏幕。默認狀況下,當一個模態路由被另外一個替換時,上一個路由將保留在內存中,若是想釋放全部資源,能夠將 maintainState 設置爲 false

示例:

onPressed: () {
  Navigator.push(
    context,
      //其中,new SecondScreen()是另外一個界面
    new MaterialPageRoute(builder: (context) => new SecondScreen()),
  );
},
複製代碼

四、命名式路由

所謂「命名路由」(Named Route)即有名字的路由,咱們能夠先給路由起一個名字,而後就能夠經過路由名字直接打開新的路由了,這爲路由管理帶來了一種直觀、簡單的方式。

一、註冊路由表

建立Routes.dart文件  
	routes 規則 
		// 不須要傳值
		"/item1":(context)=>Item1(),  
		// 能夠傳值 ,傳值的路由要多寫一個arguments
		"/item2":(context,{arguments})=>Item2(arguments:arguments),
複製代碼

實例 : 將路由抽出,單獨作一個文件

import 'package:flutter/material.dart';
import './Item1.dart';
import './Item2.dart';
import './Item3.dart';

final routes = {
  "/item1": (context) => Item1(),
    		//要傳值的路由要多寫一個arguments
  "/item2": (context,{arguments}) => Item2(arguments: arguments),
  "/item3": (context) => Item3()
};

// 若是你要把路由抽離出去,須要寫下面這一堆的代碼。。。。。。。。。必需要加。。。。。。。。。
var onGenerateRoute = (RouteSettings settings) {
  // 統一處理
  final String name = settings.name;
  final Function pageContentBuilder = routes[name];
  if (pageContentBuilder != null) {
    if (settings.arguments != null) {
      final Route route = MaterialPageRoute(
          builder: (context) =>
              pageContentBuilder(context, arguments: settings.arguments));
      return route;
    } else {
      final Route route =
          MaterialPageRoute(builder: (context) => pageContentBuilder(context));
      return route;
    }
  }
};
複製代碼

二、main.dart配置

//在main.dart中配置以下:
return MaterialApp(
	initialRoute: "/",  // 默認訪問路徑
	onGenerateRoute: onGenerateRoute
);
複製代碼

三、跳轉

children: <Widget>[
    //普通的路由跳轉
            MaterialButton(
              child: Text("去item1"),
              onPressed: (){
                  Navigator.pushNamed(context, "/item1");
              },
            ),
    //路由傳值的路由跳轉
             MaterialButton(
              child: Text("去item2"),
              onPressed: (){
                  //與普通路由跳轉相比,後多了個arguments對象,arguments就是咱們傳的值
                  Navigator.pushNamed(context, "/item2",arguments: {"id":12445});
              },
            ),
複製代碼

四、接收並顯示

  1. 首先在要接收傳值的路由對象中定義並接收
//這裏的arguments就是咱們接受的傳值
  final arguments;
  Item2({this.arguments});
複製代碼
  1. 而後在頁面上顯示出來
Text("${arguments['id']}"),
複製代碼

完整例子

import 'package:flutter/material.dart';

class Item2 extends StatelessWidget {
  final arguments;
  Item2({this.arguments});
  @override
  Widget build(BuildContext context) {
    return Container(
      height: 300,
      margin: EdgeInsets.all(10),
      child: Text("${arguments['id']}"),
    );
  }
}
複製代碼

6、數據交互

一、子傳父

/*----------------------------------獲取值的父組件------------------------------------*/
var _data = "";
.....//中間的語句省略
RaisedButton(
	child: Text("跳轉到search"),
	onPressed: () async{
		//導航到新路由
		var data = await Navigator.push(context, MaterialPageRoute(builder: (context) {
		return Search();
		}));
		setState(() {
			 _data =  data;
		});
	},
    //這個也可使用 .then 的方式來獲取數據
    /*onPressed: () { //導航到新路由 var data = await Navigator.push(context, MaterialPageRoute(builder: (context) { return Search(); })).then((res){ setState(() { _data = data; }); }); }, */
	
),
Text("${ _data =='' ? '無內容':_data}"),

/*---------------------------------發送值的子組件------------------------------------*/

body: ListView(
	children: <Widget>[
		RaisedButton(
			child: Text("返回1"),
			onPressed: () {
				Navigator.of(context).pop("搜索內容11111");
			},
		),
		RaisedButton(
			child: Text("返回2"),
			onPressed: () {
				Navigator.of(context).pop("搜索內容22222");
			},
		),
	],
)

複製代碼

二、父傳子

/*----------------------------------父組件--------------------------------------------*/
RaisedButton(
	child: Text("表單"),
	onPressed: () {
	Navigator.of(context).push(MaterialPageRoute(
		builder: (context) => FormPage(title: "表單數據xxx")));
	},
),

/*-----------------------------------子組件-------------------------------------------*/

class FormPage extends StatelessWidget {
  String title;
  FormPage({this.title = ""});		//須要在這裏進行定義,並能夠設置初始值,在沒有傳值時顯示
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(this.title)),	//在這裏接收
      
      body: Text("body"),
      floatingActionButton: FloatingActionButton(		//只是一個返回按鈕
        child: Text("返回"),
        onPressed: () {
          Navigator.of(context).pop();
        },
      ),
    );
  }
}
複製代碼

三、網絡數據請求

一、http

一、配置

http: ^0.12.0+2
複製代碼

二、引入

import 'package:http/http.dart' as http;   //這裏的 as http表示咱們之後能夠用http指代這個組件
複製代碼

三、請求數據 (URL是訪問地址)

var result = await http.get(URL);			//get請求數據
var result = await http.post(URL,body: {"username":"wangcai","age":"10"});  //post上傳

print(json.decode(result.body));	//用來轉換返回結果
複製代碼

四、渲染數據 (根據本身的須要,這裏是渲染爲簡單列表)

body: this._list.length>0 ? ListView.builder(		//三元表達式
        itemCount: this._list.length,
        itemBuilder: (context,index){
          return ListTile(
            leading: Image.network("${this._list[index]['cover']}"),
            title: Text("${this._list[index]['title']}"),
          );
        },
      ):Text("加載中...")		//能夠在這裏寫一個請求數據成功前簡單的加載動畫
複製代碼

補充

從服務器獲得的數據,要麼是buffer,要麼是字符串。 要使用數據,須要把json字符串轉成Map類型

import 'dart:convert';

json.decode()   //json字符串轉成Map類型 
json.encode() 	//若是把一個Map類型對象,轉成json字符串
json.decode(xxx is Map)	//判斷xxx是不是Map類型,返回值爲true或者false
複製代碼

二、dio

簡單示例:

import 'package:dio/dio.dart';
void getHttp() async {
  try {			//這裏的try、catch只是爲了更加方便地返回錯誤,可寫可不寫
    Response response = await Dio().get("http://www.baidu.com");
    print(response);
  } catch (e) {
    print(e);
  }
}
複製代碼

一、配置

dio: ^2.1.13	//重點。。。不要用最新版,有毛病
複製代碼

二、引入

import 'package:dio/dio.dart';   
複製代碼

三、請求數據 (apiUrl是訪問地址)

var result = await Dio().get(apiUrl);		//dio請求數據

//dio提交數據
Map jsonData = {"username":"wangcai","age":10};
Response result = await Dio().post(apiUrl,data: jsonData);
複製代碼

四、渲染數據 (根據本身的須要,這裏是渲染爲簡單列表)

body: this._list.length>0 ? ListView.builder(		//三元表達式
        itemCount: this._list.length,
        itemBuilder: (context,index){
          return ListTile(
            leading: Image.network("${this._list[index]['cover']}"),
            title: Text("${this._list[index]['title']}"),
          );
        },
      ):Text("加載中...")		//能夠在這裏寫一個請求數據成功前簡單的加載動畫
複製代碼

四、上拉加載 與 下拉刷新

一、RefreshIndicator 下拉刷新

flutter內部提供了一個組件,叫RefreshIndicator,能夠實現下拉刷新。並無上拉加載更多的組件。

二、ScrollController 上拉加載

ListView 中有一個屬性,叫ScrollController屬性,這是ListView的滑動事件。咱們能夠利用ScrollController實現上拉加載更多。

this._list.addAll(res);  //拼接,表示把每一次的res數據都拼接到_list上
複製代碼

一、RefreshIndicator

/* * 下拉刷新組件 *const RefreshIndicator ({ Key key, @required this.child, this.displacement: 40.0, //觸發下拉刷新的距離 @required this.onRefresh, //下拉回調方法,方法須要有async和await關鍵字,沒有await,刷新圖標立馬消失,沒有async,刷新圖標不會消失 this.color, //進度指示器前景色 默認爲系統主題色 this.backgroundColor, //背景色 this.notificationPredicate: defaultScrollNotificationPredicate, }) */
複製代碼
//在body中,內容都須要寫在RefreshIndicator裏
RefreshIndicator(
	onRefresh: _onRefresh,
    child: ListView.builder(.......)	//ListView.builder是用來循環顯示數據用的
    
//在RefreshIndicator中的onRefresh來觸發這個方法,而後經過這個去觸發請求數據的_getData方法
Future<void> _onRefresh() async {	//_onRefresh就是觸發的方法名
    await Future.delayed(Duration(milliseconds: 2000), () {
      _getData();
    });
  }
複製代碼

二、ScrollController

一些屬性:

  • offset:可滾動組件當前的滾動位置。
  • jumpTo(double offset)animateTo(double offset,...):這兩個方法用於跳轉到指定的位置,它們不一樣之處在於,後者在跳轉時會執行一個動畫,而前者不會。

一、註冊

ScrollController _scrollController = new ScrollController();
複製代碼

二、監聽

child: ListView.builder(
	controller: _scrollController,	//在頁面處放置屬性
.......)
    
//監聽滾動條事件
    _scrollController.addListener(() {
      // _scrollController.position.pixels.toInt(); //滾動條下拉的距離
      // _scrollController.position.maxScrollExtent.toInt(); //整個頁面的高度
      if (_scrollController.position.pixels.toInt() ==
          _scrollController.position.maxScrollExtent.toInt()) {
        this._getData();	//當滾動條下拉的距離和整個頁面的高度相同時,觸發請求數據事件
      }
    });
複製代碼

三、請求數據

void _getData() async {
    if (this.hasMore) {		//判斷不是最後一頁,纔會去請求數據
      var apiUrl =
          "http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=${_page}";
      var response = await Dio().get(apiUrl);
      var res = json.decode(response.data)["result"];   
      setState(() {
        this._list.addAll(res);  //拼接數據
        this._page++;
      });

      // //判斷是不是最後一頁
      if (res.length < 20) {
        setState(() {
          this.hasMore = false;
        });
      }
    }
  }
複製代碼

五、shared_preferences 共享狀態

包裝NSUserDefaults(在ios上)和SharedPreferences(在Android上),爲簡單數據提供一個持久存儲。數據異步地保存到磁盤。兩個平臺都不能保證寫操做在返回後被持久化到磁盤上,並且這個插件不能用於存儲關鍵數據。

一、安裝與引入

shared_preferences: ^0.5.3+4
import 'package:shared_preferences/shared_preferences.dart';
複製代碼

二、使用

Ⅰ、首先新建一個Storage.dart文件,固然也能夠不新建,直接在哪調用在哪寫,不過太麻煩

// 封裝操做狀態的方法 
import 'package:shared_preferences/shared_preferences.dart';

// 瀏覽器中能夠在localstage cookie indexDB 
// shared_preferences 存儲的數據也是鍵值對 String類型
class Storage{
  // static表示靜態方法 靜態方法只能類名來調用 Storage.setString("name","wangcai")
  static Future<void> setString(key,value) async {        //設置。。也就是添加或者修改
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString(key, value);
  }
  static Future<String> getString(key) async {           //獲取
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return prefs.getString(key);
  }
  static Future<void> remove(key) async {               //刪除
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.remove(key);
  }
}
複製代碼

Ⅱ、使用Storage

import 'package:flutter/material.dart';
import '../common/Storage.dart';		//引入新建的Storage.dart文件

//.....
//如下爲使用狀態的方法,其餘的省略
  void _saveData() async{
    //小明,18歲分別爲鍵和值,此方法既能夠添加,也能夠修改,都是根據鍵名有無來進行判斷
    await Storage.setString("小明", "18歲");	
  }
  void _getData() async {
    //輸入鍵名進行獲取,
    var username = await Storage.getString("小明");	
    print(username);
  }
  void _removeData() async {
    //刪除,一樣是根據鍵名,
    await  Storage.remove("小明");
  }
複製代碼

六、在模型類中序列化JSON

官網介紹,我只複製了一部分

一、 序列化

//序列化操做
  var strData = '{"name":"wangcai","age":20}';
  // 把JSON字符串轉成Map類型
  print(json.decode(strData));
  print(json.decode(strData) is Map); // true
  var res = json.decode(strData);
  print(res["name"]);  // wangcai,在這裏只能使用xxx[yyy]的形式來獲取,在編輯過程當中很難看出是否錯誤
複製代碼

二、反序列化

//反序列化操做
var strData = {"name":"wangcai","age":20};
print(json.encode(strData));
print(json.encode(strData) is String); // true
複製代碼

三、模型類

當請求大量數據後,咱們須要對其進行JSON.decode或者JSON.encode,但這樣轉碼會比較繁瑣,且容易出錯,咱們能夠經過引入一個簡單的模型類(model class)來解決前面提到的問題,咱們稱之爲User。在User類內部,咱們有:

  • 一個User.fromJson 構造函數, 用於從一個map構造出一個 User實例 map structure
  • 一個toJson 方法, 將 User 實例轉化爲一個map.

這樣,調用代碼如今能夠具備類型安全、自動補全字段(name和email)以及編譯時異常。若是咱們將拼寫錯誤或字段視爲int類型而不是String, 那麼咱們的應用程序就不會經過編譯,而不是在運行時崩潰。

user.dart示例

class User {
  final String name;
  final String email;

  User(this.name, this.email);

  User.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        email = json['email'];

  Map<String, dynamic> toJson() =>
    {
      'name': name,
      'email': email,
    };
}
複製代碼

如今,序列化邏輯移到了模型自己內部。採用這種新方法,咱們能夠很是容易地反序列化user。

Map userMap = JSON.decode(json);
var user = new User.fromJson(userMap);

print('Howdy, ${user.name}!');
print('We sent the verification link to ${user.email}.');
複製代碼

要序列化一個user,咱們只是將該User對象傳遞給該JSON.encode方法。咱們不須要手動調用toJson這個方法,由於JSON.encode已經爲咱們作了。

String json = JSON.encode(user);
複製代碼

使用:

一、可在lib下建立一個Model文件夾,而後在其中建立一些本身須要的Model文件,例如textModel.dart

二、在textModel.dart文件中添加一些代碼,步驟爲

​ Ⅰ、將接收過來的數據在網上找個JSON格式化解析工具進行解析

{"_id":"67890fsadf56789fsadf","title":"手機","status":"1","url":"abc"}	//接收的數據
複製代碼

​ Ⅱ、將解析好的數據放入JSON to Dart中,點擊 Geberate Dart, 將生成的代碼複製進textModel.dart中,並根據本身的須要更改類名

class TextModel {
  String sId;
  String title;
  String status;
  String url;

  TextModel({this.sId, this.title, this.status, this.url});

  TextModel.fromJson(Map<String, dynamic> json) {
    sId = json['_id'];
    title = json['title'];
    status = json['status'];
    url = json['url'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['_id'] = this.sId;
    data['title'] = this.title;
    data['status'] = this.status;
    data['url'] = this.url;
    return data;
  }
}
複製代碼

​ Ⅲ、使用

import '../../model/textModel.dart';		//引入咱們建立的textModel.dart文件

//str是請求的 數據
var str = '{"_id":"67890fsadf56789fsadf","title":"手機","status":"1","url":"abc"}';
//這是
var focus = TextModel.fromJson(json.decode(str));	//將數據放入TextModel中

print(focus.sId);		//這裏就能夠經過xxx.yyy的格式來編寫代碼,並帶有提示
print(focus.title);
print(focus.url);
複製代碼

7、flutter生命週期

RUNOOB 圖標

大體能夠當作三個階段

  • 初始化(插入渲染樹)
  • 狀態改變(在渲染樹中存在)
  • 銷燬(從渲染樹種移除)

一、構造函數

這個函數不屬於生命週期,由於這個時候State的widget屬性爲空,若是要在構造函數中訪問widget的屬性是行不通的。可是構造函數必然是要第一個調用的。

二、initState

/// Called when this object is inserted into the tree.
複製代碼

當插入渲染樹的時候調用,這個函數在生命週期中只調用一次。這裏能夠作一些初始化工做,好比初始化State的變量。

三、didChangeDependencies

/// Called when a dependency of this [State] object changes.
複製代碼

這個函數會緊跟在initState以後調用,而且能夠調用BuildContext.inheritFromWidgetOfExactType,那麼BuildContext.inheritFromWidgetOfExactType的使用場景是什麼呢?最經典的應用場景是

new DefaultTabController(length: 3, child: new TabBar(
      tabs: [ "主頁","訂單","個人" ]
      .map( (data)=>new Text(data) ).toList(),
複製代碼

四、didUpdateWidget

/// Called whenever the widget configuration changes.

當組件的狀態改變的時候就會調用didUpdateWidget,好比調用了setState.

實際上這裏flutter框架會建立一個新的Widget,綁定本State,並在這個函數中傳遞老的Widget。

這個函數通常用於比較新、老Widget,看看哪些屬性改變了,並對State作一些調整。

須要注意的是,涉及到controller的變動,須要在這個函數中移除老的controller的監聽,並建立新controller的監聽。

五、deactivate

/// Called when this object is removed from the tree.

在dispose以前,會調用這個函數。

六、dispose

/// Called when this object is removed from the tree permanently.

一旦到這個階段,組件就要被銷燬了,這個函數通常會移除監聽,清理環境。

小結

階段 調用次數 是否支持setState
構造函數 1
initState 1 無效(使用setState和不使用同樣)
didChangeDependencies >=1 無效
didUpdateWidget >=1 無效
deactivate >=1
dispose 1

8、第三方組件

一、時間日期

Flutter 的日期選擇器控件

Ⅰ、date_format

格式化日期的簡單應用程序接口。

一、安裝與引入

date_format: ^1.0.6
import 'package:date_format/date_format.dart';
複製代碼

二、使用(部分示例)

// 1989-02-21
print(formatDate(DateTime(1989, 02, 21), [yyyy, '-', mm, '-', dd]));
// 89-feb-21
print(formatDate(DateTime(1989, 2, 21), [yy, '-', M, '-', d]));
// 15:40:10
print(formatDate(DateTime(1989, 02, 1, 15, 40, 10), [HH, ':', nn, ':', ss]));
// 15:40:10+0100
print(formatDate(DateTime(1989, 02, 1, 15, 40, 10), [HH, ':', nn, ':', ss, z]));
複製代碼

Ⅱ、內置的datepicker

//基本要求

Future<DateTime> showDatePicker ({
    @required BuildContext context, // 上下文
    @required DateTime initialDate, // 初始日期
    @required DateTime firstDate,   // 日期範圍,開始
    @required DateTime lastDate,    // 日期範圍,結尾
    SelectableDayPredicate selectableDayPredicate,
    DatePickerMode initialDatePickerMode: DatePickerMode.day,
    Locale locale,                  // 國際化
    TextDirection textDirection,
});

Future<TimeOfDay> showTimePicker({
    @required BuildContext context,
    @required TimeOfDay initialTime
});
複製代碼

示例::

var result = await showDatePicker(	//showDatePicker內置時間模塊
      context: context,				//必寫,上下文
      initialDate: DateTime.now(),  // 初始日期
      firstDate: DateTime(1980),	// 日期範圍,開始
      lastDate: DateTime(2100),		// 日期範圍,結尾
      locale: Locale('zh')			// 國際化
    );
    setState(() {
     this._nowDate = result; 
    });
  }
複製代碼

Ⅲ、 flutter_cupertino_date_picker

Flutter 的日期選擇器控件

1. 安裝與引入

在項目的 pubspec.yaml 文件中添加依賴:

flutter_cupertino_date_picker: ^1.0.12
import 'package:flutter_cupertino_date_picker/flutter_cupertino_date_picker.dart';
複製代碼

2. 屬性

/// 顯示BottomSheet形式的日期時間選擇器。
/// 
/// context: [BuildContext]
/// minDateTime: [DateTime] 日期選擇器的最小值
/// maxDateTime: [DateTime] 日期選擇器的最大值
/// initialDateTime: [DateTime] 日期選擇器的初始值
/// dateFormat: [String] 日期時間格式化
/// locale: [DateTimePickerLocale] 國際化,語言地區
/// pickerMode: [DateTimePickerMode] 顯示的類型: date(日期選擇器)、time(時間選擇器)、datetime(日期時間選擇器)
/// pickerTheme: [DateTimePickerTheme] 日期選擇器的樣式
/// onCancel: [DateVoidCallback] 點擊標題取消按鈕的回調事件
/// onClose: [DateVoidCallback] 關閉日期時間選擇器的回調事件
/// onChange: [DateValueCallback] 選擇的日期時間改變的事件
/// onConfirm: [DateValueCallback] 點擊標題肯定按鈕的回調事件
DatePicker.showDatePicker(
  BuildContext context,
  DateTime minDateTime,
  DateTime maxDateTime,
  DateTime initialDateTime,
  String dateFormat,
  DateTimePickerLocale locale: DATETIME_PICKER_LOCALE_DEFAULT,
  DateTimePickerMode pickerMode: DateTimePickerMode.date,
  DateTimePickerTheme pickerTheme: DatePickerTheme.Default,
  DateVoidCallback onCancel,
  DateVoidCallback onClose,
  DateValueCallback onChange,
  DateValueCallback onConfirm,
});
複製代碼

三、使用

DatePicker.showDatePicker(							//顯示日期方法
      context,										//上下文
      pickerTheme: DateTimePickerTheme(				//底部按鈕設置
        showTitle: true,
        confirm: Text('肯定', style: TextStyle(color: Colors.red)),
        cancel: Text('取消', style: TextStyle(color: Colors.cyan)),
      ),
      minDateTime: DateTime.parse("1990-01-01"),		//最小日期
      maxDateTime: DateTime.parse("2022-12-12"),		//最大日期
      initialDateTime: _dateTime,						//當前時間
      dateFormat: "yyyy-MMMM-dd",						//日期格式
      locale: DateTimePickerLocale.zh_cn,				//語言
      onClose: () => print("----- onClose -----"),		//關閉時間組件事件
      onCancel: () => print('onCancel'),				//取消事件,與上方取消按鈕關聯
       onChange: (dateTime, List<int> index) {			//改變時間事件
         setState(() {
           _dateTime = dateTime;
      	 });
      },
      onConfirm: (dateTime, List<int> index) {			//確認事件,一樣與上方肯定按鈕關聯
        setState(() {
          _dateTime = dateTime;
        });
      },
    );
複製代碼

二、html文本顯示

flutter_html : 把前端中的標籤轉成flutter中的組件(但認識的標籤有限,可能不能徹底解析文本) webview : 在app中嵌套一個網頁 flutter_inappbrowser (第三方,功能較爲完善)

**flutter_inappbrowser **

InAppWebView.initialUrl			//將被加載的初始url。

InAppWebView.initialFile		//將要加載的初始資產文件。

InAppWebView.initialData		//最初的`InAppWebViewInitialData`會被裝載。

InAppWebView.initialHeaders		//將使用的初始標題。

InAppWebView.initialOptions		//將使用的初始選項。
複製代碼
InAppWebView(		//調用組件
    //設置初始的url
	initialUrl: "http://www.phonegap100.com/newscontent.php?aid=${this.arguments["aid"]}",
    //這是一個監測狀態變化的方法,其中的progerss是一個上面獲取數據的進度,從0~100,
	onProgressChanged: (InAppWebViewController controller, int progerss){
		// print(progerss); //0~100
		if((progerss/100).toInt() == 1){	//當數據加載徹底後。。。。
            setState(() {		//將設置的加載動畫消去(根據需求)
				this._flag = false; 
			});
        }
	},
),
複製代碼

三、device_info 獲取設備信息

一、安裝與引入

device_info: ^0.4.0+2
import 'package:device_info/device_info.dart';
複製代碼

二、官網案例

import 'package:device_info/device_info.dart';
//獲得設備信息
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();	//無論獲取安卓仍是ios,都要寫這句

//獲取安卓的設備信息
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
print('Running on ${androidInfo.model}');  // androidInfo中有不少屬性,如version、type、androidId、model,,可根據須要取用

//獲取ios的設備信息
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
print('Running on ${iosInfo.utsname.machine}');  // e.g. "iPod7,1"
複製代碼

三、在方法中使用

_getInter() async {
    var connectivityResult = await (Connectivity().checkConnectivity());
    // print(connectivityResult == ConnectivityResult.none);
    if (connectivityResult == ConnectivityResult.none) {
      print("沒有網絡");	//沒有網絡時
    } else {
      if (connectivityResult == ConnectivityResult.mobile) {
        print("手機網絡");	//手機網絡時
      } else if (connectivityResult == ConnectivityResult.wifi) {
        print("wifi");		//wifi 時
      }
    }
  }
複製代碼

三、connectivity 獲取網絡環境

一、安裝與引入

connectivity: ^0.4.4
import 'package:connectivity/connectivity.dart';
複製代碼

二、使用

import 'package:connectivity/connectivity.dart';

var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile) {	//若是是手機網絡
  // I am connected to a mobile network.
} else if (connectivityResult == ConnectivityResult.wifi) {		//若是是wifi
  // I am connected to a wifi network.
}
複製代碼

四、視頻播放

  1. 在 Flutter 裏官方提供了一個 video_player插件能夠播放視頻。

  2. chewie,是一個非官方的第三方視頻播放組件,看起來好像是基於 HTML5 播放的組件。chewie 相對 video_player 來講,有控制欄和全屏的功能。Chewie 使用 video_player 引擎並將其包裹在友好的 Material 或 Cupertino UI 中!

一、安裝與引入

video_player: ^0.10.2+1
chewie: ^0.9.8

import 'package:video_player/video_player.dart';
import 'package:chewie/chewie.dart';
複製代碼

二、使用示例:

class _VideoAppState extends State<VideoApp> {
    VideoPlayerController _controller;
    bool _isPlaying = false;
    String url = 'http://vd3.bdstatic.com/mda-ifvq' +
                'u9yp3eaqueep/mda-ifvqu9yp3eaqueep.mp4';

    @override
    void initState() {
        super.initState();
        _controller = VideoPlayerController.network(this.url)
        // 播放狀態
        ..addListener(() {
            final bool isPlaying = _controller.value.isPlaying;
            if (isPlaying != _isPlaying) {
                setState(() { _isPlaying = isPlaying; });
            }
        })
        // 在初始化完成後必須更新界面
        ..initialize().then((_) {
            setState(() {});
        });
    }

    @override
    Widget build(BuildContext context) {
        return MaterialApp(
            title: 'Video Demo',
            home: new Scaffold(
                body: new Center(
                child: _controller.value.initialized
                    // 加載成功
                    ? new AspectRatio(
                        aspectRatio: _controller.value.aspectRatio,
                        child: VideoPlayer(_controller),
                    ) : new Container(),
                ),
                floatingActionButton: new FloatingActionButton(
                    onPressed: _controller.value.isPlaying
                        ? _controller.pause
                        : _controller.play,
                    child: new Icon(
                        _controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
                    ),
                ),
            ),
        );
    }
}
複製代碼

五、輪播圖

官網自詡 --> flutter最強大的siwiper, 多種佈局方式,無限輪播,Android和IOS雙端適配 .官網地址,中文的哦

一、安裝與引入

flutter_swiper: ^1.1.6
import 'package:flutter_swiper/flutter_swiper.dart';
複製代碼

二、參數

scrollDirection 	//Axis.horizontal 滾動方向,設置爲Axis.vertical若是須要垂直滾動
loop 				//true 無限輪播模式開關
index				//0 初始的時候下標位置
autoplay			//false 自動播放開關.
onIndexChanged		//void onIndexChanged(int index) 當用戶手動拖拽或者自動播放引發下標改變的時候調用
onTap				//void onTap(int index)當用戶點擊某個輪播的時候調用
duration			//300.0 動畫時間,單位是毫秒
pagination			//null 設置 `new SwiperPagination()` 展現默認分頁指示器
control				//null 設置 `new SwiperControl()` 展現默認分頁按鈕
    
//分頁指示器
//分頁指示器繼承自 `SwiperPlugin`,`SwiperPlugin` 爲 `Swiper` 提供額外的界面.設置爲`new SwiperPagination()` 展現默認分頁.
    
alignment	//Alignment.bottomCenter若是要將分頁指示器放到其餘位置,那麼能夠修改這個參數
margin		//const EdgeInsets.all(10.0)分頁指示器與容器邊框的距離
builder		//SwiperPagination.dots目前已經定義了兩個默認的分頁指示器樣式: `SwiperPagination.dots` 、 `SwiperPagination.fraction`,均可以作進一步的自定義.

//控制按鈕
iconPrevious	//Icons.arrow_back_ios 上一頁的IconData
iconNext		//Icons.arrow_forward_ios下一頁的IconData
color			//Theme.of(context).primaryColor 控制按鈕顏色
size			//30.0 控制按鈕的大小
padding			//const EdgeInsets.all(5.0)控制按鈕與容器的距離
    
//控制器
//SwiperController` 用於控制 Swiper的`index`屬性, 中止和開始自動播放. 經過 `new SwiperController()` 建立一個SwiperController實例,並保存,以便未來能使用。
void move(int index, {bool animation: true})	//移動到指定下標,設置是否播放動畫
void next({bool animation: true})				//下一頁
void previous({bool animation: true})			//上一頁
void startAutoplay()							//開始自動播放
void stopAutoplay()								//中止自動播放
 
//自動播放
autoplayDely			//默認值3000 自動播放延遲毫秒數.
autoplayDisableOnInteraction	//true 當用戶拖拽的時候,是否中止自動播放.
複製代碼

三、簡單使用

body: Column(
          children: <Widget>[
            Container(
              child: AspectRatio(		//可用來設置寬高比例的佈局組件
                aspectRatio: 16 / 9,	//設置寬高比例
                  //輪播圖
                child: Swiper(			
                  itemBuilder: (BuildContext context, int index) {		//圖片設置
                    return new Image.network(
                      imgList[index]["url"],
                      fit: BoxFit.fill,
                    );
                  },
                  itemCount: imgList.length,		//圖片數量
                  pagination: new SwiperPagination(),
                  control: new SwiperControl(),
                ),
              ),
            )
          ],
        )
複製代碼

9、第三方應用

有時候編寫代碼,在調用一些第三方應用(如攝像頭,瀏覽器,手電筒等等時,最好重啓一下項目

⭐、url_launcher 打開外部應用

一、安裝並引入

url_launcher: ^5.1.3
import 'package:url_launcher/url_launcher.dart';
複製代碼

二、使用 (官網示例)

//這是一個打開瀏覽器的範例

import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';		//引入

void main() {
  runApp(Scaffold(
    body: Center(
      child: RaisedButton(
        onPressed: _launchURL,			//觸發方法
        child: Text('Show Flutter homepage'),
      ),
    ),
  ));
}

_launchURL() async {			//這纔是重點
  const url = 'https://flutter.dev';		//定義路徑
  if (await canLaunch(url)) {	//canLaunch 表示是否能打開這個路徑,返回true或false,是異步操做
    await launch(url);				//若是能打開的話就打開路徑,使用默認瀏覽器,也是異步
  } else {
    throw 'Could not launch $url';
  }
}
複製代碼

三、一些其餘的簡單例子

//打開瀏覽器---------------------------------------------------------------------
RaisedButton(
  child: Text("打開瀏覽器"),
  onPressed: () async {
    const url = "http://www.baidu.com";		//訪問地址
    if(await canLaunch(url)){
      await launch(url);
    }else{
      throw "Conld not launch $url";
    }
  },
),
//撥打電話---------------------------------------------------------------------
RaisedButton(
  child: Text("撥打電話"),
  onPressed: () async {
    const tel = "tel:xxx";		//電話號碼xxx
    if(await canLaunch(tel)){
      await launch(tel);
    }else{
      throw "Conld not launch $tel";
    }
  },
),
//發送短信---------------------------------------------------------------------
RaisedButton(
  child: Text("發送短信"),
  onPressed: () async {
    const tel = "sms:xxx";		//與撥打電話相似,xxx也是電話號碼
    if(await canLaunch(tel)){
      await launch(tel);
    }else{
      throw "Conld not launch $tel";
    }
  },
),
//打開微信---------------------------------------------------------------------
RaisedButton(
  child: Text("打開微信"),
  onPressed: () async {
    const url = "weixin://";	//這是微信路徑
    if(await canLaunch(url)){
      await launch(url);
    }else{
      throw "Conld not launch $url";
    }
  },
),
//打開支付寶---------------------------------------------------------------------
RaisedButton(
  child: Text("打開支付寶"),
  onPressed: () async {
    const url = "alipays://";		//這是支付寶路徑
    if(await canLaunch(url)){
      await launch(url);
    }else{
      throw "Conld not launch $url";
    }
  },
),
複製代碼

schema集合

QQ: 		  mqq:// 
微信:			 weixin:// 
京東:			 openapp.jdmoble:// 
淘寶:			 taobao:// 
美團:			 imeituan:// 
點評:			 dianping:// 
1號店:		 wccbyihaodian:// 
支付寶:		alipay:// 
微博: 		sinaweibo:// 
騰訊微博: 	    TencentWeibo:// 
weico微博:	 weico:// 
知乎: 		zhihu:// 
豆瓣fm: 		doubanradio:// 
網易公開課:	  ntesopen:// 
Chrome: 	 googlechrome:// 
QQ瀏覽器:	   mqqbrowser:// 
uc瀏覽器: 	   ucbrowser:// 
搜狗瀏覽器: 	  SogouMSE:// 
百度地圖: 	   baidumap:// bdmap:// 
優酷: 		youku:// 
人人:		    renren:// 
我查查: 	   wcc:// 
有道詞典:	  yddictproapp:// 
微盤: 	    sinavdisk:// 
名片全能王: 	 camcard://
複製代碼

一、amap_location 定位

使用amap_location來進行定位,但須要在高德開發那裏拿到一個定位id,而後根據pub.dev上的說明一點一點的使用,下面是使用時的一小部分

void _getlocation() async {
    //先啓動一下
    await AMapLocationClient.startup(new AMapLocationOption(
        desiredAccuracy: CLLocationAccuracy.kCLLocationAccuracyHundredMeters));
    //直接獲取定位:
    var result = await AMapLocationClient.getLocation(true);
    print(result);
    setState(() {
      this._longitude = result.longitude;
      this._latitude = result.latitude;
    });
  }
複製代碼

二、image_picker 調用相機、相冊

一、下載與引入

image_picker: ^0.6.1+4
import 'package:image_picker/image_picker.dart';
複製代碼

二、使用

/*拍照*/
  _takePhoto() async {
    var image =							//可設置最小寬度maxWidth
        await ImagePicker.pickImage(source: ImageSource.camera, maxWidth: 400); 
    setState(() {
      this._image = image;
    });
    this._uploadImage(image);
  }

  /*打開相冊*/
  _openGallery() async {
    var image =			
        await ImagePicker.pickImage(source: ImageSource.gallery, maxWidth: 400);
    setState(() {
      this._image = image;
    });
  }

  //定義一個組件顯示圖片
  Widget _buildImage() {
    if (this._image == null) {
      return Text("");
    }
    return Image.file(this._image);
  }

  //上傳圖片
  _uploadImage(_imageDir) async {
    FormData formData = new FormData.from({
      "name": "zhangsna 6666666666",
      "age": 20,
      "sex": "男",
      "file": new UploadFileInfo(_imageDir, "xxx.jpg"),
    });
    var response =
        await Dio().post("http://jd.itying.com/imgupload", data: formData);
    print(response);
  }
}
複製代碼

核心代碼

await ImagePicker.pickImage(source: ImageSource.camera, maxWidth: 400);	//打開相機
await ImagePicker.pickImage(source: ImageSource.gallery, maxWidth: 400); //打開相冊

//上傳圖片 
FormData formData = new FormData.from({		//圖片的各類信息
      "name": "zhangsna 6666666666",
      "age": 20,
      "sex": "男",
      "file": new UploadFileInfo(_imageDir, "xxx.jpg"),	//_imageDir是咱們選擇的圖片
    });
await Dio().post("http://jd.itying.com/imgupload", data: formData);	
複製代碼
相關文章
相關標籤/搜索