flutter 动手撸一个城市选择citypicker功能

城市选择器在项目开发中一般都会用到,基于flutter版本的也有一个city_pickers但是已经很久没有人维护了,项目中之前也用的是这个,最近升级到flutter1.17.x后,发现有一定的概率闪退,无奈之下,只能自动动手撸一个了

demo下载地址:https://github.com/qqcc1388/city_picker

CityPickerView能够实现以下功能

  • 显示省市区地址,市或者区可以为空白数据
  • 省市区数据支持自定义,但是格式要按照city.json中个格式来,如果需要外部传入省市区数据,直接使用params参数即可
  • 支持选中之后回调选中内容以及对于的省市区code码

思路

利用cupertino.dart中的CupertinoPicker来实现单层数据显示,通过row实现3层数据的滚动显示,然后让3个CupertinoPicker实现联动即可实现地址选择器,

代码

import 'dart:convert';
import 'package:city_picker/city_result.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

typedef ResultBlock = void Function(CityResult result);

class CityPickerView extends StatefulWidget {
  // json数据可以从外部传入,如果外部有值,取外部值
  final List params;
  // 结果返回
  final ResultBlock onResult;
  CityPickerView({this.onResult, this.params});
  @override
  _CityPickerViewState createState() => _CityPickerViewState();
}

class _CityPickerViewState extends State<CityPickerView> {
  List datas = [];
  int provinceIndex;
  int cityIndex;
  int areaIndex;

  FixedExtentScrollController provinceScrollController;
  FixedExtentScrollController cityScrollController;
  FixedExtentScrollController areaScrollController;

  CityResult result = CityResult();

  bool isShow = false;

  List get provinces {
    if (datas.length > 0) {
      if (provinceIndex == null) {
        provinceIndex = 0;
        result.province = provinces[provinceIndex]['label'];
        result.provinceCode = provinces[provinceIndex]['value'].toString();
      }
      return datas;
    }
    return [];
  }

  List get citys {
    if (provinces.length > 0) {
      return provinces[provinceIndex]['children'] ?? [];
    }
    return [];
  }

  List get areas {
    if (citys.length > 0) {
      if (cityIndex == null) {
        cityIndex = 0;
        result.city = citys[cityIndex]['label'];
        result.cityCode = citys[cityIndex]['value'].toString();
      }
      List list = citys[cityIndex]['children'] ?? [];
      if (list.length > 0) {
        if (areaIndex == null) {
          areaIndex = 0;
          result.area = list[areaIndex]['label'];
          result.areaCode = list[areaIndex]['value'].toString();
        }
      }
      return list;
    }
    return [];
  }

  // 保存选择结果
  _saveInfoData() {
    var prs = provinces;
    var cts = citys;
    var ars = areas;
    if (provinceIndex != null && prs.length > 0) {
      result.province = prs[provinceIndex]['label'];
      result.provinceCode = prs[provinceIndex]['value'].toString();
    } else {
      result.province = '';
      result.provinceCode = '';
    }

    if (cityIndex != null && cts.length > 0) {
      result.city = cts[cityIndex]['label'];
      result.cityCode = cts[cityIndex]['value'].toString();
    } else {
      result.city = '';
      result.cityCode = '';
    }

    if (areaIndex != null && ars.length > 0) {
      result.area = ars[areaIndex]['label'];
      result.areaCode = ars[areaIndex]['value'].toString();
    } else {
      result.area = '';
      result.areaCode = '';
    }
  }

  @override
  void dispose() {
    provinceScrollController.dispose();
    cityScrollController.dispose();
    areaScrollController.dispose();
    super.dispose();
  }

  @override
  void initState() {
    super.initState();

    //初始化控制器
    provinceScrollController = FixedExtentScrollController();
    cityScrollController = FixedExtentScrollController();
    areaScrollController = FixedExtentScrollController();

    //读取city.json数据
    if (widget.params == null) {
      _loadCitys().then((value) {
        setState(() {
          isShow = true;
        });
      });
    } else {
      datas = widget.params;
      setState(() {
         isShow = true;
      });
    }
  }

  Future _loadCitys() async {
    var cityStr = await rootBundle.loadString('assets/city.json');
    datas = json.decode(cityStr) as List;
    //result默认取第一组值
    return Future.value(true);
  }

