---
title: 'Flutter 狀態管理與共享數據'
tags: Flutter
disqus: hackmd
---
# Flutter 狀態管理與共享數據
**目錄:**
[TOC]
## 狀態管理
### setState
內建的setState是其中一種狀態管理辦法。
他會讓需要狀態的元件都重新加載
> 由於每次setState需要rebuild widgets,如果是隔很多層的傳遞,需要被rebuild的widgets會越多,相對效能會比較差,僅適合小規模的更新。

## 共享數據
### InheritedWidget
因為Flutter的特性,UI樹是一層包一層,若底層的widget若想取得上層的state會很麻煩(要一路傳下去)

所以Flutter提供了InheritedWidget,讓底層widget可以方便取得上層的state。
大概像以前,如果有資料大家都要用的話,會用Singleton。

InheritedWidget這邊提到的繼承,是Widget樹上的繼承,並不是我們以前物件導向常提到的Class的繼承。
而且InheritedWidget是讓別人繼承,不是去繼承別人
### 實作
widget架構:
* WeatherPageInheritedWidget
* appBar:DropdownButton
* body:WeatherWidget
* SkyWidget
1.建立InheritedWidget
```dart=
class WeatherWidget extends InheritedWidget {
//官方建議要用final, 這樣你就不能隨便去改它
final Weather todayWeather;
//因為是要放在上層, 所以建構子一定要有child, 跟你要共享的資料
const WeatherWidget(this.todayWeather, Widget cho, {super.key})
: super(child: cho);
//of方法是一個慣例, 代表這個widget是開放給大家取用, 官方建議這麼寫
static WeatherWidget? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<WeatherWidget>();
//如果用getElementForInheritedWidgetOfExactType, 繼承的widget就不會call didChangeDependencies
//return context.getElementForInheritedWidgetOfExactType<WeatherWidget>().widget;
}
//此widget更新後是否通知其他widget
@override
bool updateShouldNotify(WeatherWidget oldWidget) {
return oldWidget.todayWeather != todayWeather;
}
}
```
2.把InheritedWidget放在最上方
先把WeatherWidget做為body的root
然後利用WeatherPageInheritedWidget管理子widget的狀態
當appBar的DropdownButton改變後
就把state傳給WeatherWidget
```dart=
class _WeatherPageInheritedWidget extends State<WeatherPageInheritedWidget> {
Weather currentWeather = Weather.sunny;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text(
"天氣",
),
actions: <Widget>[
DropdownButton(
hint: null,
value: currentWeather,
items: const [
DropdownMenuItem(value: Weather.sunny, child: Text("晴天")),
DropdownMenuItem(value: Weather.rainy, child: Text("雨天")),
DropdownMenuItem(
value: Weather.typhoon, child: Text("颱風")),
DropdownMenuItem(
value: Weather.blizzard, child: Text("暴風雪")),
],
onChanged: (v) {
setState(() {
currentWeather = v ?? Weather.sunny;
});
})
],
),
backgroundColor: Colors.red,
body: WeatherWidget(currentWeather, const SkyWidget())));
}
}
```
3.取得InheritedWidget的狀態
先在build裡面透過of取得WeatherWidget的todayWeather
若InheritedWidget更新了
就會call didChangeDependencies
然後再call一次build
```dart=
class _SkyWidgetState extends State<SkyWidget> {
@override
Widget build(BuildContext context) {
Widget widgetIcon(IconData ico) {
return Container(
alignment: Alignment.center,
color: Colors.blue,
child: Icon(ico, color: Colors.white, size: 100),
);
}
switch (WeatherWidget.of(context)?.todayWeather) {
case Weather.sunny:
return widgetIcon(IcoFontIcons.sunny);
case Weather.rainy:
return widgetIcon(IcoFontIcons.rainy);
case Weather.typhoon:
return widgetIcon(IcoFontIcons.wind);
case Weather.blizzard:
return widgetIcon(IcoFontIcons.snow);
default:
return Container(child: null, color: Colors.red);
}
}
@override
void didChangeDependencies() {
debugPrint('did change dependencies');
super.didChangeDependencies();
}
}
```
PS. 不要直接去改InheritedWidget的state

> InheritedWidget改善了撰寫的麻煩,不過同樣有些缺點:
> * 無法將View與Logic部分分開。
>
> * 無法定向通知。
>
> * 每次更新都會通知所有Widgets,無法區分哪些Widgets需要被更新。
> 解決辦法可以透過**StreamBuilder**來監聽InheritedWidget中的**stream**的資料變化,然後判斷是否更新當前widget。
## 改善方式
請觀看下篇[Flutter 狀態管理與共享機制工具- Provider](https://hackmd.io/wMHf6UemTh-brgacNUf9Zg?both)