上一篇咱們對 Flutter UI 有了一個基本的瞭解。less
這一篇咱們經過自定義 Widget 來了解下如何寫一個 Widget?ide
然而 Widget 有兩個,StatelessWidget 和 StatefulWidget,咱們要繼承哪個?函數
下面讓咱們跟着文章來探索一番。字體
咱們先來看下繼承的 Widget 爲 StatelessWidget 的狀況。ui
第一步:新建一個文件 bold_text.dartthis
這裏文件名後面後綴 .dart 可帶可不帶3d
文件名多個單詞組成用下劃線分隔。code
這裏咱們演示直接在 lib 文件夾下面建立,實際項目記得文件夾結構的組織哦~blog
第二步: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 官網。
咱們以以前的 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!'), ); } }
對比能夠看到節省了不少代碼行,尤爲對於有多個地方用到的公共組件更加能夠這樣處理。
FileName爲你文件名的駝峯形式:
import 'package:flutter/material.dart'; class FileName extends StatelessWidget { @override Widget build(BuildContext context) { return _buildWidget(); } Widget _buildWidget() { //TODO build your widget } }
咱們再來看下繼承的 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 方法裏面寫。
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 文件就看得懂了。
好了,上面講解完了 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 初窺