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

Flutter仿微信通訊錄實(shí)現(xiàn)自定義導(dǎo)航條的示例代碼

 更新時(shí)間:2022年04月12日 15:41:55   作者:老李code  
某些頁(yè)面比如我們?cè)谶x擇聯(lián)系人或者某個(gè)城市的時(shí)候需要快速定位到我們需要的選項(xiàng),一般都會(huì)需要像微信通訊錄右邊有一個(gè)導(dǎo)航條一樣的功能,本文將利用Flutter實(shí)現(xiàn)這一效果,需要的可以參考一下

某些頁(yè)面比如我們?cè)谶x擇聯(lián)系人或者某個(gè)城市的時(shí)候需要快速定位到我們需要的選項(xiàng),一般都會(huì)需要像微信通訊錄右邊有一個(gè)導(dǎo)航條一樣的功能,由A到Z進(jìn)行快速定位,本篇文章我們將自己來(lái)實(shí)現(xiàn)一個(gè)跟微信通訊錄同樣的功能。

關(guān)鍵點(diǎn):手勢(shì)定位滑動(dòng)、列表定位、手勢(shì)、列表聯(lián)動(dòng)。

準(zhǔn)備數(shù)據(jù),首先我們需要準(zhǔn)備導(dǎo)航目錄數(shù)據(jù),

List<String> _az = [
  "☆",
  "A",  "B", "C",  "D",
  "E",  "F",  "G", "H",
  "I",  "G",  "K", "L", 
  "M",  "N",  "O", "P",
  "Q",  "R",  "S", "T",
  "U",  "V", "W",  "X", 
  "Y", "Z", 
];

然后列表數(shù)據(jù),列表數(shù)據(jù)可以讓后臺(tái)給我們排序好,如果后臺(tái)不給我們排序,我們也可以自己排序進(jìn)行組裝, 這里用到一個(gè)插件,根據(jù)漢字獲取拼音首字母,我們自己就可以對(duì)這些數(shù)據(jù)進(jìn)行整理排序。

lpinyin: ^2.0.3

數(shù)據(jù)格式:一個(gè)NameBean對(duì)應(yīng)一個(gè)字母所在的列表,這是我們存儲(chǔ)正式列表數(shù)據(jù)的格式。

class NameBean {
  String? initial;// 字母導(dǎo)航
  List<Name>? nameList;// 內(nèi)容列表

  NameBean({
    this.initial,
    this.nameList,
  });
}

/// name : "老李"

/// 這里我只放了一個(gè)name字段,以后擴(kuò)展內(nèi)容只需在這里新增字段就好了
class Name {
  String? name;

  Name({
    this.name,
  });
}

接下來(lái)我們先做導(dǎo)航條:

實(shí)現(xiàn)思路: 導(dǎo)航條就是一個(gè)List列表,點(diǎn)擊、滑動(dòng)、松開(kāi)會(huì)有不同的交互,我們根據(jù)不同的交互來(lái)進(jìn)行實(shí)現(xiàn),這里我們會(huì)用到官方的GestureDetector組件,專(zhuān)門(mén)用來(lái)進(jìn)行手勢(shì)識(shí)別。將每一個(gè)item的高度固定,通過(guò)手勢(shì)交互返回的位置數(shù)據(jù)來(lái)進(jìn)行返回我們想要的目錄。比如我的每一個(gè)目錄高度設(shè)置為20.

導(dǎo)航條代碼:點(diǎn)擊或者移動(dòng)的時(shí)候選中的目錄會(huì)有一個(gè)按壓效果,

GestureDetector(
  child: Container(
      margin: EdgeInsetsDirectional.only(top: 40),
      width: 40,
      // 導(dǎo)航條
      child: ListView.builder(
        physics: NeverScrollableScrollPhysics(),
        shrinkWrap: true,
        itemBuilder: (context, index) {
          return SizedBox(
            height: 20,
            // 這里做了一個(gè)按壓或移動(dòng)滑動(dòng)的觸發(fā)效果
            child: Container(
              alignment: Alignment.center,
              decoration: BoxDecoration(
                  color:
                      currentIndex == index ? Colors.redAccent : null,
                  shape: BoxShape.circle),
              child: Text(
                _az[index],
                style: TextStyle(
                  color: currentIndex == index
                      ? Colors.white
                      : Colors.black87,
                ),
              ),
            ),
          );
        },
        itemCount: _az.length,
      )),
      //手指按下觸發(fā) 豎著劃就用onVertica XXX回調(diào)
  onVerticalDragDown: (DragDownDetails e) {
    //打印手指按下的位置(相對(duì)于屏幕)
    int i = e.localPosition.dy ~/ 20;
    // _scrollController.jumpTo(index: i);
    setState(() {
      currentIndex = i;
    });
  },
  //手指滑動(dòng)時(shí)會(huì)觸發(fā)此回調(diào)
  onVerticalDragUpdate: (DragUpdateDetails e) {
    //用戶手指滑動(dòng)時(shí),更新偏移
    int i = e.localPosition.dy ~/ 20;
    _az.length;
    if (i >= 0 && i <= _az.length - 1) {
      if (i != currentIndex) {
        setState(() {
        // 當(dāng)前選中的index 默認(rèn)-1
          currentIndex = i;
        });
        print("滑動(dòng) ${_az[i]}");
       
       
      }
    }
  },
  // 手指抬起
  onTapUp: (e) {
    // 手指抬起
    setState(() {
      currentIndex = -1;
    });
  },
  // 移動(dòng)取消
  onVerticalDragEnd: (e) {
    // 移動(dòng)取消
    setState(() {
      currentIndex = -1;
    });
  },
)

