1. 程式人生 > 程式設計 >谷歌移動UI框架Flutter教程之Widget

谷歌移動UI框架Flutter教程之Widget

引言

在之間我已經介紹了關於Flutter的下載安裝以及配置,還有開發工具Android Studio的配置,還不知道的同學可以看看我這篇部落格——谷歌移動UI框架Flutter入門。這裡為什麼非要用Android Studio,我可以解釋一下。Android Studio是Google的親兒子,由谷歌一手開發,而Flutter也是谷歌推出的技術,所以在支援和相容問題上,Android Studio是非常有優勢的。老話說得好,肥水不流外人田,谷歌內部肯定是將Android Studio對Flutter的優化做到最佳的。

Widget基本元件

那麼話不多說,我們先來熟悉一下關於Flutter的Widget元件,在Flutter中,一切皆元件,TextView、Image、Row、Column等等,都統稱元件。

1.文字元件(Text)

首先,我們就來瞭解一下文字元件(Text)。學過前端的同學對UI部分應該都很瞭解,那Flutter當然也沒有什麼特別的,無非也就是文字內容、大小、字型樣式、顏色等等的設定,那麼首先我們就先來編寫一個案例。找到lib目錄下的main.dart,我們將在這個檔案中編寫程式碼。

import 'package:flutter/material.dart';

void main() {
  runApp(MyTextApp());
}

/**
 * 文字元件(Text)的使用
 */
class MyTextApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Text Demo',home: Scaffold(
        appBar: AppBar(title: Text('文字控制元件的使用'),),body: Center(
          child: Text(
            '這是一個文字控制元件',//文字內容
            textAlign: TextAlign.center,//居中
            maxLines: 1,//最大顯示行數
            style: TextStyle(
              fontSize: 25.0,//字型大小
              color: Colors.lightBlue,//字型顏色
            ),//樣式
          ),);
  }
}複製程式碼

有語言基礎的同學相信可以很好理解這些程式碼,第一行匯入了Material相關的類庫。程式會先執行main()方法,該方法又執行了runApp()方法,並將MyTextApp類作為引數傳遞。而MyTextApp類就是我們自定義的一個類,該類需要去繼承StatelessWidget,並重寫build()方法,該方法需要返回一個元件。具體的程式碼我就不一一介紹了,可以先不用理解每一行程式碼的意思。其中的Text便是文字元件,只需將值寫入括號,便可以在文字框中顯示,然後是文字框的一些屬性。接下來我們執行起來看一下。在這裡插入圖片描述

2.圖片元件(Image)

接下來是圖片元件,圖片元件的作用無非就是顯示圖片,在Flutter中,Image有四種方式顯示圖片,我只介紹一種,就是顯示網路圖片,其它三種方式沒有太大差別。

import 'package:flutter/material.dart';

void main() {
  runApp(MyImageApp());
}

/**
 * 圖片元件(Image)的使用
 */
class MyImageApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Text Demo',home: Scaffold(
        appBar: AppBar(title: Text('圖片元件的使用')),body: Center(
          child: Image.network(
            'https://www.baidu.com/img/baidu_jgylogo3.gif',//圖片地址
            scale: 1.0,//縮放比
          ),);
  }
}複製程式碼

執行效果如下:在這裡插入圖片描述

3.列表元件(ListView)

列表元件在移動端的開發中使用非常頻繁,那麼在Flutter中,該如何使用ListView呢?

import 'package:flutter/material.dart';

void main() {
  runApp(MyListViewApp());
}

/**
 * 列表元件(List)的使用
 */
class MyListViewApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Text Demo',body: Center(
          child: Container(
              height: 200.0,child: ListView(
              scrollDirection: Axis.horizontal,//列表方向(縱向)
              children: <Widget>[
                Container(
                  width: 180.0,color: Colors.lightBlue,Container(
                  width: 180.0,color: Colors.amber,color: Colors.deepOrange,color: Colors.deepPurple,//Container
              ],//Widget[]
            ),);
  }
}複製程式碼