  @override
  Widget build(BuildContext context) {
    return Material(
      child: Container(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            _firstView(),
            _contentView(),
          ],
        ),
      ),
    );
  }

  Widget _firstView() {
    return Container(
      height: 44,
      child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: <Widget>[
            FlatButton(
              child: Text('取消'),
              onPressed: () {
                Navigator.pop(context);
              },
            ),
            FlatButton(
              child: Text('确定'),
              onPressed: () {
                if (widget.onResult != null) {
                  widget.onResult(result);
                }
                Navigator.pop(context);
              },
            ),
          ]),
      decoration: BoxDecoration(
        border: Border(
            bottom: BorderSide(color: Colors.grey.withOpacity(0.1), width: 1)),
      ),
    );
  }

  Widget _contentView() {
    return Container(
      // color: Colors.orange,
      height: 200,
      child: isShow
          ? Row(
              children: <Widget>[
                Expanded(child: _provincePickerView()),
                Expanded(child: _cityPickerView()),
                Expanded(child: _areaPickerView()),
              ],
            )
          : Center(
              child: CupertinoActivityIndicator(
                animating: true,
              ),
            ),
    );
  }

  Widget _provincePickerView() {
    return Container(
      child: CupertinoPicker(
        scrollController: provinceScrollController,
        children: provinces.map((item) {
          return Center(
            child: Text(
              item['label'],
              style: TextStyle(color: Colors.black87, fontSize: 16),
              maxLines: 1,
            ),
          );
        }).toList(),
        onSelectedItemChanged: (index) {
          provinceIndex = index;
          if (cityIndex != null) {
            cityIndex = 0;
            if (cityScrollController.positions.length > 0) {
              cityScrollController.jumpTo(0);
            }
          }
          if (areaIndex != null) {
            areaIndex = 0;
            if (areaScrollController.positions.length > 0) {
              areaScrollController.jumpTo(0);
            }
          }
          _saveInfoData();
          setState(() {});
        },
        itemExtent: 36,
      ),
    );
  }

  Widget _cityPickerView() {
    return Container(
      child: citys.length == 0
          ? Container()
          : CupertinoPicker(
              scrollController: cityScrollController,
              children: citys.map((item) {
                return Center(
                  child: Text(
                    item['label'],
                    style: TextStyle(color: Colors.black87, fontSize: 16),
                    maxLines: 1,
                  ),
                );
              }).toList(),
              onSelectedItemChanged: (index) {
                cityIndex = index;
                if (areaIndex != null) {
                  areaIndex = 0;
                  if (areaScrollController.positions.length > 0) {
                    areaScrollController.jumpTo(0);
                  }
                }
                _saveInfoData();
                setState(() {});
              },
              itemExtent: 36,
            ),
    );
  }

  Widget _areaPickerView() {
    return Container(
      width: double.infinity,
      child: areas.length == 0
          ? Container()
          : CupertinoPicker(
              scrollController: areaScrollController,
              children: areas.map((item) {
                return Center(
                  child: Text(
                    item['label'],
                    style: TextStyle(color: Colors.black87, fontSize: 16),
                    maxLines: 1,
                  ),
                );
              }).toList(),
              onSelectedItemChanged: (index) {
                areaIndex = index;
                _saveInfoData();
                setState(() {});
              },
              itemExtent: 36,
            ),
    );
  }
}

class CityResult {
  /// 省市区
  String province = '';
  String city = '';
  String area = '';

  /// 对应的编码
  String provinceCode = '';
  String cityCode = '';
  String areaCode = '';

  CityResult({
    this.province,
    this.city,
    this.area,
    this.provinceCode,
    this.cityCode,
    this.areaCode,
  });

  CityResult.fromJson(Map<String, dynamic> json) {
    province = json['province'];
    city = json['city'];
    area = json['area'];
    provinceCode = json['provinceCode'];
    cityCode = json['cityCode'];
    areaCode = json['areaCode'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> datas = new Map<String, dynamic>();
    datas['province'] = this.province;
    datas['city'] = this.city;
    datas['area'] = this.area;
    datas['provinceCode'] = this.provinceCode;
    datas['cityCode'] = this.cityCode;
    datas['areaCode'] = this.areaCode;

    return datas;
  }
}

用法

 // var cityStr = await rootBundle.loadString('assets/city.json');
    // List datas = json.decode(cityStr) as List;
  showModalBottomSheet(
      context: context,
      isScrollControlled: true,
      builder: (ctx) {
        return CityPickerView(
          // params: datas,
          onResult: (res) {
            print(res.toJson());
            setState(() {
              citySelect = res.toJson().toString();
            });
          },
        );
      },
    );

demo下载地址:https://github.com/qqcc1388/city_picker
转载请标注来源:https://www.cnblogs.com/qqcc1388/p/13452530.html

到此这篇关于flutter 动手撸一个城市选择citypicker的文章就介绍到这了,更多相关flutter 城市选择citypicker内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Flutter 侧滑栏及城市选择UI的实现方法

