0x00 前言
前面三篇文章:
寫了一個很簡單的應用,在將這個APP的功能複雜化之前,我們必須要選擇一個合適的框架,這樣才能避免程式碼失控,就是隨著APP功能的增加,程式碼的結構和管理越來越複雜。本篇文章將如何使用InheritedWidget來管理狀態。
0x01 程式碼
本篇文章所涉及的程式碼:
分支:InheritedWidget
0x02 InheritedWidget簡介
InheritedWidget用於在樹中傳遞資訊
- 為何InheritedWidget可以用於傳遞資訊?
因為InheritedWidget的實現是物件池,所有InheritedWidget的例項都在這個的物件池裡,想要的時候從這個物件池裡去取,所以可以在樹中的任何位置都拿到InheritedWidget的單例物件,所以可以做到在樹中傳遞資訊。
0x03 InheritedWidget原始碼解析
上面說的物件池,在InheritedWidget中是一個陣列,如下:
Map<Type, InheritedElement> _inheritedWidgets;
複製程式碼
是一個Map,把型別Type作為key,具體例項作為value,所以0x06講的在子節點獲取InheritedWidget,你就知道為何要傳Type了
假設一個子節點InheritedWidget的例項,持有_inheritedWidgets
陣列,這個陣列的值,首先是把這個InheritedWidget父節點的_inheritedWidgets
值賦值給子節點,然後子節點在把自己的例項新增到這個陣列,所以你也就可以明白0x04講的作用域,為啥InheritedWidget的作用域是自己及自己的子節點了。
0x04 InheritedWidget功能
InheritedWidget繼承自ProxyWidget,ProxyWidget繼承自Widget,可以單獨使用,但是沒有狀態,為了有狀態,一般和StatefulWidget搭配使用,搭配方法的示例如下: 需要有三個類:
- AppStateWidget(繼承StatefulWidget)
- AppState (建立InheritedWidget,AppState持有資料及業務邏輯)
- InheritedWidget(持有AppState)
0x05 InheritedWidget的作用域
InheritedWidget的作用域只能包括自己及自己的子節點,所以InheritedWidget在樹中只能向下傳遞,所以在天氣查詢的APP中,為了讓InheritedWidget的作用域是全域性的,得這麼做:
要覆蓋Material App 的根節點。
void main(){
runApp(WeatherControllerWidget(child: MyApp()));
}
複製程式碼
0x06 子節點獲取InheritedWidget的方法
使用 context.inheritFromWidgetOfExactType(Type targetType)
這個方法
將你想要獲取的InheritedWidget的型別作為引數傳進去
因為這個會經常用到,為了可讀性,一般會在自己的InheritedWidget裡新增of
方法,包裝成如下使用:
static WeatherControllerState of(BuildContext context){
return (context.inheritFromWidgetOfExactType(_WeatherInheritedWidget) as _WeatherInheritedWidget).state;
}
複製程式碼
0x07 天氣查詢APP重構
接下來我們要寫一個狀態管理的類,這個類持有天氣查詢APP裡所有的資料,以及實現資料請求的功能。程式碼如下:
import 'dart:convert';
import 'package:flutter/widgets.dart';
import 'package:gdg_weather/page/city/CityData.dart';
import 'package:gdg_weather/page/weather/WeatherData.dart';
import 'package:http/http.dart' as http;
//StatefulWidget 和 InheritedWidget配合使用
class WeatherControllerWidget extends StatefulWidget{
Widget child;
//這裡需要傳入child,這個引數,InheritedWidget初始化的時候需要用到
WeatherControllerWidget({this.child});
//這裡和其他StatefulWidget一樣,返回一個state
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return WeatherControllerState();
}
//這裡提供了一個static方法,是為了外面好獲取
static WeatherControllerState of(BuildContext context){
return (context.inheritFromWidgetOfExactType(_WeatherInheritedWidget) as _WeatherInheritedWidget).state;
}
}
//這個類是核心,用於狀態管理,持有資料,並且功能都在這裡實現
class WeatherControllerState extends State<WeatherControllerWidget>{
//持有的資料
List<CityData> cityList = new List<CityData>();
String curCityName;
WeatherData weather = WeatherData.empty();
//獲取城市列表的方法
void getCityList() async {
final response = await http.get('https://search.heweather.net/top?group=cn&key=ebb698e9bb6844199e6fd23cbb9a77c5');
List<CityData> list = new List<CityData>();
if(response.statusCode == 200){
//解析資料
Map<String,dynamic> result = json.decode(response.body);
for(dynamic data in result['HeWeather6'][0]['basic']){
CityData cityData = CityData(data['location']);
list.add(cityData);
}
}
setState(() {
cityList = list;
});
}
//獲取當前城市的實時天氣
void getCityWeather() async{
final response = await http.get('https://free-api.heweather.com/s6/weather/now?location='+curCityName+'&key=ebb698e9bb6844199e6fd23cbb9a77c5');
if(response.statusCode == 200){
setState(() {
weather = WeatherData.fromJson(json.decode(response.body));
});
}else{
setState(() {
weather = WeatherData.empty();
});
}
}
//表示選擇了哪個城市
void selectCity(String city){
curCityName = city;
}
//這裡返回了_WeatherInheritedWidget
@override
Widget build(BuildContext context) {
// TODO: implement build
return _WeatherInheritedWidget(
state: this,
child: widget.child,
);
}
}
//_WeatherInheritedWidget
class _WeatherInheritedWidget extends InheritedWidget{
WeatherControllerState state;
_WeatherInheritedWidget({
Key key,
@required this.state,
@required Widget child
}):super(key: key,child: child);
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
// TODO: implement updateShouldNotify
return true;
}
}
複製程式碼
0x08 傳遞資料
獲取WeatherState的方法:
final weatherState = WeatherControllerWidget.of(context);
複製程式碼
具體例子如下:
class CityState extends State<CityWidget>{
CityState(){
}
@override
Widget build(BuildContext context) {
// TODO: implement build
final weatherState = WeatherControllerWidget.of(context);
weatherState.getCityList();
return ListView.builder(
itemCount: weatherState.cityList.length,
itemBuilder: (context,index){
return ListTile(
title: GestureDetector(
child: Text(weatherState.cityList[index].cityName),
onTap:(){
weatherState.selectCity(weatherState.cityList[index].cityName);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => WeatherWidget())
);
},
),
);
});
}
}
複製程式碼