Flutter 实现类似TabPicker省市区选择

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

1.主要的页面逻辑

本身Flutter自带有一个CupertinoPicker可以实现三级联动选择

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

2.构建细节

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

以下是实现的主要代码

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来联动的

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。

这里有个坑,动态添加TabBar和TabBarView的时候需要重新赋值给_tabController,以及动态添加TabBarView,两者要长度匹配才行,主要的实现在各自的选择回调方法

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

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

2.3 TabBarView展示

TabBar展示主要是展示List列表即可

_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 查看具体的代码逻辑

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值