【Flutter】仿京東地址選擇器(多層級無限制)

最近在做Flutter開發(fā)桌硫,碰到產品提的新的需求夭咬,類似于京東的地址選擇器,但是baidu了一堆铆隘,都只是定死層級的實現效果卓舵。不符合產品需求啊。自己碼一個膀钠,記錄一下掏湾。(可以根據需要,封裝成通用工具)
寫的不好肿嘲,請大佬輕噴融击。

效果圖


WeChat40dd05dfb8dccacffe42e73ee5ec7c22.png

需要注意,這里使用的是

TickerProviderStateMixin

而不是

SingleTickerProviderStateMixin

上代碼雳窟,注釋很清楚尊浪。

class EventAreaDialog extends StatefulWidget {
  // 這個是我們網絡拉取的數據,根據實際你所需要的去變封救。應該是個樹形結構的model
  SpaceData spaceData;
  // 選中后的callback
  EventAreaDialogCallBack eventAreaDialogCallBack;

  EventAreaDialog({this.spaceData, this.eventAreaDialogCallBack});

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

class _EventAreaDialogState extends State<EventAreaDialog>
    with TickerProviderStateMixin {
  //根據篩選出的數據
  Map<int, List<Children>> datas = new Map();
  //選中的model拇涤,根據業(yè)務需求獲取所需要的信息
  Map<int, String> selectDatas = new Map();
  //選中的名稱
  Map<int, String> selectNameDatas = new Map();
  //初始化的tab名稱
  List<String> _tabs = ["請選擇"];
  //tab的控制器
  TabController _tabController;
  //當前所選中的tab位置
  int currentTabPos = 0;

  @override
  void initState() {
    super.initState();
    //初始化第一層級所需要顯示的內容
    List<Children> list = [];
    for (int i = 0; i < widget.spaceData.data.length; i++) {
      Data data = widget.spaceData.data[i];
      list.add(new Children(data.id, data.children, data.name));
    }
    //datas第一層級初始化賦值
    datas[currentTabPos] = list;
    _tabController = new TabController(length: _tabs.length, vsync: this);
  }

  @override
  void dispose() {
    super.dispose();
    _tabController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: AnimatedPadding(
        padding: MediaQuery.of(context).viewInsets, //邊距(必要)
        duration: const Duration(milliseconds: 100), //時常 (必要)
        child: Container(
          decoration: BoxDecoration(
            borderRadius: BorderRadius.only(
              topLeft: Radius.circular(20),
              topRight: Radius.circular(20),
            ),
            color: Colors.white,
          ),
          height: 350,
          child: Flex(
            direction: Axis.vertical,
            children: <Widget>[
              Container(
                padding: new EdgeInsets.only(left: 10, right: 10),
                height: 40,
                child: Scaffold(
                  backgroundColor: Colors.transparent,
                  appBar: TabBar(
                    controller: _tabController,
                    unselectedLabelColor: ColorUtils.TEXT_GRAY,
                    labelColor: ColorUtils.TEXT_BLUE,
                    indicatorColor: ColorUtils.TEXT_BLUE,
                    labelStyle: TextStyle(fontSize: 16),
                    isScrollable: true,
                    indicatorWeight: 3,
                    indicatorSize: TabBarIndicatorSize.label,
                    indicatorPadding: new EdgeInsets.fromLTRB(8, 0, 8, 0),
                    onTap: (index) {
                      if (!mounted) {
                        return;
                      }
                      setState(() {
                        //獲取當前tab有多少
                        int size = _tabs.length;
                        //記錄當前位置
                        currentTabPos = index;
                        //循環(huán)處理,將當前位置之后的數據去除
                        for (int i = size; i > currentTabPos + 1; i--) {
                          _tabs.removeAt(i - 1);
                          selectDatas.remove(i - 1);
                          selectNameDatas.remove(i - 1);
                        }
                        //這一步是重新創(chuàng)建tab
                        _tabController = new TabController(
                            length: _tabs.length, vsync: this);
                        //將當前tab移動到選中的位置上
                        _tabController.animateTo(currentTabPos);
                      });
                    },
                    tabs: _tabs.map((e) => Tab(text: e)).toList(),
                  ),
                ),
              ),
              Expanded(
                  flex: 1,
                  child: TabBarView(
                    children: _buildPages(),
                    controller: _tabController,
                    physics: NeverScrollableScrollPhysics(),
                  )),
            ],
          ),
        ),
      ),
    );
  }

  /**
  *創(chuàng)建tab界面
  */
  List<Widget> _buildPages() {
    List<Widget> pages = List();
    for (int i = 0; i < _tabs.length; i++) {
      Widget page = new ListView.builder(
        padding: EdgeInsets.only(top: 15),
        itemCount: datas[currentTabPos].length,
        itemBuilder: (BuildContext context, int index) {
          return _getListItem(index);
        },
        physics: new BouncingScrollPhysics(),
        shrinkWrap: true,
      );
      pages.add(page);
    }
    return pages;
  }

