Flutter imita la barra de navegación personalizada de la libreta de direcciones de WeChat

¡Acostúmbrate a escribir juntos! Este es el cuarto día de mi participación en el "Nuevo plan diario de los Nuggets·Desafío de actualización de abril", haz clic para ver los detalles del evento .

Para algunas páginas, por ejemplo, cuando seleccionamos un contacto o una ciudad, necesitamos ubicar rápidamente las opciones que necesitamos. Generalmente, necesitamos una función como una barra de navegación en el lado derecho de la libreta de direcciones de WeChat, que puede ser rápidamente ubicado de la A a la Z. Este artículo En este artículo, implementaremos la misma función que la libreta de direcciones de WeChat por nosotros mismos.

  • Puntos clave: deslizamiento de posicionamiento de gestos, posicionamiento de listas, gestos, enlace de listas.

Para preparar los datos, primero debemos preparar los datos del directorio de navegación,

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", 
];
复制代码

Luego, los datos de la lista, los datos de la lista se pueden ordenar para nosotros por el fondo. Si el fondo no nos ordena, también podemos ordenarlos y ensamblarlos nosotros mismos. Aquí se usa un complemento para obtener las iniciales de pinyin según chino caracteres, y podemos hacer estos datos nosotros mismos. Ordenar por.

lpinyin: ^2.0.3
复制代码

Formato de datos: un NameBean corresponde a una lista de letras, este es el formato en el que almacenamos los datos de la lista formal.

class NameBean {
  String? initial;// 字母导航
  List<Name>? nameList;// 内容列表

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

/// name : "老李"

/// 这里我只放了一个name字段,以后扩展内容只需在这里新增字段就好了
class Name {
  String? name;

  Name({
    this.name,
  });
}
复制代码

A continuación, primero hagamos la barra de navegación:
Ideas de implementación: la barra de navegación es una lista. Hacer clic, deslizar y soltar tendrá diferentes interacciones. Las implementaremos de acuerdo con las diferentes interacciones. Aquí usaremos el componente oficial GestureDetector, específicamente Se utiliza para el reconocimiento de gestos. Corrija la altura de cada elemento y devuelva el directorio deseado a través de los datos de posición devueltos por la interacción de gestos. Por ejemplo, la altura de cada uno de mis directorios se establece en 20.
Código de barras de navegación: Al hacer clic o moverse, el directorio seleccionado tendrá un efecto de presión.

GestureDetector(
  child: Container(
      margin: EdgeInsetsDirectional.only(top: 40),
      width: 40,
      // 导航条
      child: ListView.builder(
        physics: NeverScrollableScrollPhysics(),
        shrinkWrap: true,
        itemBuilder: (context, index) {
          return SizedBox(
            height: 20,
            // 这里做了一个按压或移动滑动的触发效果
            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,
      )),
      //手指按下触发 竖着划就用onVertica XXX回调
  onVerticalDragDown: (DragDownDetails e) {
    //打印手指按下的位置(相对于屏幕)
    int i = e.localPosition.dy ~/ 20;
    // _scrollController.jumpTo(index: i);
    setState(() {
      currentIndex = i;
    });
  },
  //手指滑动时会触发此回调
  onVerticalDragUpdate: (DragUpdateDetails e) {
    //用户手指滑动时,更新偏移
    int i = e.localPosition.dy ~/ 20;
    _az.length;
    if (i >= 0 && i <= _az.length - 1) {
      if (i != currentIndex) {
        setState(() {
        // 当前选中的index 默认-1
          currentIndex = i;
        });
        print("滑动 ${_az[i]}");
       
       
      }
    }
  },
  // 手指抬起
  onTapUp: (e) {
    // 手指抬起
    setState(() {
      currentIndex = -1;
    });
  },
  // 移动取消
  onVerticalDragEnd: (e) {
    // 移动取消
    setState(() {
      currentIndex = -1;
    });
  },
)
复制代码

Luego podemos ver que hay una burbuja de ampliación de letras que se mueve con el gesto cuando se desliza WeChat.
Idea de implementación: la burbuja y la barra de navegación están una al lado de la otra, y el margen superior se puede actualizar de acuerdo con la posición del gesto. Debido a que la altura de cada elemento de nuestra barra de navegación es fija, podemos calcular la altura de la distancia de deslizamiento desde la parte superior de acuerdo con la posición de deslizamiento. Las burbujas aquí pueden permitir que la interfaz de usuario corte una imagen de fondo, o puede dibujar una usted mismo con el lienzo.
Código fuente de dibujo de burbujas: actualmente estoy aprendiendo a dibujar componentes, y dibujé uno por cierto. Puede que no sea el mejor, pero no es importante ~ El enfoque de hoy no es eso ~~~

@override
void paint(Canvas canvas, Size size) {
  // 原点移到左下角
  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));
}
复制代码

La barra de navegación y las burbujas están completas, el siguiente paso es muy simple, no podemos usar ListView para completar el contenido, aquí necesitamos un complemento oficial: scrollable_positioned_list: ^0.2.3utilícelo para ubicar la posición específica del elemento, para que podamos ubicar el lista Vinculada a la barra de navegación.
código de contenido:

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(
            // 禁用滑动事件
            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,
)
复制代码

El uso de ScrollablePositionedList es básicamente el mismo que el de ListView, excepto que tiene un método más, que puede ubicar un elemento específico.

_scrollController.jumpTo(index: i);
复制代码

Eche un vistazo al efecto final: ignore la repetición de datos... Es demasiado complicado completar los datos manualmente.Si no entiende algo, puede comunicarse.

0f353b0b-5595-4dc6-b6d5-5020c080b378.gif

  • Resumen: A través de este componente, podemos simplemente entender la operación de interacción de gestos de Flutter. A través del reconocimiento de gestos, podemos realizar muchos componentes interesantes, especialmente la combinación de dibujo y animación puede hacer interacciones muy interesantes. La capa inferior de todas las interacciones es a través de gestos. Después de completar la identificación, tendré tiempo para estudiar el código fuente y luego compartirlo con ustedes ~

Supongo que te gusta

Origin juejin.im/post/7083806995085525006
Recomendado
Clasificación