Flutter 開發實戰——自定義省略字尾

Eddiegooo發表於2021-05-10

直接說具體需求樣式吧

一行裡有三個widget,只有第一個widget內容確定,第二個widget內容由介面返回,返回什麼顯示什麼,第三個widget,位置也由第二個確定。只顯示一排,如果內容過多,widget2要省略以...可見結束。 畫個圖吧,清晰點,然後在給出一個具體樣子。

image.png 具體栗子: 這是一個提示語:widget1: 最大發布範圍 widget2:為介面返回釋出範圍名單,可能很長,也可能很短。當很長時,widget1、3都確定了位置,widget2儘可能的顯示多的內容,然後以"...可見"文案結束。 可能是下面這樣:

eg1:最大可見範圍:張三、李四、王五三人可見。 ✅ (widget2 內容很少時)

eg2:最大可見範圍:張三、李四、王五、趙...等26人可見。✅ (widget2 內容很多,超過了一行最大顯示時)

UED效果圖:

具體樣式圖.png

實現方式:
方式一:

Row-> widget1 -> Container(BoxConstraints 限定寬度約束)->widget2/widget3

Row(children: [
      Container(
        constraints: BoxConstraints(
            minWidth: margin,
            maxWidth: MediaQuery.of(context).size.width - margin),
        child: Text(
          str,
          style: titleStyle,
          maxLines: 1,
          overflow: TextOverflow.ellipsis,
        ),
      ),
      Text(
        endStr,
        style: StyleConstant.subtitleStyle,
      )
    ]);
複製程式碼
實現二:

使用系統的自帶的CustomMultiChildLayout,自定義一個widget 繼承自它

class MultipleChildWrapWidget extends CustomMultiChildLayout {
  MultipleChildWrapWidget({Key key, List<Widget> children})
      : super(
            key: key, children: children, delegate: MultiChildrenWidgetDelegate());
}


class MultiChildrenWidgetDelegate extends MultiChildLayoutDelegate {

  @override
  void performLayout(Size size) {
    // 一行裡剩餘的size
    var surplus = Size(size.width, size.height);
    // 第一個控制元件
    if (hasChild("former")) {
      var formerSize = layoutChild("former", BoxConstraints.loose(surplus));
      positionChild("former", Offset.zero);

      //更新剩餘size
      surplus = Size(surplus.width - formerSize.width, surplus.height);
    }

    // 第二個控制元件
    if (hasChild("middle") && hasChild("last")) {
      // 先計算最後一個控制元件
      var lastSize = layoutChild("last", BoxConstraints.loose(surplus));
      // 在計算中間控制元件
      var middleSize = layoutChild("middle", BoxConstraints.loose(Size(surplus.width - lastSize.width, surplus.height)));

      // 放置控制元件
      positionChild("middle", Offset(size.width - surplus.width, 0));
      positionChild("last", Offset(size.width - surplus.width + middleSize.width, 0));

    }
  }

複製程式碼

使用的時候只需將它作為一個widget即可,但要注意控制元件的id不要寫錯了,前後要對應上。 使用例子:

Container(
      child: MultipleChildWrapWidget(
        children: [
          LayoutId(id: "former", child: child), // 這個id 要和 裡面控制元件對應上
          LayoutId(id: "middle", child: child,),
          LayoutId(id: "last", child: child)
        ],
      ),
    );
複製程式碼

同樣類似的需求

彈窗提示,最多展示兩行文字,超過兩行以"...可見"結束。

image.png

實現方式

最開始想用偷雞方式實現來著,讓文字就顯示兩行,然後第二行底部用"...可見"Text覆蓋上去。 很快三下五初二幹完了,搞定, 哈哈? 理想很豐滿,現實很果敢啊。 一執行發現,後面直接蓋一個固定的widget會有問題,可能壓住了半邊字,你懂的哈。

不行那就換一種吧。後來又想兩行分別顯示,第二行用兩個widget組合來顯示。 但是問題又來了,怎麼確定每行顯示多少文字呢?在哪個文字那裡斷開呢?? 這樣就得一個一個字的繪製出來,太麻煩,不想這麼幹。 程式猿都是懶人嘛,我要找簡便的方法。

既然文字超過固定的行數,會以"..."結束,那我能不能自定義讓其以"...可見"結束呢?? 這不就實現了我的要求了麼!! 嗯,這樣很好。 於是就去看省略樣式設定overflow: TextOverflow.ellipsis。 然而TextWidget系統給出的省略樣式就那幾種,還不能自定義。

不行,我不幹,既然你這能給出那幾種省略樣式,就可以設定別的樣式。 我繼續找,你Text不行,我找其他的東西。那我自己畫的方式呢,TextPainter;

  TextPainter({
    InlineSpan? text,
    TextAlign textAlign = TextAlign.start,
    TextDirection? textDirection,
    double textScaleFactor = 1.0,
    int? maxLines,
    String? ellipsis,
    Locale? locale,
    StrutStyle? strutStyle,
    TextWidthBasis textWidthBasis = TextWidthBasis.parent,
    ui.TextHeightBehavior? textHeightBehavior,
  })
複製程式碼

誒,這裡可以把ellipsis 傳進來,那我就用我自己希望的String當結尾就好了啊。 這個貌似可以。 嘿嘿 就是你了。

自定義一個widget繼承自CustomPainter, 實現對應的協議即可。

class CustomTextPaintWidget extends CustomPainter {
  final String text;
  final String endStr;
  final TextStyle textStyle;
  final int maxLines;

  TextPainter _textPainter;

  CustomTextPaintWidget({this.text, this.endStr,this.textStyle, this.maxLines}) {
    _textPainter = TextPainter(
        text: TextSpan(text: text, style: textStyle),
        maxLines: maxLines,
        ellipsis: endStr,
        textDirection: TextDirection.ltr);
  }

  @override
  void paint(Canvas canvas, Size size) {
    _textPainter.layout(maxWidth: size.width);
    _textPainter.paint(canvas, Offset(0, 0));
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }

}
複製程式碼

使用方式如下,就是一個widget。 這個也可以解決上面那個問題。

Widget CustomPaint(
      size: Size(MediaQuery.of(context).size.width - margin, 20),
      painter: CustomTextPaintWidget(
          text: str,
          endStr: endStr,
          textStyle: TextStyle,
          maxLines: 2),
    );
複製程式碼

完美 搞定。?

其實需求很簡單,不要急,先想好在幹,總會有更優的方式。

如果大佬有更好的實現樣式,還望不吝賜教,小弟在此先感謝了[抱拳].

相關文章