3.Flutter Widget框架

1.介紹

中心思想就是用widget構建UI

2.Hello World

一個最簡單的Flutter應用程序,只需一個widget即可

import 'package:flutter/material.dart';
void main() {
  runApp(
    new Center(
      child: new Text(
        'Hello World!',
        textDirection: TextDirection.ltr,
      ),
    )
  );
}

  • runApp接受給定的widget,並將它作爲widget樹的根,在上面的代碼中,widget由Center和Text組成。框架強制根widget佔滿整個屏幕,文本居中顯示,文本顯示的方向需要在Text實例中用textDirection指定,當使用MaterialApp時,文本的方向將自動設定。
  • 在編寫應用程序的時候通常要創建新widget,這些widget是無狀態的StatelessWidget或者是有狀態的StatefulWidget,具體的選擇取決於你的widget是否需要管理一些狀態
  • widget的主要工作是實現一個build函數,用以構建自身
  • 一個widget通常由一些較低級別widget組成。Flutter框架將依次構建這些widget,直到構建到最底層的子widget時。這些最低層的widget通常爲RenderObject,它會計算並描述widget的幾何形狀。

3.基礎 Widget

Flutter有一套豐富、強大的基礎widget,其中以下是很常用的widget :

  • Text:可創建一個帶格式的文本。
  • Row、 Column: 可在水平(Row)和垂直(Column)方向上創建靈活的佈局。其設計是基於web開發中的Flexbox佈局模型。
  • Stack: 取代線性佈局 (和Android中的LinearLayout相似),Stack允許子 widget 堆疊, 你可以使用 Positioned 來定位他們相對於Stack的上下左右四條邊的位置。Stacks是基於Web開發中的絕度定位(absolute positioning )佈局模型設計的。
  • Container: 可創建矩形視覺元素。container 可以裝飾爲一個BoxDecoration, 如 background、一個邊框、或者一個陰影。 Container 也可以具有邊距(margins)、填充(padding)和應用於其大小的約束(constraints)。另外, Container可以使用矩陣在三維空間中對其進行變換。

下面創建一個 Container

//創建一個標題欄
class MyAppBar extends StatelessWidget{
  MyAppBar({this.title});

  // Widget子類中的字段往往都會定義爲"final"
  final Widget title;
  
  @override
  Widget build(BuildContext context) {
    return new Container( // 這裏是圓括號
      height: 64.0, //單位是邏輯上的像素(包括手機頂部的導航欄)
      padding: const EdgeInsets.symmetric(horizontal: 8.0), //左右都有8個像素的填充
      decoration: new BoxDecoration(color: Colors.blue[500]), //填充背景,後面的數字表示深淺
      // Row 是水平方向的線性佈局(linear layout),使用Row 佈局來排列其子項
      child: new Row(
        //列表項的類型是 <Widget>
        children: <Widget>[
          new IconButton(
              icon: new Icon(Icons.menu),
              tooltip: '導航欄',
              onPressed: null),  // null 會禁用 button
          // 中間的title widget被標記爲Expanded, ,這意味着它會填充尚未被其他子項佔用的的剩餘可用空間,
          // Expanded可以擁有多個children,然後使用flex參數來確定他們佔用剩餘空間的比例
          new Expanded(
              child: title
          ),
          new IconButton(
              icon: new Icon(Icons.search), 
              tooltip: '搜索',
              onPressed: null),
        ],
      ),
    );
  }
}

class MyScaffold extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Material 是UI呈現的“一張紙”
    return new Material(
      child: new Column(
        children: <Widget>[
          // 在Column的頂部,放置了一個MyAppBar實例,將一個Text widget作爲其標題傳遞給應用程序欄
          new MyAppBar(
            title: new Text(
              'Example title',
              style: Theme.of(context).primaryTextTheme.title,
            ),
          ),
          new Expanded(
              child: new Center(
                child: new Text('Hello World'),
              ) 
          ),
        ],
      ),
    );
  }

void main() {
  runApp(new MaterialApp(
    title: 'Container Test',
    home: new MyScaffold()
  )
  );
}

在這裏插入圖片描述

4.使用 Material 組件

  • Material應用程序以MaterialApp widget開始, 該widget在應用程序的根部創建了一些有用的widget,其中包括一個Navigator, 它管理由字符串標識的Widget棧(即頁面路由棧)。Navigator可以讓您的應用程序在頁面之間的平滑的過渡。
