Flutter 实现相似TabPicker省市区选择

最近作一个省市区选择的控件,产品的需求则是参考某银行的省市区选择的交互,是一个TabPicker的交互的控件git

1.主要的页面逻辑

自己Flutter自带有一个CupertinoPicker能够实现三级联动选择github

后面根据TabPicker交互,发现实现也不难,使用TabBar+TabBarView+ListView 就能够实现对应的交互功能bash

2.构建细节

主要是根据city_pickers 这个开源项目作的修改app

如下是实现的主要代码ui

2.1 构建底部弹出框

///构建底部弹出框
  Widget _bottomBuild() {
    if (provinceCityAreaList == null) {
      return Container();
    }
    return Scaffold(
      appBar: PreferredSize(
          child: Container(
            color: Colors.white,
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisAlignment: MainAxisAlignment.start,
              children: <Widget>[
                _buildHeaderTitle(),
                _buildHeaderTabBar(),
                Container(
                  height: 0.5,
                  margin: EdgeInsets.symmetric(horizontal: 16),
                  color: Color(0xFFEBEBEB),
                )
              ],
            ),
          ),
          preferredSize: Size.fromHeight(84)),
      body: TabBarView(
        controller: _tabController,
        children: _buildTabBarView(),
      ),
    );
复制代码

构建总体的弹框界面,使用PreferredSize来构建顶部的title和TabBar,body则是对应的TabBarView,TabBar和TabBarView则是经过同一个_tabController来联动的this

2.2 TabBarView 的实现逻辑

///构建Header tabBar
  List<Widget> _buildTabBarView() {
    List<Widget> tabList = [];
    tabList.add(_MyCityPicker(
      key: Key('province'),
      isShow: widget.showType.contain(ShowType.p),
      height: widget.height,
      controller: provinceController,
      value: targetProvince.label,
      itemList: provinceCityAreaList.map((v) {
        return LabelSelectBean(
            label: v.label, select: v.select == null ? false : v.select);
      }).toList(),
      changed: (index) {
        _onProvinceChange(index);
        Future.delayed(Duration(milliseconds: 200))
            .then((value) => _tabController.animateTo(1));
      },
    ));
    if (_provinceName != null) {
      tabList.add(_MyCityPicker(
        key: Key('citys $targetProvince'),
        // 这个属性是为了强制刷新
        isShow: widget.showType.contain(ShowType.c),
        controller: cityController,
        height: widget.height,
        value: targetCity == null ? null : targetCity.label,
        itemList: getCityItemList(),
        changed: (index) {
          _onCityChange(index);
        },
      ));
    }

    if (_cityName != null && widget.showType == ShowType.pca) {
      tabList.add(_MyCityPicker(
        key: Key('towns $targetCity'),
        isShow: widget.showType.contain(ShowType.a),
        controller: areaController,
        value: targetArea == null ? null : targetArea.label,
        height: widget.height,
        itemList: getAreaItemList(),
        changed: (index) {
          _areaName = targetCity.children[index].label;
          _onAreaChange(index);
        },
      ));
    }
    return tabList;
  }
复制代码

经过添加不一样的CityPicker,来动态的展现对应的TabBarView。spa

这里有个坑,动态添加TabBar和TabBarView的时候须要从新赋值给_tabController,以及动态添加TabBarView,二者要长度匹配才行,主要的实如今各自的选择回调方法code

setState(() {
          _tabLabels = [targetProvince.label, "请选择"];
          _provinceName = targetProvince.label;
          _tabController =
              TabController(length: _tabLabels.length, vsync: this);
        });
复制代码

每次改变TabController都须要动态设置一次ip

2.3 TabBarView展现

TabBar展现主要是展现List列表便可ci

_MyCityPicker主要的builder代码

Container(
      color: Colors.white,
      child: ListView.builder(
          itemBuilder: (context, index) {
            return Material(
                color: Colors.white,
                child: InkWell(
                  child: Container(
                      padding: EdgeInsets.only(left: 16, top: 8, bottom: 8),
                      child: Row(
                        children: <Widget>[
                          Text(
                            '${widget.itemList[index].label} ',
                            maxLines: 1,
                            style: TextStyle(color: Colors.black, fontSize: 16),
                            overflow: TextOverflow.ellipsis,
                          ),
                          widget.itemList[index].select
                              ? Icon(
                                  Icons.check,
                                  size: 20,
                                  color: Color(0xFFD71718),
                                )
                              : Container()
                        ],
                      )),
                  onTap: () {
                    widget.changed(index);
                  },
                ));
          },
          itemCount: widget.itemList.length),
    );
复制代码

就是一个简单的ListBuilder

3.数据回显

选择完数据以后须要,再一次点击的时候,须要回显上一次的数据,逻辑也比较简单

void _initLocation(String locationCode) {
    Result dataResult = widget.initDataResult;

    if (dataResult != null && dataResult.provinceName != null) {
      {
        CityPoint point = provinceCityAreaList.firstWhere(
            (province) => dataResult.provinceName == province.label,
            orElse: () => null);

        if (point != null) {
          targetProvince = point;
          targetProvince.select = true;
          _provinceName = targetProvince.label;
        } else {
          targetProvince = provinceCityAreaList[0];
        }
      }

      {
        CityPoint point = targetProvince.children.firstWhere(
            (city) => dataResult.cityName == city.label,
            orElse: () => null);

        if (point != null) {
          targetCity = point;
          targetCity.select = true;
          _cityName = targetCity.label;
        } else {
          targetCity = targetProvince.children[0];
        }
      }
      {
        CityPoint point = targetCity.children.firstWhere(
            (area) => dataResult.areaName == area.label,
            orElse: () => null);

        if (point != null) {
          targetArea = point;
          targetArea.select = true;
          _areaName = targetArea.label;
        } else {
          targetArea = targetCity.children[0];
        }
      }
    } else {
      targetProvince = provinceCityAreaList[0];
      targetCity = targetProvince.children[0];
      targetArea = targetCity.children[0];
    }
  }
复制代码

须要把回显的数据经过Result结果返回,而后遍历对应的List数据,设置便可

主要代码能够经过 fluttertabpicker 查看具体的代码逻辑

相关文章
相关标签/搜索