Flutter入門並開發天氣預報APP(8)——天氣預報第二步-選擇省、市、區界面及網絡請求

項目Github地址:a1203991686/CoolWeather_Flutter

第六章中我們寫了天氣預報的頁面, 但是你作爲天氣預報肯定能選擇城市吧。所以我們現在來寫選擇省、市、區的界面。

我們使用的是郭霖大神在第一行代碼最後面酷歐天氣的API。

1. 實現界面

既然是一個選擇省市區的界面,那麼我們就用ListView

首先看一下大致界面:
在這裏插入圖片描述

就直接使用ListViewbuilder()方法,忘了的同學可以看前面第5章

view文件夾下新建一個類provinces_page.dart,接下來就在這個文件裏面寫代碼:

class ProvincesPageWidget extends StatefulWidget {
    ProvincesPageWidget({Key key}) : super(key: key);

    @override
    ProvincesPageStateWidget createState() => new ProvincesPageStateWidget();
}

class ProvincesPageStateWidget extends State<ProvincesPageWidget> {
    @override
    Widget build(BuildContext context) {
        return Scaffold(
            appBar: new AppBar(
                title: Text(
                    "省份",
                    style: TextStyle(fontSize: 25.0),
                ),
            ),
            body: ListView.builder(
                itemCount: 30,
                itemBuilder: (context, index) {
                    return ListTile(
                            title: Text("$index"),
                    );
                },
            ),
        );
    }
}

和省一樣。在view文件夾下新建一個類city_page.dart,接下來就在這個文件裏面寫代碼。照着省的依葫蘆畫瓢,寫一個ListView

和省一樣。在view文件夾下新建一個類counties_page.dart,接下來就在這個文件裏面寫代碼。照着省的依葫蘆畫瓢,寫一個ListView

2. 網絡請求

網絡請求可以看下之前第7章的內容。主要是通過Dio進行網絡請求。大家可以看下第7章網絡請求的內容。

我們網絡請求主要需要以下幾個API:

  • 請求省份列表:http://guolin.tech/api/china
  • 請求對應市列表:http://guolin.tech/api/china/provinceID
  • 請求對應區縣列表:http://guolin.tech/api/china/provinceID/cityID

由於我們在之前文章中使用的省作爲例子,這塊我們就用區縣作爲例子:

轉爲Dart類

首先使用網站https://caijinglong.github.io/json2dart/來講Json數據轉爲Dart實體類:
在這裏插入圖片描述

然後在項目lib目錄新建一個文件夾。名爲bean。這個文件夾主要存放實體類的代碼。然後在這個文件夾下面新建一個dart文件,命名爲Counties。然後把生成的Dart代碼複製粘貼進去。但是你會發現複製進去後會報錯,這個不用急,接下來我們來處理錯誤。

生成.g.dart文件

接下來在項目根目錄的pubspec.yaml文件的dependencies項下面添加依賴json_annotationdev_dependencies項下面添加build_runnerjson_serializable

接着打開終端,定位到項目根目錄,輸入
flutter packages pub run build_runner build,運行即可。運行完畢你就會發現你項目的存實體類的lib/bean目錄多了一個文件,名爲Counties.g.dart。同時你Counties.dart裏面的錯誤也沒有了。

網絡請求

最後你就可以通過Dio來進行網絡請求了。

