美文网首页
Flutter 水印效果

Flutter 水印效果

作者: 野生塔塔酱 | 来源:发表于2021-11-26 11:52 被阅读0次

    公司业务需要做可以超出屏幕的水印效果,但是网上找的一些都不符合条件,所以决定自己来做一个。

    因为flutter普通控件是不允许超出屏幕的,如果用listview这些布局会在屏幕内平铺无法超出屏幕,如果使用其他控件强行超出屏幕会报警告。

    所以转换思路,使用一个纵向的SingleChildScrollView来保证纵向可以超出屏幕,然后再在里面添加一行一行的横向SingleChildScrollView来保证横向可以超出屏幕。

    效果图.png
    /**
     * 水印样式
     * rowCount: 当前屏幕宽度中 展示多少列水印
     * columnCount: 当前屏幕高度中,展示多少行水印
     * text: 水印展示的文字
     * textStyle: 文字的样式
     */
    class DisableScreenshotsWaterMark extends StatelessWidget {
      final int rowCount; //水印需要展示的列数
      final int columnCount;//水印需要展示的行数
      final String text;//水印文字
      final TextStyle? textStyle;//水印文字样式
      final BuildContext parentContext;//父控件上下文
      final ScrollController? scrollController;//纵向scroll初始化控制器
      final bool layOutBottomNav;//是否需要考虑底部导航
    
      const DisableScreenshotsWaterMark(
        this.text,
        this.parentContext, {
        Key? key,
        required this.rowCount,
        required this.columnCount,
        this.textStyle,
        this.scrollController,
        this.layOutBottomNav = false,
      }) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return IgnorePointer(//忽略交互
          child: Container(
            child: LayoutBuilder(
              builder: (BuildContext context, BoxConstraints constraints) {
                return SingleChildScrollView(
                  scrollDirection: Axis.vertical,//最外层的一定是纵向的scroll
                  child: createColumnWidgets(context),
                  controller: scrollController ?? ScrollController(),
                  //根据有无外部传入进行初始化,可以解决想要初始纵向偏移量的问题
                  //展示图中就是传入了一个ScrollController(initialScrollOffset: 20),否则顶部会留白
                );
              },
            ),
          ),
        );
      }
    
      //创建每一行的widget
      List<Widget> createRowWidgets(BuildContext context) {
        //纵向水印最后一列宽度,根据需要自行更改
        final double lastWidth = 35.0;
        //纵向水印非最后一列的宽度,根据最后一列宽度自动计算 
        //这样计算是参照了网上说的精度丢失问题
        final double normalWidth =
            ((screenWidth(context) * 100).floor() - (lastWidth * 100).floor()) / 100 / (rowCount - 1);
    
        List<Widget> list = [];
        for (var i = 0; i < rowCount; i++) {
          //根据传入的列数,添加旋转效果的控件,实现某一行的效果
          final widget = Transform.rotate(
            angle: 611 / pi,//角度,根据需要自行修改
            child: Container(
              color: Colors.transparent,//注意要透明
              clipBehavior: Clip.none,
              padding: EdgeInsets.all(12),//每个水印widget内边距,根据需要调整
              alignment: Alignment.topLeft,
              width: normalWidth,
              child: Text(
                text,
                style: textStyle ??
                    TextStyle(
                      color: Color.fromRGBO(150, 150, 150, 0.25),
                      fontSize: 12,
                      decoration: TextDecoration.none,
                      fontWeight: FontWeight.w800,
                      height: 1.0,
                    ),
              ),
            ),
          );
    
          list.add(widget);
        }
        return list;
      }
    
      //创建每一列的widget
      Column createColumnWidgets(BuildContext context) {
        //最后一行高度,因为要保证最后一行有个显示一半的效果才故意加了一个这个高度,如果不需要该效果,可以不要这个高度,让每一行都等高,自然布局
        final double lastHeight = layOutBottomNav ? 40.0 : 0;
    
        MediaQueryData mediaQueryData = MediaQuery.of(this.parentContext);
        double topNavHeight = mediaQueryData.padding.top + 56;
        double bottomNavHeight = layOutBottomNav ? mediaQueryData.padding.bottom + 56 : 0;
        
        //水印显示区域高度
        double waterMarkAreaHeight = ((screenHeight(context) * 100).floor() -
                (topNavHeight * 100).floor() -
                (bottomNavHeight * 100).floor()) /
            100;
    
        //自动计算非最后一行高度
        double normalHeight =
            ((waterMarkAreaHeight * 100).floor() - (lastHeight * 100).floor()) / 100 / (columnCount - 1);
    
        List<Widget> list = [];
        //根据传入的行数创建每一行
        for (var i = 0; i < columnCount; i++) {
          final widget = Container(
            height: normalHeight,
            child: LayoutBuilder(
              builder: (BuildContext context, BoxConstraints constraints) {
                return SingleChildScrollView(
                  scrollDirection: Axis.horizontal,
                  child: Row(
                    children: createRowWidgets(context),
                  ),
                );
              },
            ),
          );
          list.add(widget);
        }
    
        Column column = Column(
          children: list,
        );
    
        return column;
      }
    }
    

    这套代码在iOS上没有问题,但是在某些小屏幕安卓机上,水印文本超过3行时可能会有文本被截断的问题,如遇到需要进行调整。

    使用的时候只需要在需要添加水印的widegt外部包一个stack然后把该水印widget添加到stack最后即可

    使用样例

    return Scaffold(
            appBar: _getAppBar(),
            body: Stack(
              children: [
                GestureDetector(
                  onTap: () {
                    
                  },
                  child: Container(),
                ),
                DisableScreenshotsWaterMark(
                        AppData.instance.account!.waterMarkStr,
                        context,
                        rowCount: 4,
                        columnCount: 10,
                        scrollController: ScrollController(initialScrollOffset: 20),
                        layOutBottomNav: true,
                      )
                    : Container(),
              ],
            ),
          );
    

    相关文章

      网友评论

          本文标题:Flutter 水印效果

          本文链接:https://www.haomeiwen.com/subject/gjubpltx.html