IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Flutter 多标签自动换行及展开 -> 正文阅读

[移动开发]Flutter 多标签自动换行及展开

最终效果?

思路:

首先还是使用Wrap组件,然后计算item的宽度,当item们的的宽度超过屏幕-展开按钮的时候,隐藏剩余标签。

如何计算宽度?

这里没有采用GlobalKey的方式,是采用渲染完成之后把宽度放在组件内完成的。

用到的是 NotificationListener 用来监听 LayoutChangedNotification

子组件用SizeChangedLayoutNotifier包裹,这样在渲染完成的时候会调用onNotification方法,然后再通过context获取到组件的宽高,付给自身属性,一共组件使用。

但是由于SizeChangedLayoutNotifier的回掉方法是有触发机制的,看下SizeChangedLayoutNotification的源码,只有在初次初始化的时候才会发送通知。

这里直接复制一份,新起一个CustomSizeChangedLayoutNotification类,直接改成

?这里借鉴的是:flutter text内容高度不定问题解决_whs867712232的博客-CSDN博客_flutter text高度

其他的我已经写好了注释,直接上代码了。这里引用了 flutter_screenutil 组件

本文为抛砖引玉,肯定有不太合理的地方,路过的大佬们提提意见。

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_html/shims/dart_ui_real.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'dart:ui';

import 'package:scan/scan.dart';

 

class WrapAndExpanded extends StatefulWidget {
  const WrapAndExpanded({Key? key}) : super(key: key);

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

class _WrapAndExpandedState extends State<WrapAndExpanded>
    with WidgetsBindingObserver {
  // 页面的宽度
  double screenWidth = 1.sw;
  // 两边的间隔
  double screenPadding = 15;
  // 原始数据
  List<Widget> sourceWidgets = [];
  // 显示得数据
  List<Widget> showWidgets = [];
  double btnWidth = 50;
  fetchData() {
    List<String> list = [];
    list.add("value1value1value1value1value1value1");
    list.add("value1value1value1value1value1value1value1value1value1");
    list.add("value1value1value1");
    list.add("value1value1value1");
    list.add("value1");
    list.add("value1");
    list.add("value1");
    list.add("value1");
    list.add("value1value1value1");
    list.add("value1value1value1");
    list.add("value1value1value1");

    list.add("value1");
    list.add("value1");

    // 限制标签的最大宽度,多减10个单位,以防万一。
    double maxWidth = 1.sw - (screenPadding * 2) - btnWidth - 10;
    sourceWidgets = List.generate(list.length, (index) {
      return _CustomChip(
        text: list[index],
        maxWidth: maxWidth,
      );
    });
    // 数据同步
    showWidgets = sourceWidgets;
  }

  double chipWidth = 0;
  @override
  void initState() {
    fetchData();
    super.initState();
    // 当页面渲染完成的时候完成调用
    WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
      renderShowData();
    });
  }

  renderShowData() {
    // 临时存放要展示的数据
    List<Widget> tempList = [];
    // 展开按钮的宽度度

    // 计算长度
    for (var i = 0; i < sourceWidgets.length; i++) {
      chipWidth += (sourceWidgets[i] as _CustomChip).width;
      // 如果要是大于屏幕的宽度
      if (chipWidth >= (screenWidth * 2 - (screenPadding * 2) - btnWidth)) {
        tempList.add(Container(
          width: btnWidth,
          child: GestureDetector(
            child: Text("展开"),
            onTap: () {
              showWidgets = sourceWidgets;
              setState(() {});
            },
          ),
        ));
        break;
      } else {
        tempList.add(sourceWidgets[i]);
      }
    }
    showWidgets = tempList;
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.symmetric(horizontal: screenPadding),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Wrap(
            children: showWidgets,
          ),
          Text("测试是否有视觉问题")
        ],
      ),
    );
  }
}

class _CustomChip extends StatefulWidget {
  final String text;
  // 组件宽度
  double width = 0;
  // 组件高度
  double height = 0;
  // 水平外边距
  double horizontalMargin;
  // 水平内边距
  double horizontalPadding;
  // 垂直外边距
  double verticalMargin;
  // 垂直内边距
  double verticalPadding;
  // maxWidth
  double maxWidth;
  _CustomChip(
      {Key? key,
      required this.text,
      this.horizontalMargin = 2.0,
      this.horizontalPadding = 2.0,
      this.verticalMargin = 2.0,
      this.verticalPadding = 2.0,
      this.maxWidth = 100.0})
      : super(key: key);

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

class __CustomChipState extends State<_CustomChip> {
  _printSize() {
    if (!mounted) return;
    var size = context.findRenderObject()?.paintBounds.size;
    if (size != null) {
      // 计算宽度的时候加上两边的间距
      widget.width = size.width +
          (widget.horizontalMargin * 2) +
          (widget.horizontalPadding * 2);
      // 如果计算高速则加上
      widget.height = size.height +
          (widget.verticalMargin * 2) +
          (widget.verticalPadding * 2);
      // print(size.toString());
    }
  }

  @override
  Widget build(BuildContext context) {
    return NotificationListener<LayoutChangedNotification>(
      onNotification: (notification) {
        /// 收到布局结束通知,打印尺寸
        _printSize();
        return false;
      },
      child: CustomSizeChangedLayoutNotifier(
        child: Container(
          constraints: BoxConstraints(
              // 限制单个标签最长长度
              maxWidth: widget.maxWidth),
          color: Colors.grey,
          margin: EdgeInsets.symmetric(
              vertical: widget.verticalMargin,
              horizontal: widget.horizontalPadding),
          padding: EdgeInsets.symmetric(
              vertical: widget.verticalPadding,
              horizontal: widget.horizontalMargin),
          child: Text(
            widget.text,
            style: TextStyle(),
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
          ),
        ),
      ),
    );
  }
}

class CustomSizeChangedLayoutNotifier extends SingleChildRenderObjectWidget {
  /// Creates a [SizeChangedLayoutNotifier] that dispatches layout changed
  /// notifications when [child] changes layout size.
  const CustomSizeChangedLayoutNotifier({
    Key? key,
    Widget? child,
  }) : super(key: key, child: child);

  @override
  _RenderSizeChangedWithCallback createRenderObject(BuildContext context) {
    return _RenderSizeChangedWithCallback(
      onLayoutChangedCallback: () {
        SizeChangedLayoutNotification().dispatch(context);
      },
    );
  }
}

class _RenderSizeChangedWithCallback extends RenderProxyBox {
  _RenderSizeChangedWithCallback({
    RenderBox? child,
    required this.onLayoutChangedCallback,
  })  : assert(onLayoutChangedCallback != null),
        super(child);

  // There's a 1:1 relationship between the _RenderSizeChangedWithCallback and
  // the `context` that is captured by the closure created by createRenderObject
  // above to assign to onLayoutChangedCallback, and thus we know that the
  // onLayoutChangedCallback will never change nor need to change.

  final VoidCallback onLayoutChangedCallback;

  Size? _oldSize;

  @override
  void performLayout() {
    super.performLayout();
    // Don't send the initial notification, or this will be SizeObserver all
    // over again!
    if (size != _oldSize) onLayoutChangedCallback();
    _oldSize = size;
  }
}

?

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-11-20 18:30:36  更:2021-11-20 18:32:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 3:30:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码