    Flutter简介 Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面. Flutter可以与现有的代码一起工作.在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费.开源的. 它也是构建未来的Google Fuchsia 应用的主要方式. 目前移动市场上很多业务都需要开发Android/IOS两个端,开发成本比较高. Flutter 在跨端上凭借着性能优势关注量,使用度也持续上升.今天给大家分享在去年就写的一个Flu

  • flutter 动手撸一个城市选择citypicker功能

    城市选择器在项目开发中一般都会用到,基于flutter版本的也有一个city_pickers但是已经很久没有人维护了,项目中之前也用的是这个,最近升级到flutter1.17.x后,发现有一定的概率闪退,无奈之下,只能自动动手撸一个了 demo下载地址:https://github.com/qqcc1388/city_picker CityPickerView能够实现以下功能 显示省市区地址,市或者区可以为空白数据 省市区数据支持自定义,但是格式要按照city.json中个格式来,如果需要外部传

  • jQuery实现火车票买票城市选择切换功能

    效果图如下所示: <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <style> select { width: 200px; background-color: teal; height: 200px; font-size: 20px; } .btn-box { width

  • jQuery模拟12306城市选择框功能简单实现方法示例

    本文实例讲述了jQuery模拟12306城市选择框功能简单实现方法.分享给大家供大家参考,具体如下: <!DOCTYPE html> <html> <head> <title>www.jb51.net jQuery城市选择框</title> <style> #parent{ width:500px; position:relative; } #text{ height:25px; } #select{ width:420px; hei

  • Jetpack Compose 实现一个图片选择框架功能

    目录 获取图片 拍照策略 NothingCaptureStrategy FileProviderCaptureStrategy MediaStoreCaptureStrategy 总结 拍照权限 取消拍照导致的脏数据 resolveActivity API 的兼容性 File API 的兼容性 Github 知乎的 Matisse应该蛮多 Android 开发者有了解过或者是曾经使用过,这是知乎在 2017 年开源的一个 Android 端图片选择框架,其颜值在现在看来也还是挺不错的 可惜近几年

  • Node.js动手撸一个静态资源服务器的方法

    简介 本文介绍了一个简单的静态资源服务器的实例项目,希望能给Node.js初学者带来帮助.项目涉及到http.fs.url.path.zlib.process.child_process等模块,涵盖大量常用api:还包括了基于http协议的缓存策略选取.gzip压缩优化等:最终我们会发布到npm上,做成一个可以全局安装.使用的小工具.麻雀虽小,五脏俱全,一想是不是还有点小激动?话不多说,放码过来. 文中源码地址在最后附录中. 可先行体验项目效果: 安装:npm i -g here11 任意文件夹

  • js实现仿阿里巴巴城市选择框效果实例

    本文实例讲述了js实现仿阿里巴巴城市选择框效果.分享给大家供大家参考.具体分析如下: 这并不是一个城市选择插件,在这里介绍只是为了mark一下二级联动的方法,此效果适用于有二级子菜单的效果,如导航栏.城市选择.类别选择等等. 样式效果是基于阿里的样式,懒得做其他调整,在area.css中仅仅是为了修改浏览器兼容性略做了一点调整. 城市数据是通过js构造,当然也可以通过后端取得数据,不过感觉没必要. <!doctype html> <html> <head> <me

  • 微信小程序使用map组件实现获取定位城市天气或者指定城市天气数据功能

    本文实例讲述了微信小程序使用map组件实现获取定位城市天气或者指定城市天气数据功能.分享给大家供大家参考,具体如下: 效果图 实现原理 采用高德地图微信小程序开发API(getWeather),如果 city 属性的值为空(或者没有city属性),默认返回定位位置的天气数据:如果 city 不为空,则返回 city 指定位置的天气数据. WXML <view class="map-weather"> <view><text>城市:</text&

  • 基于Vue 撸一个指令实现拖拽功能

    之前撸了一个copy 指令,这次再撸一个拖拽指令.. 具体是个什么蛇皮玩意儿呢,大概就像介样: emmm..没错,看起来就是如此的鸡肋,但是莫得办法,大佬喜欢啊. 由于我们项目中用的是 element-ui ,所有这个指令只针对 element-ui 的对话框组件哈,如果你们用的别的 ui 库也有这个需求的,涂涂改改应该也能用.. 其实这个拖拽的原理还是很简单的: 1.首先鼠标按下( onmousedown ) 记录目标元素当前的 left 和 top 值 2.鼠标移动( onmousemove

  • 微信小程序城市选择及搜索功能的方法

    实现搜索城市功能 参考 微信小程序 之『仿美团城市选择 城市切换』 https://github.com/cinoliu/-selectCity js文件 // pages/address/address.js var app = getApp() Page({ data: { searchLetter: [], showLetter: "", winHeight: 0, cityList: [], isShowLetter: false, scrollTop: 0,//置顶高度 sc

随机推荐