Teknologian jakaminen

Flutterin mukautettu kapselointi toteuttaa liitoksen tekstin lopussa... laajennettu/tiivistetty ExpandableText

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Esipuhe

Olen viime aikoina kirjoittanut Flutteria sopeutuakseni ristipäätteisiin ja täydentänyt yhteistä koodisarjaa Androidille, iOS:lle ja HarmonyOS:lle työvoiman ylläpitokustannusten vähentämiseksi Android-natiivi, kun teksti ylittää enimmäismäärän rivejä , katkaise loput ja liitä laajenna/kutista. En tuntenut sitä aluksi, joten käytin ensin Stackia dynaamisesti ohjaamaan maxLinesia ja ylivuotoa, ja laitoin maskin ja tekstin oikeaan alakulmaan sen saavuttamiseksi. Myöhemmin huomasin, että joissain näyttöpuhelimissa on kiusallinen tilanne tekstistä on peitetty ja puolet sanasta vuotaa ulos , tänään minulla on vihdoin aikaa optimoida toteutussuunnitelma.

Toteuta koodi

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

class ExpandableText extends StatefulWidget {
  final String text;
  final int maxLines;
  final TextStyle textStyle;
  final String linkTextExpand;
  final String linkTextCollapse;
  final Color linkTextColor;

  const ExpandableText(
      {super.key,
      required this.text,
      this.maxLines = 2,
      this.textStyle = const TextStyle(color: Colors.black),
      this.linkTextColor = Colors.blue,
      this.linkTextExpand = " 展开",
      this.linkTextCollapse = " 收起"});

  
  State<StatefulWidget> createState() => _ExpandableTextState();
}

class _ExpandableTextState extends State<ExpandableText> {
  bool isExpanded = false;
  late TextSpan expandSpan;
  late TextSpan linkTextSpan;

  
  void initState() {
    super.initState();
    expandSpan = TextSpan(
      text: widget.linkTextExpand,
      style: TextStyle(color: widget.linkTextColor),
      recognizer: TapGestureRecognizer()
        ..onTap = () {
          setState(() {
            isExpanded = !isExpanded;
          });
        },
    );

    linkTextSpan = TextSpan(
      text: '...',
      style: widget.textStyle,
      children: [expandSpan],
    );
  }

  /// 检查文本是否溢出
  bool checkOverflow(double width) {
    final textPainter = TextPainter(
      text: TextSpan(text: widget.text, style: widget.textStyle),
      textDirection: TextDirection.ltr,
    );
    textPainter.layout(maxWidth: width);
    return textPainter.height > widget.maxLines * textPainter.preferredLineHeight;
  }

  
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (BuildContext context, BoxConstraints constraints) {
        final maxWidth = constraints.maxWidth;
        final textSpan = TextSpan(
          text: widget.text,
          style: widget.textStyle,
        );

        final textPainter = TextPainter(
          text: textSpan,
          maxLines: isExpanded ? null : widget.maxLines,
          textDirection: TextDirection.ltr,
        );
        textPainter.layout(maxWidth: maxWidth);

        return Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            isExpanded ? _buildExpandedText() : _buildCollapsedText(textPainter, maxWidth),
          ],
        );
      },
    );
  }

  /// 构建折叠状态下的文本
  Widget _buildCollapsedText(TextPainter textPainter, double maxWidth) {
    var truncatedText = widget.text;
    if (checkOverflow(maxWidth)) {
      // 文本的长度超过了最大行数,需要截取然后拼接展开部分的逻辑
      final linkPainter = TextPainter(
        text: linkTextSpan,
        textDirection: TextDirection.ltr,
      );
      linkPainter.layout(maxWidth: maxWidth);
      final position = textPainter.getPositionForOffset(Offset(maxWidth - linkPainter.width, textPainter.height));
      final endOffset = textPainter.getOffsetBefore(position.offset) ?? position.offset;
      truncatedText = widget.text.substring(0, endOffset);
    }

    return RichText(
      text: TextSpan(
        text: truncatedText,
        style: widget.textStyle,
        children: widget.text.length == truncatedText.length ? [] : [linkTextSpan],
      ),
      maxLines: widget.maxLines,
      overflow: TextOverflow.ellipsis,
    );
  }

  /// 构建展开状态下的文本
  Widget _buildExpandedText() {
    final collapseSpan = TextSpan(
      text: widget.linkTextCollapse,
      style: TextStyle(color: widget.linkTextColor),
      recognizer: TapGestureRecognizer()
        ..onTap = () {
          setState(() {
            isExpanded = !isExpanded;
          });
        },
    );

    return RichText(
      text: TextSpan(
        text: widget.text,
        style: widget.textStyle,
        children: [collapseSpan],
      ),
    );
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135

Toiminnallinen yleiskatsaus

ExpandableText on mukautettu Flutter-widget, jota käytetään pitkän tekstisisällön näyttämiseen. Kun tekstin pituus ylittää ennalta määritetyn rivien enimmäismäärän, teksti tiivistetään ja näkyviin tulee "laajenna"-linkki.

Tärkeimmät toteutuskohdat

1. Tilanhallinta:

Käytä StatefulWidgetiä ja _ExpandableTextStatea laajennetun/tiivistetyn tekstin tilan hallintaan.
IsExpanded-muuttuja ohjaa tekstin näyttötilaa.

2. Tekstinkäsittely:

Käytä TextPainter- ja RichText-komponentteja tekstin mittaamiseen ja hahmontamiseen.
Kun teksti ylittää enimmäisrivien lukumäärän, varmista laskemalla ja sieppaamalla teksti, että teksti näkyy oikein tiivistetyssä tilassa, ja lisää "laajenna"-linkki.

3. Vuorovaikutuslogiikka:

Lisää napsautustapahtumien seuranta TapGestureRecognizerin kautta toteuttaaksesi laajennus- ja tiivistystoiminnot.
SetState-menetelmää käytetään tekstin näytön tilan päivittämiseen.

4. Responsiivinen asettelu:

Käytä LayoutBuilderiä saadaksesi käytettävissä olevat leveydet eri näyttökokoihin ja asetteluympäristöihin sopivaksi.
CheckOverflow-menetelmää käytetään määrittämään, ylittääkö teksti asetetun enimmäismäärän rivejä.

5. Tyylin mukauttaminen:

Tarjoaa useita konfiguroitavia parametreja, kuten maxLines, textStyle, linkTextExpand, linkTextCollapse, linkTextColor, joiden avulla käyttäjät voivat muokata tekstin ulkonäköä ja käyttäytymistä.

6. Kuinka käyttää

ExpandableText(
  text: "这是一段很长的文本,它将被折叠起来,只有在点击'展开'链接后才会完全显示。",
  maxLines: 2,
  textStyle: TextStyle(fontSize: 16, color: Colors.black),
  linkTextExpand: " 展开",
  linkTextCollapse: " 收起",
  linkTextColor: Colors.blue,
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Varotoimenpiteet

Varmista ExpandableTextiä käytettäessä, että annat riittävän kontekstin leveyden, jotta tekstin taittopiste lasketaan oikein.
Mukautetut tyyliparametrit tulee säätää todellisten tarpeiden mukaan parhaan visuaalisen vaikutelman saavuttamiseksi.