有些同學看到這樣的程式碼可能驚呆了,這麼多層的巢狀維護起來豈不是很麻煩,其實這也是Dart語法的特點,避免不了,但是還是有辦法的,我們可以把ListView單獨抽出來,這樣主體的程式碼將會簡潔很多。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FlutterApp',home: Scaffold( //主頁
        appBar: AppBar(title: Text('FlutterDemo')),//標題
        body: Center(
          child: Container(
            height: 200.0,child: MyList(),//ListView
          ),//Container
        ),//主體
      ),//Scaffold
    ); //MaterialApp
  }
}

class MyList extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return ListView(
      scrollDirection: Axis.horizontal,children: <Widget>[
        Container(
          width: 180.0,Container(
          width: 180.0,//Container
      ],//Widget[]
    );
  }
}複製程式碼

現在執行看一下效果。在這裡插入圖片描述當然,這樣編寫列表在實際開發中是不現實的,我們應該讓列表活起來,所以,下面介紹如何實現動態列表。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp(
      items: List < String>.generate(1000,(i) => "Item $i")
  )
  );
}

class MyApp extends StatelessWidget {

  final List<String> items;

  MyApp({Key key,@required this.items}) :super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FlutterApp',//標題
        body: ListView.builder(
          itemCount: items.length,itemBuilder: (context,index) {
            return ListTile(
              title: Text('${items[index]}'),);
          },//Scaffold
    ); //MaterialApp
  }
}複製程式碼

這樣就實現了動態列表,只不過這個資料還是自己提供的,只需要後期通過網路獲取資料再封裝成集合然後傳遞即可。不懂Dart語法的同學對於裡面的某些程式碼可能會覺得難以理解,但是不用擔心。即使沒有一點Dart語言基礎的同學也是可以很容易地學會Flutter的,只不過在某些Dart語法上就只能死記了,記住它,不用管為什麼。那麼現在來執行看下效果。在這裡插入圖片描述

4.列表元件(GridView)

第二個列表元件,網格元件,該元件在如今的移動應用中也非常常見,最典型的便是系統相簿。那麼我們關心的是在Flutter中該如何去使用GridView呢?通過一個例子來瞭解一下。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FlutterApp',home: Scaffold( //主頁
          appBar: AppBar(title: Text('FlutterDemo')),//標題
          body: GridView(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 3,mainAxisSpacing: 2.0,//縱軸邊距
                crossAxisSpacing: 2.0,//橫軸邊距
                childAspectRatio: 0.7 //縮放比例(寬高比)
            ),children: <Widget>[
              Image.network(
                  'http://img5.mtime.cn/mg/2019/05/31/163641.36482297_270X405X4.jpg',fit: BoxFit.cover),Image.network(
                  'http://img5.mtime.cn/mg/2019/07/01/091243.35485139_270X405X4.jpg',Image.network(
                  'http://img5.mtime.cn/mg/2019/06/28/141449.40971533_270X405X4.jpg',Image.network(
                  'http://img5.mtime.cn/mg/2019/05/31/163641.36482297_270X405X4.jpg',],//Widget[]
          ) //GridView
      ),//Scaffold
    ); //MaterialApp
  }
}複製程式碼

網格元件其實也非常的簡單,和ListView其實沒有什麼差別,最主要的就是它獨特的屬性,這些屬性在官網檔案中都有解釋和示例。那麼這段程式碼執行的效果如何呢?我們看一下:在這裡插入圖片描述

佈局

Flutter中基本的一些元件就介紹完了,但是光知道如何編寫元件可遠遠不夠,UI設計中的佈局管理也尤為重要,那麼,我們繼續深入,瞭解一下Flutter中的佈局。

1.水平佈局(Row)

經過前面基本元件的學習,會發現Flutter無非就是一些元件的巢狀,但注意巢狀級別,不要被自己的程式碼搞暈了,那麼佈局其實是一樣的。我們看一個例子。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Row Widget Demo',home: Scaffold(
        appBar: AppBar(
          title: Text('水平方向佈局'),//AppBar
        body: Row(
          children: <Widget>[
            RaisedButton(
              onPressed: () {},color: Colors.redAccent,child: Text('Red Button'),RaisedButton(
              onPressed: () {},color: Colors.orangeAccent,child: Text('Orange Button'),child: Text('Blue Button'),//Widget[]
        ),//Row
      ),//Scaffold
    ); //MaterialApp
  }
}複製程式碼