Dio().get(http://guolin.tech/api/china/$_provinceID/$_cityID);

那麼到這有的同學可能就會問了,你網址裏面有provinceIDcityID,那這兩個怎麼獲取,這個時候就得用上帶值路由跳轉了。

我們一般都是這樣的一個邏輯:先選擇省份、再選擇城市、最後選擇區和縣。所以我們這個CountyPageWidget肯定是由CityPageWidget調用的。那麼我們可以在CityPageWidgetListView裏面將Text通過GestureDetector包起來,然後將GestureDetectoronTap方法設置爲跳轉到CountyPageWidgetCityPageWidgetListView代碼如下:

ListView.builder(
    itemCount: _cityList.cities.length,
    itemBuilder: (context, index) {
        return GestureDetector(
            child: ListTile(
                title: Text("${_cityList.cities[index].name}"),
            ),
            onTap: () {
                Navigator.push(context,
                    MaterialPageRoute(builder: (context) {
                        return CountiesPageWidget(
                            provinceID: _provinceID, //由CityWidget的上一級ProvinceWidget傳過來,
                            cityID: _cityList.cities[index].id, // 由網絡請求回來的數據傳入
                        );
                    }));
            },
        );
    },
);

這樣CountyPageWidget所需的provinceIDcityID都是由CityPageWidget傳入的,那麼我們怎麼對他傳入的值進行處理呢,怎麼把它添加到網址裏去?接下來大家看代碼就行了:

class CountyPageWidget extends StatefulWidget {
  //在Widget中定義兩個,方便由其他Widget傳入
  final int provinceID;
  final int cityID;

  // 設置構造方法,在構造方法中傳入兩個變量
  CountyPageWidget({Key key, this.provinceID, this.cityID}) : super(key: key);

  @override
  CountyPageWidgetState createState() => CountyPageWidgetState();
}

class CountyPageWidgetState extends State<CountyPageWidget> {
  // 在State中定義兩個變量
  int _provinceID;
  int _cityID;
  List<County> _county;

  @override
  void initState() {
    // 在此將Widget中的兩個變量賦值給State中的變量
    _provinceID = widget.provinceID;
    _cityID = widget.cityID;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: new AppBar(
        title: Text(
          "區縣",
          style: TextStyle(fontSize: 25.0),
        ),
      ),
      body: FutureBuilder( // UI異步更新組件
        // 這塊就可以調用了
        future: Dio().get("http://guolin.tech/api/china/$_provinceID/$_cityID"),
        builder: (BuildContext context, AsyncSnapshot snapshot) {
        ...省略後面所有代碼
}

3. UI異步更新

與異步更新相關的知識大家也可以看我們之前的第7章博客,這塊只講應用。

將Scaffold的body參數設置爲FutureBuilder,並在FutureBuilder裏面調用ListView:

FutureBuilder(
    future: Dio().get("http://guolin.tech/api/china"),
    builder: (BuildContext context, AsyncSnapshot snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
            Response response = snapshot.data;
            //發生錯誤
            if (snapshot.hasError) {
                return Text(snapshot.error.toString());
            }

            provinceList = getProvinceList(response.data);
            //請求成功,通過項目信息構建用於顯示項目名稱的ListView
            return ListView.builder(
                itemCount: provinceList.length,
                itemBuilder: (context, index) {
                    return GestureDetector(
                        child: ListTile(
                            title: Text("${provinceList[index].name}"),
                        ),
                        onTap: () {
                            Navigator.push(context, MaterialPageRoute(builder: (context) {
                                return CityPageWidget(
                                    provinceID: provinceList[index].id,
                                );
                            }));
                        },
                    );
                },
            );
        }
        // 請求未完成時彈出loading
        return CircularProgressIndicator();
    },
)

與省同理:

FutureBuilder(
    future: Dio().get("http://guolin.tech/api/china/$_provinceID"),
    builder: (BuildContext context, AsyncSnapshot snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
            Response response = snapshot.data;
            //發生錯誤
            if (snapshot.hasError) {
                return Text(snapshot.error.toString());
            }

            _cityList = getCityList(response.data);
            //請求成功,通過項目信息構建用於顯示項目名稱的ListView
            return ListView.builder(
                itemCount: _cityList.length,
                itemBuilder: (context, index) {
                    return GestureDetector(
                        child: ListTile(
                            title: Text("${_cityList[index].name}"),
                        ),
                        onTap: () {
                            Navigator.push(context, MaterialPageRoute(builder: (context) {
                                return CountiesPageWidget(
                                    provinceID: _provinceID,
                                    cityID: _cityList[index].id,
                                );
                            }));
                        },
                    );
                },
            );
        }
        // 請求未完成時彈出loading
        return CircularProgressIndicator();
    },
)

區縣

與省同理:

FutureBuilder(
    future: Dio().get("http://guolin.tech/api/china/$_provinceID/$_cityID"),
    builder: (BuildContext context, AsyncSnapshot snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
            Response response = snapshot.data;
            //發生錯誤
            if (snapshot.hasError) {
                return Text(snapshot.error.toString());
            }

            _county = getCountyList(response.data);
            //請求成功,通過項目信息構建用於顯示項目名稱的ListView
            return ListView.builder(
                itemCount: _county.length,
                itemBuilder: (context, index) {
                    return GestureDetector(
                        child: ListTile(
                            title: Text("${_county[index].name}"),
                        ),
                        onTap: () {
                            _saveCityID(_county[index].weatherId);
                            Navigator.push(context, MaterialPageRoute(builder: (context) {
                                return MainPage(
                                    cityID: _county[index].weatherId,
                                );
                            }));
                        },
                    );
                },
            );
        }
        // 請求未完成時彈出loading
        return CircularProgressIndicator();
    },
)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章