//創建Material樣式
class TutorialHome  extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    //Scaffold是Material中主要的佈局組件.
    return new Scaffold(
      appBar: new AppBar(
        leading: new IconButton(icon: new Icon(Icons.menu),tooltip: '導航欄', onPressed: null),
        title: new Text('搜索'),
        actions: <Widget>[
          new IconButton(icon: new Icon(Icons.search),tooltip: '搜索', onPressed: null)
        ],
      ),
      body: new Center(
        child: new Text('Hello World'),
      ),
      floatingActionButton: new FloatingActionButton(
          tooltip:'Add',
          child: new Icon(Icons.add),
          onPressed: null
      ),
    );
  }


void main() {
  runApp(new MaterialApp(
    title: 'Flutter  Test',
    home: new TutorialHome(),
  )
  );
}
  • 請注意,我們再次將widget作爲參數傳遞給其他widget。該 Scaffold widget 需要許多不同的widget的作爲命名參數,其中的每一個被放置在Scaffold佈局中相應的位置。 同樣,AppBar 中,我們給參數leading、actions、title分別傳一個widget。 這種模式在整個框架中會經常出現,這也可能是您在設計自己的widget時會考慮到一點。
    在這裏插入圖片描述

5.處理手勢

class MyButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new GestureDetector(
      onTap: () {
        print('MyButton was tapped');
      },

      child: new Container(
        height: 36.0,
        padding: const EdgeInsets.all(8.0),
        margin: const EdgeInsets.symmetric(horizontal: 8.0),
        decoration: new BoxDecoration(
          borderRadius: new BorderRadius.circular(5.0),
          color: Colors.lightGreen[500],
        ),
        child: new Center(
          child: new Text('Engage'),
        ),
      ),
    );
  }
}

void main() {
  runApp(new MaterialApp(
    title: 'ButtonTap  Test',
    home: new MyButton(),
  )
  );
}

在這裏插入圖片描述

  • GestureDetector widget並不具有顯示效果,而是檢測由用戶做出的手勢。 當用戶點擊Container時, GestureDetector會調用它的onTap回調, 在回調中,將消息打印到控制檯。您可以使用GestureDetector來檢測各種輸入手勢,包括點擊、拖動和縮放。

  • 許多widget都會使用一個GestureDetector爲其他widget提供可選的回調。 例如,IconButton、 RaisedButton、 和FloatingActionButton ,它們都有一個onPressed回調,它會在用戶點擊該widget時被觸發。

6.根據用戶輸入改變widget

//計數
class Counter extends StatefulWidget{
  @override
  _CounterState createState() => new _CounterState();
}

class _CounterState  extends State<Counter>{
  int _counter = 0;

  void _increment() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Row(
      children: <Widget>[
        new RaisedButton(
          onPressed: _increment,
          child: new Text('Increment'),
        ),
        new Text('Count : $_counter'),
      ],
    );
  }
}


void main() {
  runApp(new MaterialApp(
    title: 'ButtonTap  Test',
    home: new Counter(),
  )
  );
}

在這裏插入圖片描述

  • 無狀態widget從它們的父widget接收參數, 它們被存儲在final型的成員變量中。 當一個widget被要求構建時,它使用這些存儲的值作爲參數來構建widget。
  • StatefulWidgets是特殊的widget,它知道如何生成State對象,然後用它來保持狀態

爲什麼StatefulWidget和State是單獨的對象?

在Flutter中,這兩種類型的對象具有不同的生命週期: Widget是臨時對象,用於構建當前狀態下的應用程序,而State對象在多次調用build()之間保持不變,允許它們記住信息(狀態)。
class CounterDisplay extends StatelessWidget {
  // 注意:圓括號裏面有一個大括號
  CounterDisplay({this.count});

  final int count;

  @override
  Widget build(BuildContext context) {
    return new Text('Count :$count');
  }
}

class CounterIncrementor extends StatelessWidget{
  CounterIncrementor({this.onPressed});

  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    return new RaisedButton(
      onPressed: onPressed,
      child: new Text('Increment'),
    );
  }
}

class CounterUpdate extends StatefulWidget{
  @override
  _CounterStateUpdate createState() => new _CounterStateUpdate();
}

class _CounterStateUpdate  extends State<CounterUpdate>{
  int _counter = 0;

  void _increment() {
    setState(() {
      ++_counter;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Row(
      children: <Widget>[
        new CounterDisplay(count:_counter),
        new CounterIncrementor(onPressed: _increment),
      ],
    );
  }
}

void main() {
  runApp(new MaterialApp(
    title: 'ButtonTap  Test',
    home: new CounterUpdate(),
  )
  );
}

說明:

  1. Widget子類中的字段往往都會定義爲"final"
  2. 在Flutter中,事件流是“向上”傳遞的,而狀態流是“向下”傳遞的(譯者語:這類似於React/Vue中父子組件通信的方式:子widget到父widget是通過事件通信,而父到子是通過狀態),重定向這一流程的共同父元素是State
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章