Flutter(八)之Flutter的基礎Widget

Flutter的基礎Widget

前言一:接下來一段時間我會陸續更新一些列Flutter文字教程前端

更新進度: 每週至少兩篇;vue

更新地點: 首發於公衆號,次日更新於掘金、思否、開發者頭條等地方;算法

更多交流: 能夠添加個人微信 372623326,關注個人微博:coderwhyapi

但願你們能夠 幫忙轉發,點擊在看,給我更多的創做動力。服務器

前言二:這個章節原本打算講解Flutter的渲染原理,可是學習初期過多的講解原理性的內容,並不利於你們快速入門和上手,作出一些帶效果的內容;微信

因此,我打算換一種思路,先講解一些組件的用法,讓你們習慣Flutter的開發過程和模式,再回頭去鞏固原理性的知識;網絡

另外,在講解這些Widget的時候,我並不打算將全部的屬性一一列出,由於沒有意義,也記不住;數據結構

我後面打算有一個專題是關於Flutter佈局的,會選出一些好看的佈局界面帶着你們一塊兒來完成:美團頁面、京東頁面、B站頁面等等,某些我目前沒有講到的屬性,後面應用的會再進行講解;app

1. 文本Widget

在Android中,咱們使用TextView,iOS中咱們使用UILabel來顯示文本;less

Flutter中,咱們使用Text組件控制文本如何展現;

1.1. 普通文本展現

在Flutter中,咱們能夠將文本的控制顯示分紅兩類:

  • 控制文本佈局的參數: 如文本對齊方式 textAlign、文本排版方向 textDirection,文本顯示最大行數 maxLines、文本截斷規則 overflow 等等,這些都是構造函數中的參數;
  • 控制文本樣式的參數: 如字體名稱 fontFamily、字體大小 fontSize、文本顏色 color、文本陰影 shadows 等等,這些參數被統一封裝到了構造函數中的參數 style 中;

下面咱們來看一下其中一些屬性的使用:

class MyHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text(
      "《定風波》 蘇軾 \n莫聽穿林打葉聲,何妨吟嘯且徐行。\n竹杖芒鞋輕勝馬,誰怕?一蓑煙雨任生平。",
      style: TextStyle(
        fontSize: 20,
        color: Colors.purple
      ),
    );
  }
}
複製代碼

展現效果以下:

image-20190902101522966

咱們能夠經過一些屬性來改變Text的佈局:

  • textAlign:文本對齊方式,好比TextAlign.center
  • maxLines:最大顯示行數,好比1
  • overflow:超出部分顯示方式,好比TextOverflow.ellipsis
  • textScaleFactor:控制文本縮放,好比1.24

代碼以下:

class MyHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text(
      "《定風波》 蘇軾 \n莫聽穿林打葉聲,何妨吟嘯且徐行。\n竹杖芒鞋輕勝馬,誰怕?一蓑煙雨任生平。",
      textAlign: TextAlign.center, // 全部內容都居中對齊
      maxLines: 3, // 顯然 "生。" 被刪除了
      overflow: TextOverflow.ellipsis, // 超出部分顯示...
// textScaleFactor: 1.25,
      style: TextStyle(
        fontSize: 20,
        color: Colors.purple
      ),
    );
  }
}
複製代碼

image-20190902102035307

1.2. 富文本展現

前面展現的文本,咱們都應用了相同的樣式,若是咱們但願給他們不一樣的樣式呢?

  • 好比《定風波》我但願字體更大一點,而且是黑色字體,而且有加粗效果;
  • 好比 蘇軾 我但願是紅色字體;

若是但願展現這種混合樣式,那麼咱們能夠利用分片來進行操做(在Android中,咱們可使用SpannableString,在iOS中,咱們可使用NSAttributedString完成,瞭解便可)

代碼以下:

class MyHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text.rich(
      TextSpan(
        children: [
          TextSpan(text: "《定風波》", style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold, color: Colors.black)),
          TextSpan(text: "蘇軾", style: TextStyle(fontSize: 18, color: Colors.redAccent)),
          TextSpan(text: "\n莫聽穿林打葉聲,何妨吟嘯且徐行。\n竹杖芒鞋輕勝馬,誰怕?一蓑煙雨任生平。")
        ],
      ),
      style: TextStyle(fontSize: 20, color: Colors.purple),
      textAlign: TextAlign.center,
    );
  }
}
複製代碼

