從零開始的Flutter之旅: StatelessWidgetgit
在以前的文章中,咱們介紹了StatelessWidget的特性與它在Flutter中的呈現原理。github
此次咱們接着來聊聊它的兄弟StatefulWidget,俗稱有狀態小部件。bash
若是你看了我以前的文章,你可能已經很是熟悉無狀態小部件StatelessWidget。它們是由一個藍圖與不可變的element配置來實現的,實際安裝到屏幕上的是各個StatelessElement。服務器
不可變的東西我是很是喜歡的,就像寫代碼同樣,一旦定義了一個不可變的變量,我就不用再關心它以後的全部事情,由於它不可變的性質,導致它不會發生不可預期的問題,只需直接使用它便可。微信
但一個程序只有不可變的配置是不行的,咱們不可能編寫一個只繪製一次後就中止的應用。由於一旦數據改變,不可變的配置是不可能幫助咱們刷新ui,達到咱們預期的效果;而有狀態小部件StatefulWidget卻能夠輕鬆解決這些事情。網絡
StatefulWidget提供不可變的配置信息以及能夠隨着時間變化而觸發的狀態對象;經過監聽狀態的變化來達到ui的更新。架構
簡單點,咱們從flutter_github挑選一個實例。框架
當咱們點擊其中一個未讀通知信息時,咱們須要將其ui狀態變成已讀的樣式。根據狀態來改變ui,StatefulWidget可以很好的實現這種場景。來看一下其實現less
class NotificationTabPage extends BasePage<_NotificationPageState> {
const NotificationTabPage();
@override
_NotificationPageState createBaseState() => _NotificationPageState();
}
class _NotificationPageState
extends BaseState<NotificationVM, NotificationTabPage> {
@override
NotificationVM createVM() => NotificationVM(context);
@override
Widget createContentWidget() {
return RefreshIndicator(
onRefresh: vm.handlerRefresh,
child: Scrollbar(
child: ListView.builder(
itemCount: vm.notifications?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
final NotificationModel item = vm.notifications[index];
return GestureDetector(
onTap: () {
vm.contentTap(index);
},
child: Container(
color: item.unread ? Colors.white : Color.fromARGB(13, 0, 0, 0),
padding: EdgeInsets.only(left: 15.0, top: 10.0, right: 15.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
item.repository.fullName,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
color: item.unread
? Colors.black87
: Color.fromARGB(255, 102, 102, 102),
),
),
Row(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 5.0),
child: Image.asset(
vm.getTypeFlagSrc(item.subject.type),
width: 18.0,
height: 18.0,
),
),
Expanded(
child: Padding(
padding: EdgeInsets.only(top: 5.0, left: 10.0),
child: Text(
item.subject.title,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
fontSize: 14.0,
color: item.unread
? Color.fromARGB(255, 17, 17, 17)
: Color.fromARGB(255, 102, 102, 102),
),
),
),
),
],
),
Padding(
padding: EdgeInsets.only(top: 10.0),
child: Divider(
height: 1.0,
endIndent: 0.0,
color: Color.fromARGB(255, 207, 216, 220),
),
),
],
),
),
);
},
),
),
);
}
}
複製代碼
這裏的BasePage是MSVM
架構中的基類,它繼承於StatefulWidget;_NotificationPageState也是同樣,它繼承於Stateasync
abstract class BasePage<S extends BaseState>
extends StatefulWidget {
...
}
abstract class BaseState<VM extends BaseVM, T extends StatefulWidget>
extends State implements VMSContract {
...
}
複製代碼
關於MSVM後續會專門開文章介紹,想了解的能夠期待一下
咱們來看createContentWidget方法中的佈局,找到上述狀況關聯的ui,在ListView的item中。
item佈局的狀態是根據item.unread來判斷的,未讀狀態爲ture。
當用戶onTap點擊時,將會向服務器發送thread閱讀請求,當請求成功以後,再將相應位置的item.unread值改成false。
但就這樣改變你會發現ui是不會刷新的,由於在StatefulWidget,若是你想改變某個值,同時要同步更新ui,須要使用setState方法。
_markThreadRead(int index) async {
try {
Response response =
await dio.patch('/notifications/threads/${_notifications[index].id}');
if (response != null &&
response.statusCode >= 200 &&
response.statusCode < 300) {
_notifications[index].unread = false;
notifyStateChanged();
}
} on DioError catch (e) {
Toast.show('makThreadRead error: ${e.message}', context);
}
}
複製代碼
這裏將setState方法封裝到notifyStateChanged方法中。因此如今再回過去看ui,會發現ui已經刷新了。
以上是使用StatefulWidget來達到ui的動態改變。再對比於以前的StatelessWidget,它們之間的區別顯而易見了。
與StatelessWidget同樣,接下來看下StatefulWidget的呈現原理。
StatefulWidget也是繼承於Widget,因此它的內部也是存在createElement方法。本質也是經過createElement來建立對應的Element Tree,只不過建立的是StatefulElement;而後再調用對應的Widget Tree中的build方法來獲取相應的藍圖。
但與StatelessWidget所不一樣的是,它還有另一個方法
@protected
State createState();
複製代碼
經過createState來建立對應的State。StatefulWidget保留了StatelessWidget的特性,即保證final數據的不變性,而對於非final可變數據,將經過Stete進行管理。
上面是以前StatelessWidget呈現原理圖,下面來對照看下StatefulWidget的。
除了Widget Tree與Element Tree,還有對應的State,它管理着可變的數據,例如item.unread。
一旦item.unread改變了,且通知到State,State將會再下一幀從新要求Widget Tree進行刷新。從新構建一個Container
因爲是同一種類型Container,將會直接被替換,同時使用更新後的item.unread,因此對應的Container的color也將發生改變。最終呈現的是佈局的刷新。
值得一提的是,State依附於Element Tree中,因此它的生命週期很是長,即便Widget Tree中的NotificationTabPage被移除重建,只要保證重建的類型是一致的,同時Widget Tree 與Element Tree的對應位置是沒有變化的,那麼Widget能夠避免重建,只是會將其標記爲髒狀態,而後它的子widget將會經過build方法進行重建,替換State中的變化的值。
若是你要監聽Widget的變化,能夠重寫didUpdateWidget
@override
void didUpdateWidget(StatefulWidget oldWidget) {
// TODO: implement didUpdateWidget
super.didUpdateWidget(oldWidget);
}
複製代碼
綜上所述,StatefulWidget使你能夠隨時跟蹤數據的變化並更新應用的ui。但你深刻Flutter以後,你會發現本身寫的更多的是StatelessWidget,由於須要用到的StatefulWidget基本上已經實現了,咱們更多的是對StatelessWidget的封裝,是否是頗有意思呢,期待你的加入。
文中的代碼都是來自於flutter_github,這是一個基於Flutter的Github客戶端同時支持Android與IOS,支持帳戶密碼與認證登錄。使用dart語言進行開發,項目架構是基於Model/State/ViewModel的MSVM;使用Navigator進行頁面的跳轉;網絡框架使用了dio。項目正在持續更新中,感興趣的能夠關注一下。
固然若是你想了解Android原生,相信flutter_github的純Android版本AwesomeGithub是一個不錯的選擇。
從零開始的Flutter之旅: InheritWidget
若是你喜歡個人文章模式,或者對我接下來的文章感興趣,能夠點擊一下個人頭像進行關注,固然您也能夠關注我微信公衆號:【Android補給站】
或者掃描下方二維碼,與我創建有效的溝通,同時更快更準的收到個人更新推送。