  /**
  *創(chuàng)建每個界面的item顯示
  */
  Widget _getListItem(int index) {
    Children children = datas[currentTabPos][index];
    return GestureDetector(
      child: Container(
        alignment: Alignment.centerLeft,
        color: Colors.white,
        height: 50,
        padding: new EdgeInsets.only(left: 15),
        child: Text(children.name, style: TextStyle(fontSize: 15, color: selectDatas[currentTabPos] == children.id ? ColorUtils.TEXT_BLUE : ColorUtils.TEXT_GRAY),),
      ),
      onTap: () {
        if (!mounted) {
          return;
        }
        setState(() {
          if (currentTabPos > 0 && index == 0) {
            Navigator.pop(context);
            return;
          }
          if (children.children != null && children.children.length > 0) {//這是選中的tab還有下級數據時
            _tabs[currentTabPos] = children.name;
            currentTabPos++;
            //加上一條【暫不選擇】的空數據
            if(children.children[0].id != "-1") {
              children.children.insert(0, new Children("-1", [], "暫不選擇"));
            }
            //修改datas的數據源
            datas[currentTabPos] = children.children;
            selectDatas[currentTabPos - 1] = children.id;
            selectNameDatas[currentTabPos - 1] = children.name;
            _tabs.add("請選擇");
            _tabController =
                new TabController(length: _tabs.length, vsync: this);
            _tabController.animateTo(currentTabPos);
          } else {//這是選中的tab沒有下級數據時
            _tabs[currentTabPos] = children.name;
            selectDatas[currentTabPos] = children.id;
            selectNameDatas[currentTabPos] = children.name;
            Navigator.pop(context);
          }
          //將選中的區(qū)域id添加到集合誉结,根據自己的業(yè)務去甄別
          List<String> areaIds = [];
          for(int i = 0; i < selectDatas.length; i++) {
            areaIds.add(selectDatas[i]);
          }
          //這個是處理選中的名稱拼接鹅士,根據自己的業(yè)務去甄別
          String names = "";
          for(int i = 0; i < selectNameDatas.length; i++) {
            if (i == selectNameDatas.length - 1) {
              names += selectNameDatas[i];
            } else {
              names += "${selectNameDatas[i]}-";
            }
          }
          //將選中的數據實時回調到需要的地方
          widget.eventAreaDialogCallBack.onEventAreaDialogCallBack(names, areaIds);
        });
      },
    );
  }
}

最后使用方法

showModalBottomSheet(
    isScrollControlled: true,
    context: context,
    builder: (ctx) => EventAreaDialog(
        spaceData: spaceData,
        eventAreaDialogCallBack: this,
),backgroundColor: ColorUtils.CLEAR);
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市惩坑,隨后出現的幾起案子如绸,更是在濱河造成了極大的恐慌嘱朽,老刑警劉巖旭贬,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件怔接,死亡現場離奇詭異,居然都是意外死亡稀轨,警方通過查閱死者的電腦和手機扼脐,發(fā)現死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來奋刽,“玉大人瓦侮,你說我怎么就攤上這事∮缎常” “怎么了肚吏?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長狭魂。 經常有香客問我罚攀,道長,這世上最難降的妖魔是什么雌澄? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任斋泄,我火速辦了婚禮,結果婚禮上镐牺,老公的妹妹穿的比我還像新娘炫掐。我一直安慰自己,他們只是感情好睬涧,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布募胃。 她就那樣靜靜地躺著,像睡著了一般畦浓。 火紅的嫁衣襯著肌膚如雪痹束。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天宅粥,我揣著相機與錄音参袱,去河邊找鬼。 笑死秽梅,一個胖子當著我的面吹牛抹蚀,可吹牛的內容都是我干的。 我是一名探鬼主播企垦,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼环壤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了钞诡?” 一聲冷哼從身側響起郑现,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤湃崩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后接箫,有當地人在樹林里發(fā)現了一具尸體攒读,經...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年辛友,在試婚紗的時候發(fā)現自己被綠了薄扁。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡废累,死狀恐怖邓梅,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情邑滨,我是刑警寧澤日缨,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站掖看,受9級特大地震影響匣距,放射性物質發(fā)生泄漏。R本人自食惡果不足惜乙各,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一墨礁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧耳峦,春花似錦恩静、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至循签,卻和暖如春级乐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背县匠。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工风科, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乞旦。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓贼穆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親兰粉。 傳聞我的和親對象是個殘疾皇子故痊,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359