image-20190902103333353

二. 按鈕Widget

2.1. 按鈕的基礎

Material widget庫中提供了多種按鈕Widget如FloatingActionButton、RaisedButton、FlatButton、OutlineButton等

咱們直接來對他們進行一個展現:

class MyHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        FloatingActionButton(
          child: Text("FloatingActionButton"),
          onPressed: () {
            print("FloatingActionButton Click");
          },
        ),
        RaisedButton(
          child: Text("RaisedButton"),
          onPressed: () {
            print("RaisedButton Click");
          },
        ),
        FlatButton(
          child: Text("FlatButton"),
          onPressed: () {
            print("FlatButton Click");
          },
        ),
        OutlineButton(
          child: Text("OutlineButton"),
          onPressed: () {
            print("OutlineButton Click");
          },
        )
      ],
    );
  }
}
複製代碼

image-20190902105017343

2.2. 自定義樣式

前面的按鈕咱們使用的都是默認樣式,咱們能夠經過一些屬性來改變按鈕的樣式

RaisedButton(
  child: Text("贊成協議", style: TextStyle(color: Colors.white)),
  color: Colors.orange, // 按鈕的顏色
  highlightColor: Colors.orange[700], // 按下去高亮顏色
  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), // 圓角的實現
  onPressed: () {
    print("贊成協議");
  },
)
複製代碼

image-20190902110011756

事實上這裏還有一個比較常見的屬性:elevation,用於控制陰影的大小,不少地方都會有這個屬性,你們能夠自行演示一下

三. 圖片Widget

圖片可讓咱們的應用更加豐富多彩,Flutter中使用Image組件

Image組件有不少的構造函數,咱們這裏主要學習兩個:

  • Image.assets:加載本地資源圖片;
  • Image.network:加載網絡中的圖片;

3.1. 加載網絡圖片

相對來說,Flutter中加載網絡圖片會更加簡單,直接傳入URL並不須要什麼配置,因此咱們先來看一下Flutter中如何加載網絡圖片。

咱們先來看看Image有哪些屬性能夠設置:

const Image({
  ...
  this.width, //圖片的寬
  this.height, //圖片高度
  this.color, //圖片的混合色值
  this.colorBlendMode, //混合模式
  this.fit,//縮放模式
  this.alignment = Alignment.center, //對齊方式
  this.repeat = ImageRepeat.noRepeat, //重複方式
  ...
})
複製代碼
  • widthheight:用於設置圖片的寬、高,當不指定寬高時,圖片會根據當前父容器的限制,儘量的顯示其原始大小,若是隻設置widthheight的其中一個,那麼另外一個屬性默認會按比例縮放,但能夠經過下面介紹的fit屬性來指定適應規則。
  • fit:該屬性用於在圖片的顯示空間和圖片自己大小不一樣時指定圖片的適應模式。適應模式是在BoxFit中定義,它是一個枚舉類型,有以下值:
    • fill:會拉伸填充滿顯示空間,圖片自己長寬比會發生變化,圖片會變形。
    • cover:會按圖片的長寬比放大後居中填滿顯示空間,圖片不會變形,超出顯示空間部分會被剪裁。
    • contain:這是圖片的默認適應規則,圖片會在保證圖片自己長寬比不變的狀況下縮放以適應當前顯示空間,圖片不會變形。
    • fitWidth:圖片的寬度會縮放到顯示空間的寬度,高度會按比例縮放,而後居中顯示,圖片不會變形,超出顯示空間部分會被剪裁。
    • fitHeight:圖片的高度會縮放到顯示空間的高度,寬度會按比例縮放,而後居中顯示,圖片不會變形,超出顯示空間部分會被剪裁。
    • none:圖片沒有適應策略,會在顯示空間內顯示圖片,若是圖片比顯示空間大,則顯示空間只會顯示圖片中間部分。
  • colorcolorBlendMode:在圖片繪製時能夠對每個像素進行顏色混合處理,color指定混合色,而colorBlendMode指定混合模式;
  • repeat:當圖片自己大小小於顯示空間時,指定圖片的重複規則。

