目錄
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(),
)
);
}
說明:
- Widget子類中的字段往往都會定義爲"final"
- 在Flutter中,事件流是“向上”傳遞的,而狀態流是“向下”傳遞的(譯者語:這類似於React/Vue中父子組件通信的方式:子widget到父widget是通過事件通信,而父到子是通過狀態),重定向這一流程的共同父元素是State