Row即是水平佈局,那麼水平佈局中我們放置了三個按鈕,現在,執行看效果。在這裡插入圖片描述會發現 ,這個按鈕的右邊空出了一塊,這是為什麼呢?其實是因為我們使用的是一個不靈活的水平佈局,那麼既然有不靈活的水平佈局,那就肯定會有靈活的水平佈局。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Row Widget Demo',//AppBar
        body: Row(
          children: <Widget>[
            Expanded(child: RaisedButton(
              onPressed: () {},)),Expanded(child: RaisedButton(
              onPressed: () {},//Scaffold
    ); //MaterialApp
  }
}複製程式碼

我們並沒有對程式碼進行過多的修改,只是在每個按鈕外部包了一個Expanded元件,那麼現在我們來看一下執行效果:在這裡插入圖片描述會發現,按鈕成功自適應螢幕了,這才是我們想要的效果。

2.垂直佈局(Column)

既然有水平佈局,當然就有垂直佈局。現在通過一個例子來理解一下垂直佈局。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Row Widget Demo',home: Scaffold(
          appBar: AppBar(
            title: Text('水平方向佈局'),//AppBar
          body: Column(
            children: <Widget>[
              Text('Column 1'),Text('This is Column 2'),Text('Column 3'),//Widget
          ) //Column
      ),//Scaffold
    ); //MaterialApp
  }
}複製程式碼

應該不難理解,道理是一樣的,現在看一下效果:在這裡插入圖片描述細心的同學會發現,它預設會有一個居中的對齊方式。但有同學提出疑問了,這也沒居中啊,這不還是在螢幕的左側嗎?其實這個對齊是相對Column來說的,這個Column的大小是由最長的Text元件決定的。通過crossAxisAlignment屬性可以設定Column的對齊方式。

3.層疊佈局(Stack)

使用水平佈局和垂直佈局雖然可以實現大部分的佈局效果,但是如果要在一張圖片上顯示一段文字,這兩種佈局將無法實現。所以,這裡我們學習一種層疊佈局,它能夠很輕鬆地實現這個效果。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    var stack = Stack(
      alignment: const FractionalOffset(0.5,0.8),children: <Widget>[
        CircleAvatar(
          backgroundImage: NetworkImage(
              'https://i0.hdslb.com/bfs/archive/79c30cf5850cb9ec9d6129b200145e1644f696f8.jpg@880w_440h.jpg'),radius: 100.0,//CircleAvatar
        Container(
          decoration: BoxDecoration(
              color: Colors.lightBlue
          ),padding: EdgeInsets.all(5.0),child: Text('層疊佈局'),)
      ],//Widget[]
    ); //Stack
    return MaterialApp(
      title: 'Row Widget Demo',//AppBar
          body: Center(
            child: stack,)
      ),//Scaffold
    ); //MaterialApp
  }
}複製程式碼

我們首先建立一個元件變數,將我們的圖片和文字都定義在裡面,然後通過alignment屬性可以決定文字元件的相對位置,看一下效果:在這裡插入圖片描述

4.卡片佈局(Card)

最後一個佈局,卡片佈局。來看例子。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    var card = Card(
      child: Column(
        children: <Widget>[
          ListTile(
            title: Text(
              '江西省南昌市青雲譜區',style: TextStyle(fontWeight: FontWeight.w500),subtitle: Text('Temptation:123456789'),leading: Icon(Icons.account_box,new Divider(),ListTile(
            title: Text(
              '北京市海淀區中國科技大學',ListTile(
            title: Text(
              '河南省濮陽市百姓辦公樓',) //ListTile
        ],//Widget[]
      ),//Column
    ); //Card

    return MaterialApp(
      title: 'Row Widget Demo',//AppBar
          body: Center(
              child: card
          )
      ),//Scaffold
    ); //MaterialApp
  }
}複製程式碼

執行看效果:在這裡插入圖片描述篇幅有限,關於Flutter的元件和佈局就介紹到這裡,接下來還會有一篇關於Flutter的進階部落格,感興趣的同學可以看一看。