咱們對其中某些屬性作一個演練:

  • 注意,這裏我用了一個Container,你們能夠把它理解成一個UIView或者View,就是一個容器;
  • 後面我會專門講到這個組件的使用;
class MyHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        child: Image.network(
          "http://img0.dili360.com/ga/M01/48/3C/wKgBy1kj49qAMVd7ADKmuZ9jug8377.tub.jpg",
          alignment: Alignment.topCenter,
          repeat: ImageRepeat.repeatY,
          color: Colors.red,
          colorBlendMode: BlendMode.colorDodge,
        ),
        width: 300,
        height: 300,
        color: Colors.yellow,
      ),
    );
  }
}
複製代碼

image-20190902113310213

3.2. 加載本地圖片

加載本地圖片稍微麻煩一點,須要將圖片引入,而且進行配置

class MyHomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        width: 300,
        height: 300,
        color: Colors.yellow,
        child: Image.asset("images/test.jpeg"),
      ),
    );
  }
}
複製代碼

image-20190902114616699

3.3. 實現圓角圖像

在Flutter中實現圓角效果也是使用一些Widget來實現的。

3.3.1. 實現圓角頭像

方式一:CircleAvatar

CircleAvatar能夠實現圓角頭像,也能夠添加一個子Widget:

const CircleAvatar({
  Key key,
  this.child, // 子Widget
  this.backgroundColor, // 背景顏色
  this.backgroundImage, // 背景圖像
  this.foregroundColor, // 前景顏色
  this.radius, // 半徑
  this.minRadius, // 最小半徑
  this.maxRadius, // 最大半徑
}) 
複製代碼

咱們來實現一個圓形頭像:

  • 注意一:這裏咱們使用的是NetworkImage,由於backgroundImage要求咱們傳入一個ImageProvider;

    • ImageProvider是一個抽象類,事實上全部咱們前面建立的Image對象都有包含image屬性,該屬性就是一個ImageProvider
  • 注意二:這裏我還在裏面添加了一個文字,可是我在文字外層包裹了一個Container;

    • 這裏Container的做用是爲了能夠控制文字在其中的位置調整;
class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: CircleAvatar(
        radius: 100,
        backgroundImage: NetworkImage("https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg"),
        child: Container(
          alignment: Alignment(0, .5),
          width: 200,
          height: 200,
          child: Text("兵長利威爾")
        ),
      ),
    );
  }
}

複製代碼

image-20190924081343639

方式二:ClipOval

ClipOval也能夠實現圓角頭像,並且一般是在只有頭像時使用

class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ClipOval(
        child: Image.network(
          "https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg",
          width: 200,
          height: 200,
        ),
      ),
    );
  }
}

複製代碼

image-20190924092127687

實現方式三:Container+BoxDecoration

這種方式咱們放在講解Container時來說這種方式

3.3.2. 實現圓角圖片

方式一:ClipRRect

ClipRRect用於實現圓角效果,能夠設置圓角的大小。

實現代碼以下,很是簡單:

class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ClipRRect(
        borderRadius: BorderRadius.circular(10),
        child: Image.network(
          "https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg",
          width: 200,
          height: 200,
        ),
      ),
    );
  }
}
複製代碼

image-20190924094516174

方式二:Container+BoxDecoration

這個也放到後面講解Container時講解

四. 表單Widget

和用戶交互的其中一種就是輸入框,好比註冊、登陸、搜索,咱們收集用戶輸入的內容將其提交到服務器。

4.1. TextField的使用

4.1.1. TextField的介紹

TextField用於接收用戶的文本輸入,它提供了很是多的屬性,咱們來看一下源碼:

  • 可是咱們不必一個個去學習,不少時候用到某個功能時去查看是否包含某個屬性便可
