Flutter 即學即用系列博客第五彈來了。 自定義 Widget 讓你對 UI 瞭解更加深入。 還有 dart 語法糖
前言
上一篇我們對 Flutter UI 有了一個基本的瞭解。
這一篇我們通過自定義 Widget 來瞭解下如何寫一個 Widget?
然而 Widget 有兩個,StatelessWidget 和 StatefulWidget,我們要繼承哪一個?
下麵讓我們跟著文章來探索一番。
目錄
1. StatelessWidget
我們先來看下繼承的 Widget 為 StatelessWidget 的情況。
第一步:新建一個文件 bold_text.dart
這裡文件名後面尾碼 .dart 可帶可不帶
文件名多個單片語成用下劃線分隔。
這裡我們演示直接在 lib 文件夾下麵創建,實際項目記得文件夾結構的組織哦~
第二步:import 系統包
一般自定義 Widget 都要 import 下麵的一個包。
import 'package:flutter/material.dart';
IDE 有自動提示和補全功能,因此不用死記硬背。
第三步:自定義一個類繼承自 StatelessWidget
一般類名跟文件名一致就可以,採用駝峰格式命名。
import 'package:flutter/material.dart';
class BoldText extends StatelessWidget {
}
第四步:實現一個需要 override 的方法 build
import 'package:flutter/material.dart';
class BoldText extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return null;
}
}
一般第三步操作之後 IDE 有提示,直接使用快捷修複自動追加 build 代碼即可。如下圖:
第五步:實現 Widget
上述代碼的 TODO 表示我們要在裡面實現對應的 Widget。所以我們刪除 TODO,然後在寫我們要返回的 Widget 來替換 null 即可。
我們寫一個單獨的方法 **_buildWidget** 來返回 Widget,同時返回我們之前寫的 Text,如下:
import 'package:flutter/material.dart';
class BoldText extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _buildWidget();
}
Widget _buildWidget() {
return Text(
'Hello, world!',
textDirection: TextDirection.ltr,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.bold),
);
}
}
可以看到我們這個 Widget 應該會顯示成上篇我們界面所見的粗體文本。
但是這裡 Hello, world! 寫死了,我們要讓這個自定義 Widget 通用一些,可以定義一個必傳參數文本內容,修改如下:
import 'package:flutter/material.dart';
class BoldText extends StatelessWidget {
final String data;
BoldText(this.data);
@override
Widget build(BuildContext context) {
return _buildWidget();
}
Widget _buildWidget() {
return Text(
data,
textDirection: TextDirection.ltr,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.bold),
);
}
}
可以看到我們定義了一個變數,通過構造函數讓外部傳進來。
這裡的 BoldText(this.data); 等價於 Android 下麵代碼:
BoldText(String data) {
this.data = data;
}
可以看到 dart 的語法糖簡化了寫法。具體更多構造函數寫法可以查看 dart 官網。
2. 自定義 Widget 使用
我們以之前的 main.dart 為例進行講解。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Text(
'Hello, world!',
textDirection: TextDirection.ltr,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.bold),
),
);
}
}
第一步:導入我們的自定義 Widget 包
相對路徑:
import 'bold_text.dart';
絕對路徑:
import 'package:my_flutter/bold_text.dart';
上面任選其一即可。主要是相對路徑和絕對路徑的區別。
第二步:使用
import 'package:flutter/material.dart';
import 'bold_text.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: BoldText('Hello, world!'),
);
}
}
對比可以看到節省了很多代碼行,尤其對於有多個地方用到的公共組件更加可以這樣處理。
3. StatelessWidget 通用模板
FileName為你文件名的駝峰形式:
import 'package:flutter/material.dart';
class FileName extends StatelessWidget {
@override
Widget build(BuildContext context) {
return _buildWidget();
}
Widget _buildWidget() {
//TODO build your widget
}
}
4. StatefulWidget
我們再來看下繼承的 Widget 為 StatefulWidget 的情況。
第一步:新建 increment.dart 文件
第二步:import 系統包
第三步:自定義一個類繼承自 StatefulWidget
第四步:實現一個需要 override 的方法 createState
到這裡就有點不一樣了。我們先看下目前的代碼。
import 'package:flutter/material.dart';
class Increment extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return null;
}
}
和 StatelessWidget 不一樣,這裡不是返回 Widget。
我們看下如何操作。
第五步:創建一個類繼承 State< T extends StatefulWidget>
這裡我們創建 _IncrementState 類繼承 State< Increment>,這裡尖括弧<>裡面的類型就是我們一開始寫的繼承自 StatefulWidget 的類 Increment。
然後我們需要實現一個需要 override 的方法 build。
到這裡是不是就是很熟悉了。
直接看代碼:
import 'package:flutter/material.dart';
class Increment extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return _IncrementState();
}
}
class _IncrementState extends State<Increment> {
@override
Widget build(BuildContext context) {
// TODO: implement build
return null;
}
}
所以接下來的工作就是類似的。
第六步:實現 Widget
參考一開始的例子我們簡單寫出下麵代碼:
import 'package:flutter/material.dart';
class Increment extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return _IncrementState();
}
}
class _IncrementState extends State<Increment> {
int _count = 0;
void _incrementCount() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return _buildPage();
}
Widget _buildPage() {
return MaterialApp(
home: Scaffold(
body: Center(
child : Text('$_count')
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCount,
tooltip: 'Increment',
child: Icon(Icons.add),
),
),
);
}
}
這裡面需要說明的是多了一個新的 Widget FloatingActionButton。
可以看到它是作為 Scaffold 自帶的一個屬性的。
FloatingActionButton 講解:
onPressed 後面是這個按鈕點擊之後會回調的一個方法。
tooltip 是長按之後會顯示的提示文字。
child 是這個按鈕顯示的圖標。
我們修改 main.dart 文件如下,看下效果:
import 'package:flutter/material.dart';
import 'increment.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Increment();
}
}
效果如下:
這裡重點的代碼是下麵:
setState(() {
_count++;
});
它表示將數字加一之後更新界面。
需要更新界面時需要調用 setState 方法。
更新數據源可以在 setState 方法裡面寫。
5. StatefulWidget 通用模板
FileName為你文件名的駝峰形式,_FileNameState 裡面的 FileName 也是哦~
import 'package:flutter/material.dart';
class FileName extends StatefulWidget{
@override
State<StatefulWidget> createState() {
return _FileNameState();
}
}
class _FileNameState extends State<FileName> {
@override
Widget build(BuildContext context) {
return _buildPage();
}
Widget _buildPage() {
//TODO build your widget
}
}
到了這裡你回過頭去看新建 Flutter 項目時自動創建的 main.dart 文件就看得懂了。
6. StatelessWidget vs StatefulWidget
好了,上面講解完了 StatelessWidget 和 StatefulWidget,相信大家應該知道如何自定義一個 Widget 了,也知道如何在其他頁面引入了。
但是我們實際上在使用的時候到底是要繼承 StatelessWidget 還是 StatefulWidget 呢?
其實根據名稱可以看出取決於你這個 Widget 是有狀態還是無狀態?
不過「狀態」這個詞也不是好理解。
所以筆者是這樣來區分使用 StatelessWidget 還是 StatefulWidget的?
看界面是否需要更新
比如我們上面的例子,點擊按鈕文本更新了,所以我們選擇了 StatefulWidget。
而第一個只是字體調整,界面渲染之後不再需要更新了,所以我們選擇了 StatelessWidget。
所以我們可以認為當界面需要更新時,我們的自定義 Widget 就要繼承 StatefulWidget 而不是 StatelessWidget。
更多閱讀:
Flutter 即學即用系列博客——01 環境搭建
Flutter 即學即用系列博客——02 一個純 Flutter Demo 說明
Flutter 即學即用系列博客——03 在舊有項目引入 Flutter
Flutter 即學即用系列博客——04 Flutter UI 初窺