然后我們可以看到微信在滑動(dòng)的時(shí)候有個(gè)字母放大氣泡會(huì)跟隨著手勢(shì)移動(dòng)。

實(shí)現(xiàn)思路:

氣泡和導(dǎo)航條并列,并根據(jù)手勢(shì)位置更新上邊距即可,因?yàn)槲覀兊膶?dǎo)航條的每一個(gè)item的高度是固定的,所以我們就可以根據(jù)滑動(dòng)的位置計(jì)算出滑動(dòng)距離頂部的高度,這里氣泡可以讓UI切個(gè)背景圖,也可以自己用canvas畫(huà)一個(gè)。

氣泡繪制源碼:目前我在學(xué)習(xí)繪制組件,順便畫(huà)了一個(gè),可能不是最佳的,但這不重要~今天的重點(diǎn)不是它~~~

@override
void paint(Canvas canvas, Size size) {
  // 原點(diǎn)移到左下角
  canvas.translate(size.width / 2, size.height / 2);
  Paint paint = Paint()
    ..color = Colors.redAccent
    ..strokeWidth = 2
    ..style = PaintingStyle.fill;

  Path path = Path();
  // 繪制文字
  path.lineTo(0, -size.width / 2);
  // path.conicTo(33, -28, 20, 0, 1);

  path.arcToPoint(Offset(size.width / 2, 0),
      radius: Radius.circular(size.width / 2),
      largeArc: true,
      clockwise: true);
  path.close();
  var bounds = path.getBounds();
  canvas.save();
  canvas.translate(-bounds.width / 2, bounds.height / 2);
  canvas.rotate(pi * 1.2);
  canvas.drawPath(path, paint);
  canvas.restore();
  // 繪制文字
  var textPainter = TextPainter(
      text: TextSpan(
          text: text,
          style: TextStyle(
            fontSize: 24,
            foreground: Paint()
              ..style = PaintingStyle.fill
              ..color = Colors.white,
          )),
      textAlign: TextAlign.center,
      textDirection: TextDirection.ltr);
  textPainter.layout();
  canvas.translate(-size.width, -size.height / 2);
  textPainter.paint(canvas, Offset(-size.width / 2.4, size.height / 1.2));
}

導(dǎo)航條、氣泡都完成了,接下來(lái)就非常簡(jiǎn)單了,內(nèi)容的填充我們就不能用ListView了,這里我們需要一個(gè)官方的插件: scrollable_positioned_list: ^0.2.3 使用它可以定位到具體的item位置,這樣我們就可以進(jìn)行列表定位和導(dǎo)航條進(jìn)行聯(lián)動(dòng)了。

內(nèi)容代碼:

ScrollablePositionedList.builder(
  physics: BouncingScrollPhysics(),
  itemScrollController: _scrollController,
  itemBuilder: (context, index) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      mainAxisSize: MainAxisSize.min,
      children: [
        Container(
          padding: EdgeInsetsDirectional.only(start: 10),
          child: Text(dataList[index].initial ?? ""),
          color: Colors.grey.shade300,
          height: 30,
          width: double.infinity,
          alignment: Alignment.centerLeft,
        ),
        Container(
          padding: EdgeInsetsDirectional.only(start: 15),
          child: ListView.builder(
            // 禁用滑動(dòng)事件
            physics: NeverScrollableScrollPhysics(),
            shrinkWrap: true,
            itemBuilder: (context, cityIndex) {
              return InkWell(
                child: Container(
                  height: 40,
                  child: Column(
                    children: [
                      Expanded(
                        child: Container(
                          child: Text(dataList[index]
                                  .nameList?[cityIndex]
                                  .name ??
                              ""),
                          alignment: Alignment.centerLeft,
                        ),
                      ),
                      Divider(
                        height: 0.5,
                      )
                    ],
                  ),
                ),
                onTap: () {},
              );
            },
            itemCount: dataList[index].nameList?.length,
          ),
        )
      ],
    );
  },
  itemCount: dataList.length,
)

ScrollablePositionedList用法基本和ListView一直,只是它多了一個(gè)這個(gè)方法,可以定位到具體的item。

_scrollController.jumpTo(index: i);

看下最終效果:請(qǐng)忽略數(shù)據(jù)的重復(fù)...手動(dòng)填充數(shù)據(jù)太麻煩了,有哪里不懂可以交流哦

總結(jié):

通過(guò)這個(gè)組件我們可以簡(jiǎn)單的了解Flutter的手勢(shì)交互操作,通過(guò)手勢(shì)識(shí)別我們可以實(shí)現(xiàn)很多有意思的組件,尤其結(jié)合繪制和動(dòng)畫(huà)可以做出來(lái)非常有意思的交互,所有的交互最終的底層都是通過(guò)手勢(shì)識(shí)別完成的,以后有時(shí)間研究下源碼再和大家分享~

以上就是Flutter仿微信通訊錄實(shí)現(xiàn)自定義導(dǎo)航條的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Flutter微信導(dǎo)航條的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

雅江县| 吉木萨尔县| 邻水| 商丘市| 泸水县| 双城市| 嘉黎县| 屏东市| 滨海县| 玉林市| 罗江县| 静乐县| 政和县| 松滋市| 济南市| 邵东县| 祁阳县| 淳化县| 嘉义县| 利津县| 湖南省| 古浪县| 玉树县| 商南县| 永德县| 资阳市| 台前县| 邓州市| 佛教| 桃园市| 白银市| 扶绥县| 随州市| 聊城市| 阜新| 光泽县| 礼泉县| 沁源县| 镇坪县| 安阳市| 衡东县|