const TextField({
  Key key,
  this.controller,
  this.focusNode,
  this.decoration = const InputDecoration(),
  TextInputType keyboardType,
  this.textInputAction,
  this.textCapitalization = TextCapitalization.none,
  this.style,
  this.strutStyle,
  this.textAlign = TextAlign.start,
  this.textAlignVertical,
  this.textDirection,
  this.readOnly = false,
  ToolbarOptions toolbarOptions,
  this.showCursor,
  this.autofocus = false,
  this.obscureText = false,
  this.autocorrect = true,
  this.maxLines = 1,
  this.minLines,
  this.expands = false,
  this.maxLength,
  this.maxLengthEnforced = true,
  this.onChanged,
  this.onEditingComplete,
  this.onSubmitted,
  this.inputFormatters,
  this.enabled,
  this.cursorWidth = 2.0,
  this.cursorRadius,
  this.cursorColor,
  this.keyboardAppearance,
  this.scrollPadding = const EdgeInsets.all(20.0),
  this.dragStartBehavior = DragStartBehavior.start,
  this.enableInteractiveSelection = true,
  this.onTap,
  this.buildCounter,
  this.scrollController,
  this.scrollPhysics,
}) 

複製代碼

咱們來學習幾個比較常見的屬性:

  • 一些屬性比較簡單:keyboardType鍵盤的類型,style設置樣式,textAlign文本對齊方式,maxLength最大顯示行數等等;
  • decoration:用於設置輸入框相關的樣式
    • icon:設置左邊顯示的圖標
    • labelText:在輸入框上面顯示一個提示的文本
    • hintText:顯示提示的佔位文字
    • border:輸入框的邊框,默認底部有一個邊框,能夠經過InputBorder.none刪除掉
    • filled:是否填充輸入框,默認爲false
    • fillColor:輸入框填充的顏色
  • controller
  • onChanged:監聽輸入框內容的改變,傳入一個回調函數
  • onSubmitted:點擊鍵盤中右下角的down時,會回調的一個函數

4.1.2. TextField的樣式以及監聽

咱們來演示一下TextField的decoration屬性以及監聽:

class HomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(20),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          TextFieldDemo()
        ],
      ),
    );
  }
}

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

class _TextFieldDemoState extends State<TextFieldDemo> {
  @override
  Widget build(BuildContext context) {
    return TextField(
      decoration: InputDecoration(
        icon: Icon(Icons.people),
        labelText: "username",
        hintText: "請輸入用戶名",
        border: InputBorder.none,
        filled: true,
        fillColor: Colors.lightGreen
      ),
      onChanged: (value) {
        print("onChanged:$value");
      },
      onSubmitted: (value) {
        print("onSubmitted:$value");
      },
    );
  }
}
複製代碼

image-20190923165526780

4.1.3. TextField的controller

咱們能夠給TextField添加一個控制器(Controller),可使用它設置文本的初始值,也可使用它來監聽文本的改變;

事實上,若是咱們沒有爲TextField提供一個Controller,那麼會Flutter會默認建立一個TextEditingController的,這個結論能夠經過閱讀源碼獲得:

@override
  void initState() {
    super.initState();
    // ...其餘代碼
    if (widget.controller == null)
      _controller = TextEditingController();
  }

複製代碼

咱們也能夠本身來建立一個Controller控制一些內容:

class _TextFieldDemoState extends State<TextFieldDemo> {
  final textEditingController = TextEditingController();

  @override
  void initState() {
    super.initState();

    // 1.設置默認值
    textEditingController.text = "Hello World";

    // 2.監聽文本框
    textEditingController.addListener(() {
      print("textEditingController:${textEditingController.text}");
    });
  }
	
  // ...省略build方法
}

複製代碼

image-20190923171132816

4.2. Form表單的使用

在咱們開發註冊、登陸頁面時,一般會有多個表單須要同時獲取內容或者進行一些驗證,若是對每個TextField都分別進行驗證,是一件比較麻煩的事情。

作過前端的開發知道,咱們能夠將多個input標籤放在一個form裏面,Flutter也借鑑了這樣的思想:咱們能夠經過Form對輸入框進行分組,統一進行一些操做。

4.2.1. Form表單的基本使用

Form表單也是一個Widget,能夠在裏面放入咱們的輸入框。

可是Form表單中輸入框必須是FormField類型的

  • 咱們查看剛剛學過的TextField是繼承自StatefulWidget,並非一個FormField類型;
  • 咱們可使用TextFormField,它的使用相似於TextField,而且是繼承自FormField的;

