Flutter实战4 -- 天气查询APP重构之状态管理(InheritedWidget)

0x00 前言

前面三篇文章:

  1. Flutter实战1 --- 写一个天气查询的APP
  2. Flutter实战2 --- 写一个天气查询的APP
  3. FFlutter实战3 --- PC上运行Flutter APP

写了一个很简单的应用,在将这个APP的功能复杂化之前,我们必须要选择一个合适的框架,这样才能避免代码失控,就是随着APP功能的增加,代码的结构和管理越来越复杂。本篇文章将如何使用InheritedWidget来管理状态。

0x01 代码

本篇文章所涉及的代码:

github.com/google/flut…

分支: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搭配使用,搭配方法的示例如下: 需要有三个类:

  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())
                );
              },
            ),
          );
        });
  }

}
复制代码
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很高兴为您解答问题!关于Flutter实战视频-移动电商-48.详细页_详情和评论的切换,我可以提供以下解答: 在Flutter实战视频-移动电商-48中,详细页的详情和评论的切换是通过一个TabBar实现的。具体来说,通过在AppBar中添加一个TabBar来实现不同页面之间的切换。在这个TabBar中,我们可以添加两个Tab,分别为“详情”和“评论”,并分别对应两个页面。当用户点击不同的Tab时,就可以切换到不同的页面。 在Flutter中,TabBar可以使用DefaultTabController和TabBarView来实现。DefaultTabController是一个Widget,它可以管理TabBar和TabBarView之间的关系。TabBarView则是一个Widget,它可以显示不同的页面。 具体实现方法如下: ```dart class DetailPage extends StatefulWidget { @override _DetailPageState createState() => _DetailPageState(); } class _DetailPageState extends State<DetailPage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('商品详情'), bottom: TabBar( tabs: <Widget>[ Tab( text: '详情', ), Tab( text: '评论', ), ], ), ), body: DefaultTabController( length: 2, child: TabBarView( children: <Widget>[ // 详情页面 DetailWidget(), // 评论页面 CommentWidget(), ], ), ), ); } } ``` 在上面的代码中,我们首先创建了一个AppBar,并在其中添加了一个TabBar。然后,在Scaffold的body中,我们创建了一个DefaultTabController,并将其length设为2,即有两个页面。最后,我们在TabBarView中添加了两个Widget,即DetailWidget和CommentWidget,分别对应详情页面和评论页面。 希望这个解答能够对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值