Flutter實戰4 -- 天氣查詢APP重構之狀態管理(InheritedWidget)

0x00 前言

前面三篇文章:git

  1. Flutter實戰1 --- 寫一個天氣查詢的APP
  2. Flutter實戰2 --- 寫一個天氣查詢的APP
  3. FFlutter實戰3 --- PC上運行Flutter APP

寫了一個很簡單的應用,在將這個APP的功能複雜化以前,咱們必需要選擇一個合適的框架,這樣才能避免代碼失控,就是隨着APP功能的增長,代碼的結構和管理愈來愈複雜。本篇文章將如何使用InheritedWidget來管理狀態。github

0x01 代碼

本篇文章所涉及的代碼:json

github.com/google/flut…api

分支:InheritedWidget數組

0x02 InheritedWidget簡介

InheritedWidget用於在樹中傳遞信息bash

  • 爲什麼InheritedWidget能夠用於傳遞信息?

由於InheritedWidget的實現是對象池,全部InheritedWidget的實例都在這個的對象池裏,想要的時候從這個對象池裏去取,因此能夠在樹中的任何位置都拿到InheritedWidget的單例對象,因此能夠作到在樹中傳遞信息。框架

0x03 InheritedWidget源碼解析

上面說的對象池,在InheritedWidget中是一個數組,以下:async

Map<Type, InheritedElement> _inheritedWidgets;
複製代碼

是一個Map,把類型Type做爲key,具體實例做爲value,因此0x06講的在子節點獲取InheritedWidget,你就知道爲什麼要傳Type了ide

假設一個子節點InheritedWidget的實例,持有_inheritedWidgets數組,這個數組的值,首先是把這個InheritedWidget父節點的_inheritedWidgets值賦值給子節點,而後子節點在把本身的實例添加到這個數組,因此你也就能夠明白0x04講的做用域,爲啥InheritedWidget的做用域是本身及本身的子節點了。post

0x04 InheritedWidget功能

InheritedWidget繼承自ProxyWidget,ProxyWidget繼承自Widget,能夠單獨使用,可是沒有狀態,爲了有狀態,通常和StatefulWidget搭配使用,搭配方法的示例以下: 須要有三個類:

  1. AppStateWidget(繼承StatefulWidget)
  2. AppState (建立InheritedWidget,AppState持有數據及業務邏輯)
  3. 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())
                );
              },
            ),
          );
        });
  }

}
複製代碼
相關文章
相關標籤/搜索