咱們經過Form的包裹,來實現一個註冊的頁面:

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

class _FormDemoState extends State<FormDemo> {
  @override
  Widget build(BuildContext context) {
    return Form(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          TextFormField(
            decoration: InputDecoration(
              icon: Icon(Icons.people),
              labelText: "用戶名或手機號"
            ),
          ),
          TextFormField(
            obscureText: true,
            decoration: InputDecoration(
              icon: Icon(Icons.lock),
              labelText: "密碼"
            ),
          ),
          SizedBox(height: 16,),
          Container(
            width: double.infinity,
            height: 44,
            child: RaisedButton(
              color: Colors.lightGreen,
              child: Text("注 冊", style: TextStyle(fontSize: 20, color: Colors.white),),
              onPressed: () {
                print("點擊了註冊按鈕");
              },
            ),
          )
        ],
      ),
    );
  }
}
複製代碼

image-20190923175224983

4.2.2. 保存和獲取表單數據

有了表單後,咱們須要在點擊註冊時,能夠同時獲取和保存表單中的數據,怎麼能夠作到呢?

  • 一、須要監聽註冊按鈕的點擊,在以前咱們已經監聽的onPressed傳入的回調中來作便可。(固然,若是嵌套太多,咱們待會兒能夠將它抽取到一個單獨的方法中)
  • 二、監聽到按鈕點擊時,同時獲取用戶名密碼的表單信息。

如何同時獲取用戶名密碼的表單信息?

  • 若是咱們調用Form的State對象的save方法,就會調用Form中放入的TextFormField的onSave回調:
TextFormField(
  decoration: InputDecoration(
    icon: Icon(Icons.people),
    labelText: "用戶名或手機號"
  ),
  onSaved: (value) {
    print("用戶名:$value");
  },
),
複製代碼
  • 可是,咱們有沒有辦法能夠在點擊按鈕時,拿到 Form對象 來調用它的save方法呢?

知識點:在Flutter如何能夠獲取一個經過一個引用獲取一個StatefulWidget的State對象呢?

答案:經過綁定一個GlobalKey便可。

image-20190923202433788

案例代碼演練:

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

class _FormDemoState extends State<FormDemo> {
  final registerFormKey = GlobalKey<FormState>();
  String username, password;

  void registerForm() {
    registerFormKey.currentState.save();

    print("username:$username password:$password");
  }

  @override
  Widget build(BuildContext context) {
    return Form(
      key: registerFormKey,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          TextFormField(
            decoration: InputDecoration(
              icon: Icon(Icons.people),
              labelText: "用戶名或手機號"
            ),
            onSaved: (value) {
              this.username = value;
            },
          ),
          TextFormField(
            obscureText: true,
            decoration: InputDecoration(
              icon: Icon(Icons.lock),
              labelText: "密碼"
            ),
            onSaved: (value) {
              this.password = value;
            },
          ),
          SizedBox(height: 16,),
          Container(
            width: double.infinity,
            height: 44,
            child: RaisedButton(
              color: Colors.lightGreen,
              child: Text("注 冊", style: TextStyle(fontSize: 20, color: Colors.white),),
              onPressed: registerForm,
            ),
          )
        ],
      ),
    );
  }
}
複製代碼

image-20190923202832219

4.2.3. 驗證填寫的表單數據

在表單中,咱們能夠添加驗證器,若是不符合某些特定的規則,那麼給用戶必定的提示信息

好比咱們須要帳號和密碼有這樣的規則:帳號和密碼都不能爲空。

按照以下步驟就能夠完成整個驗證過程:

  • 一、爲TextFormField添加validator的回調函數;
  • 二、調用Form的State對象的validate方法,就會回調validator傳入的函數;

image-20190923203843492

也能夠爲TextFormField添加一個屬性:autovalidate

  • 不須要調用validate方法,會自動驗證是否符合要求;

image-20190923204051768

備註:全部內容首發於公衆號,以後除了Flutter也會更新其餘技術文章,TypeScript、React、Node、uniapp、mpvue、數據結構與算法等等,也會更新一些本身的學習心得等,歡迎你們關注

公衆號
相關文章
相關標籤/搜索