最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Flutter橫向樹形選擇器實(shí)現(xiàn)效果及完整代碼

 更新時間:2026年06月01日 09:33:32   作者:明君87997  
樹形菜單是Flutter本身不支持的,但在桌面端或 Web 端中是非常常見,所以設(shè)計一個樹形菜單組件是非常必要,它屬于一種基礎(chǔ)設(shè)施,這篇文章主要介紹了Flutter橫向樹形選擇器實(shí)現(xiàn)效果及完整代碼的相關(guān)資料,需要的朋友可以參考下

最近項(xiàng)目里需要做一個多級分類選擇,類似淘寶那種"電子產(chǎn)品 > 手機(jī) > iPhone"的效果。找了一圈沒看到合適的輪子,索性自己擼一個。

先看效果

大概就是這個樣子,點(diǎn)擊一級會展開下一級,頂部有面包屑導(dǎo)航可以回退,支持搜索。

為什么要橫向?

說實(shí)話一開始我是想用縱向折疊的方案的,但產(chǎn)品覺得層級多了以后太丑了,而且用戶不知道自己選到第幾層。后來看了一些競品,發(fā)現(xiàn)橫向展開+面包屑這個方案確實(shí)清晰很多,用戶一眼就知道自己選了什么。

核心思路

其實(shí)整個組件的核心就是維護(hù)兩個狀態(tài):

  • _breadcrumbs - 面包屑路徑,記錄用戶點(diǎn)了哪些父級
  • _currentOptions - 當(dāng)前顯示的選項(xiàng)列表

每次點(diǎn)擊一個有子項(xiàng)的選項(xiàng),就把它加到面包屑里,然后把它的 children 設(shè)成當(dāng)前選項(xiàng)列表。點(diǎn)面包屑就反向操作,截取數(shù)組就行。

/// 導(dǎo)航到子項(xiàng)
void _navigateToChildren(T parent) {
  setState(() {
    _breadcrumbs.add(parent);
    _currentOptions = parent.children?.map((child) => child as T).toList() ?? [];
  });
}

/// 導(dǎo)航到面包屑指定位置
void _navigateToBreadcrumb(int index) {
  setState(() {
    _breadcrumbs = _breadcrumbs.sublist(0, index + 1);
    _currentOptions = _breadcrumbs[index].children?.map((child) => child as T).toList() ?? [];
  });
}

回顯這個坑

最麻煩的其實(shí)是回顯,就是用戶之前選過的值要顯示出來。這就需要在整棵樹里遞歸查找,找到后把完整路徑構(gòu)建出來:

/// 遞歸查找項(xiàng)目路徑
bool _findItemPath(List<T> options, T target, List<T> path) {
  for (var option in options) {
    path.add(option);

    if (option.value == target.value) {
      return true;
    }

    if (option.children != null && option.children!.isNotEmpty) {
      List<T> children = option.children!.map((child) => child as T).toList();
      if (_findItemPath(children, target, path)) {
        return true;
      }
    }

    path.removeLast();  // 回溯
  }
  return false;
}

這個回溯算法挺經(jīng)典的,找到了就一路返回 true,沒找到就把剛加的節(jié)點(diǎn)刪掉繼續(xù)找下一個。

搜索功能

搜索功能也是后來加的,需求是只搜葉子節(jié)點(diǎn)(最終可選的項(xiàng)),然后顯示完整路徑讓用戶知道這個選項(xiàng)在哪。

void _searchInTree(List<T> options, String query, List<T> results, List<T> path) {
  for (var option in options) {
    final bool isLeaf = option.children == null || option.children!.isEmpty;
    final bool nameMatches = option.name?.toLowerCase().contains(query) == true;
    
    if (isLeaf && nameMatches) {
      results.add(option);
    }

    // 遞歸搜索子項(xiàng)
    if (option.children != null && option.children!.isNotEmpty) {
      List<T> newPath = List.from(path)..add(option);
      List<T> children = option.children!.map((child) => child as T).toList();
      _searchInTree(children, query, results, newPath);
    }
  }
}

數(shù)據(jù)結(jié)構(gòu)設(shè)計

為了通用性,我定義了一個抽象類,業(yè)務(wù)方繼承一下就能用:

abstract class OptionChildrenItem {
  String? name;
  String? value;
  List<OptionChildrenItem>? children;

  OptionChildrenItem({this.name, this.value, this.children});
}

也提供了一個默認(rèn)實(shí)現(xiàn) OptionChildrenItemImpl,不想自定義可以直接用。

使用方式

用起來很簡單,調(diào)一個方法就行:

final result = await showHorizontalTreePopup(
  context: context,
  options: _demoOptions,
  title: '請選擇分類',
  themeColor: Colors.blue,
  showSearch: true,  // 要不要搜索框
  showConfirmButton: true,  // 要不要確定按鈕,false 的話選中直接返回
  onConfirm: (selectedItem) {
    print('選中了: ${selectedItem?.name}');
  },
);

一些細(xì)節(jié)處理

  1. 底部安全區(qū) - iPhone 那個小橫條要留出來,用 MediaQuery.of(context).padding.bottom

  2. 泛型支持 - 整個組件是泛型的,T extends OptionChildrenItem,這樣業(yè)務(wù)方自己的數(shù)據(jù)模型也能用

  3. 兩種模式 - showConfirmButton 為 true 時需要點(diǎn)確定才返回,為 false 時點(diǎn)擊葉子節(jié)點(diǎn)直接返回,適合不同場景

總結(jié)

整體下來代碼量不大,700 多行,核心邏輯就是維護(hù)面包屑和當(dāng)前列表這兩個狀態(tài)。最坑的地方是泛型轉(zhuǎn)換,Dart 的泛型協(xié)變有點(diǎn)坑,children 那里需要手動 map 轉(zhuǎn)一下類型。

有需要的可以直接用,pub.dev 搜 horizontal_tree_selector 就行:

dependencies:
  horizontal_tree_selector: ^0.0.1

到此這篇關(guān)于Flutter橫向樹形選擇器實(shí)現(xiàn)效果及完整代碼的文章就介紹到這了,更多相關(guān)Flutter橫向樹形選擇器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

布尔津县| 电白县| 房产| 尼木县| 永年县| 鄂温| 牡丹江市| 宁南县| 丽水市| 临西县| 保亭| 潢川县| 四平市| 浏阳市| 花莲县| 祁东县| 曲周县| 巴林左旗| 高要市| 三亚市| 沈丘县| 射洪县| 陆川县| 舒兰市| 石林| 桐柏县| 尚义县| 东丽区| 边坝县| 姜堰市| 龙泉市| 石屏县| 连城县| 利川市| 缙云县| 平顶山市| 山丹县| 杨浦区| 微博| 宜宾市| 福清市|