Flutter: How to hide or show more text within certain length

前端 未结 7 928
小鲜肉
小鲜肉 2020-11-29 07:00

My Container having a description about movies.

Initially i want to show only few lines of description. And below to that there should be a link (more...

相关标签:
7条回答
  • 2020-11-29 07:08

    Try this.

    Watch the output from here ( Video )

    or refer below images

    import 'package:flutter/material.dart';
    
    class DemoPage extends StatefulWidget {
      final Widget child;
    
      DemoPage({Key key, this.child}) : super(key: key);
    
      _DemoPageState createState() => _DemoPageState();
    }
    
    class _DemoPageState extends State<DemoPage> {
    
    String descText = "Description Line 1\nDescription Line 2\nDescription Line 3\nDescription Line 4\nDescription Line 5\nDescription Line 6\nDescription Line 7\nDescription Line 8";
    bool descTextShowFlag = false;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("DemoPage"),
          ),
          body: new Container(
            margin: EdgeInsets.all(16.0),
            child: new Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                  Text(descText,
                    maxLines: descTextShowFlag ? 8 : 2,textAlign: TextAlign.start),
                  InkWell(
                    onTap: (){ setState(() {
                    descTextShowFlag = !descTextShowFlag; 
                    }); },
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.end,
                      children: <Widget>[
                        descTextShowFlag ? Text("Show Less",style: TextStyle(color: Colors.blue),) :  Text("Show More",style: TextStyle(color: Colors.blue))
                      ],
                    ),
                  ),
              ],
            ),
          ),
        );
      }
    }
    
    0 讨论(0)
  • 2020-11-29 07:10

    Please use package flutter-expandable.
    This package can produce an effect of expanding an image or text.
    https://github.com/aryzhov/flutter-expandable

    import 'package:expandable/expandable.dart';
    ...
    ExpandablePanel(
            header: Text( content,
              maxLines: 2,
              style: Theme.of(context).textTheme.body2,
            ),
            expanded: Align(
                alignment: Alignment.centerLeft,
                child: Text(
                  content,
                  softWrap: true,
                )),
            tapHeaderToExpand: true,
            hasIcon: true,
          ),
    
    0 讨论(0)
  • 2020-11-29 07:11

    To get Exact Behavior like JQuery plugin. you need to use TextPainter and calculate the hight on given Width.

    Expanded State

    Normal State

    import 'package:flutter/gestures.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';
    import 'package:flutter/widgets.dart';
    
    void main() => runApp(MaterialApp( home: DemoApp()));
    
    class DemoApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
              title: Text(
                'Read More Text',
              )),
          body:  SingleChildScrollView(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: ExpandableText(
                      'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque scelerisque efficitur posuere. Curabitur tincidunt placerat diam ac efficitur. Cras rutrum egestas nisl vitae pulvinar. Donec id mollis diam, id hendrerit neque. Donec accumsan efficitur libero, vitae feugiat odio fringilla ac. Aliquam a turpis bibendum, varius erat dictum, feugiat libero. Nam et dignissim nibh. Morbi elementum varius elit, at dignissim ex accumsan a',
                       trimLines: 2,
                    ),
                  ),
                ],
              ),
            ),
        );
      }
    }
    class ExpandableText extends StatefulWidget {
      const ExpandableText(
          this.text, {
            Key key,
            this.trimLines = 2,
          })  : assert(text != null),
            super(key: key);
    
      final String text;
      final int trimLines;
    
      @override
      ExpandableTextState createState() => ExpandableTextState();
    }
    
    class ExpandableTextState extends State<ExpandableText> {
      bool _readMore = true;
      void _onTapLink() {
        setState(() => _readMore = !_readMore);
      }
    
      @override
      Widget build(BuildContext context) {
        final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context);
        final colorClickableText = Colors.blue;
        final widgetColor = Colors.black;
        TextSpan link = TextSpan(
          text: _readMore ? "... read more" : " read less",
          style: TextStyle(
            color: colorClickableText,
          ),
          recognizer: TapGestureRecognizer()..onTap = _onTapLink
        );
        Widget result = LayoutBuilder(
          builder: (BuildContext context, BoxConstraints constraints) {
            assert(constraints.hasBoundedWidth);
            final double maxWidth = constraints.maxWidth;
            // Create a TextSpan with data
            final text = TextSpan(
              text: widget.text,
            );
            // Layout and measure link
            TextPainter textPainter = TextPainter(
              text: link,
              textDirection: TextDirection.rtl,//better to pass this from master widget if ltr and rtl both supported
              maxLines: widget.trimLines,
              ellipsis: '...',
            );
            textPainter.layout(minWidth: constraints.minWidth, maxWidth: maxWidth);
            final linkSize = textPainter.size;
            // Layout and measure text
            textPainter.text = text;
            textPainter.layout(minWidth: constraints.minWidth, maxWidth: maxWidth);
            final textSize = textPainter.size;
            // Get the endIndex of data
            int endIndex;
            final pos = textPainter.getPositionForOffset(Offset(
                textSize.width - linkSize.width,
                textSize.height,
            ));
            endIndex = textPainter.getOffsetBefore(pos.offset);
            var textSpan;
            if (textPainter.didExceedMaxLines) {
              textSpan = TextSpan(
                text: _readMore
                    ? widget.text.substring(0, endIndex)
                    : widget.text,
                style: TextStyle(
                  color: widgetColor,
                ),
                children: <TextSpan>[link],
              );
            } else {
              textSpan = TextSpan(
                text: widget.text,
              );
            }
            return RichText(
              softWrap: true,
              overflow: TextOverflow.clip,
              text: textSpan,
            );
          },
        );
        return result;
      }
    }
    
    0 讨论(0)
  • 2020-11-29 07:16

    ExpandableText with regrex validation as well.

    import 'package:flutter/material.dart';
    import 'package:url_launcher/url_launcher.dart';
    
    class ExpandableText extends StatefulWidget {
      ExpandableText(this.text);
    
      final String text;
      // bool isExpanded = false;
    
      @override
      _ExpandableTextState createState() => new _ExpandableTextState();
    }
    
    class _ExpandableTextState extends State<ExpandableText> {
      String text;
      bool canExpand = false;
      bool isExpand = false;
    
      @override
      Widget build(BuildContext context) {
        //
        canExpand = widget.text != null && widget.text.length >= 150;
        text = canExpand
            ? (isExpand ? widget.text : widget.text.substring(0, 150))
            : (widget.text);
    
        return canExpand
            ? Column(
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  buildTextWithLinks(text.trim()),
                  GestureDetector(
                    onTap: () {
                      setState(() {
                        isExpand = !isExpand;
                      });
                    },
                    child: Padding(
                      padding: const EdgeInsets.symmetric(vertical: 4.0),
                      child: Text(isExpand ? ' ... show less' : ' ... show more'
                      ),
                    ),
                  ),
                ],
              )
            : Text(text != null ? text : "");
      }
    }
    
    Text buildTextWithLinks(String textToLink, {String text}) =>
        Text.rich(TextSpan(children: linkify(textToLink)));
    
    Future<void> openUrl(String url) async {
      if (await canLaunch(url)) {
        await launch(url);
      } else {
        throw 'Could not launch $url';
      }
    }
    
    const String urlPattern =
        r"(https?|http)://([-A-Z0-9.]+)(/[-A-Z0-9+&@#/%=~_|!:,.;]*)?(\?[A-Z0-9+&@#/%=~_|!:‌​,.;]*)?";
    const String emailPattern = r'\S+@\S+';
    const String phonePattern = r'[\d-]{9,}';
    final RegExp linkRegExp = RegExp(
        '($urlPattern)|($emailPattern)|($phonePattern)',
        caseSensitive: false);
    
    WidgetSpan buildLinkComponent(String text, String linkToOpen) => WidgetSpan(
            child: InkWell(
          child: Text(
            text,
            style: TextStyle(
              color: Colors.red,
              decoration: TextDecoration.underline,
            ),
          ),
          onTap: () => openUrl(linkToOpen),
        ));
    
    List<InlineSpan> linkify(String text) {
      final List<InlineSpan> list = <InlineSpan>[];
      final RegExpMatch match = linkRegExp.firstMatch(text);
      if (match == null) {
        list.add(TextSpan(text: text));
        return list;
      }
    
      if (match.start > 0) {
        list.add(TextSpan(text: text.substring(0, match.start)));
      }
    
      final String linkText = match.group(0);
      if (linkText.contains(RegExp(urlPattern, caseSensitive: false))) {
        list.add(buildLinkComponent(linkText, linkText));
      } else if (linkText.contains(RegExp(emailPattern, caseSensitive: false))) {
        list.add(buildLinkComponent(linkText, 'mailto:$linkText'));
      } else if (linkText.contains(RegExp(phonePattern, caseSensitive: false))) {
        list.add(buildLinkComponent(linkText, 'tel:$linkText'));
      } else {
        throw 'Unexpected match: $linkText';
      }
    
      list.addAll(linkify(text.substring(match.start + linkText.length)));
    
      return list;
    }
    
    
    0 讨论(0)
  • 2020-11-29 07:21

    A simple example

    class ExpandableText extends StatefulWidget {
      ExpandableText(this.text);
    
      final String text;
      bool isExpanded = false;
    
      @override
      _ExpandableTextState createState() => new _ExpandableTextState();
    }
    
    class _ExpandableTextState extends State<ExpandableText> {
      @override
      Widget build(BuildContext context) {
        return new Column(children: <Widget>[
          new ConstrainedBox(
              constraints: widget.isExpanded
                  ? new BoxConstraints()
                  : new BoxConstraints(maxHeight: 50.0),
              child: new Text(
                widget.text,
                softWrap: true,
                overflow: TextOverflow.fade,
              )),
          widget.isExpanded
              ? new Container()
              : new FlatButton(
                  child: const Text('...'),
                  onPressed: () => setState(() => widget.isExpanded = true))
        ]);
      }
    }
    

    with animation

    class ExpandableText extends StatefulWidget {
      ExpandableText(this.text);
    
      final String text;
      bool isExpanded = false;
    
      @override
      _ExpandableTextState createState() => new _ExpandableTextState();
    }
    
    class _ExpandableTextState extends State<ExpandableText>
        with TickerProviderStateMixin<ExpandableText> {
      @override
      Widget build(BuildContext context) {
        return new Column(children: <Widget>[
          new AnimatedSize(
              vsync: this,
              duration: const Duration(milliseconds: 500),
              child: new ConstrainedBox(
                  constraints: widget.isExpanded
                      ? new BoxConstraints()
                      : new BoxConstraints(maxHeight: 50.0),
                  child: new Text(
                    widget.text,
                    softWrap: true,
                    overflow: TextOverflow.fade,
                  ))),
          widget.isExpanded
              ? new ConstrainedBox(constraints: new BoxConstraints())
              : new FlatButton(
              child: const Text('...'),
              onPressed: () => setState(() => widget.isExpanded = true))
        ]);
      }
    }
    
    0 讨论(0)
  • 2020-11-29 07:24

    you can do that this way

    import 'package:flutter/material.dart';
    import 'package:meta/meta.dart';
    
    void main() => runApp(new MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          title: 'Flutter Demo',
          home: new HomeScreen(),
        );
      }
    }
    
    class HomeScreen extends StatelessWidget {
      final String description =
          "Flutter is Google’s mobile UI framework for crafting high-quality native interfaces on iOS and Android in record time. Flutter works with existing code, is used by developers and organizations around the world, and is free and open source.";
    
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(
            title: const Text("Demo App"),
          ),
          body: new Container(
            child: new DescriptionTextWidget(text: description),
          ),
        );
      }
    }
    
    class DescriptionTextWidget extends StatefulWidget {
      final String text;
    
      DescriptionTextWidget({@required this.text});
    
      @override
      _DescriptionTextWidgetState createState() => new _DescriptionTextWidgetState();
    }
    
    class _DescriptionTextWidgetState extends State<DescriptionTextWidget> {
      String firstHalf;
      String secondHalf;
    
      bool flag = true;
    
      @override
      void initState() {
        super.initState();
    
        if (widget.text.length > 50) {
          firstHalf = widget.text.substring(0, 50);
          secondHalf = widget.text.substring(50, widget.text.length);
        } else {
          firstHalf = widget.text;
          secondHalf = "";
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return new Container(
          padding: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
          child: secondHalf.isEmpty
              ? new Text(firstHalf)
              : new Column(
                  children: <Widget>[
                    new Text(flag ? (firstHalf + "...") : (firstHalf + secondHalf)),
                    new InkWell(
                      child: new Row(
                        mainAxisAlignment: MainAxisAlignment.end,
                        children: <Widget>[
                          new Text(
                            flag ? "show more" : "show less",
                            style: new TextStyle(color: Colors.blue),
                          ),
                        ],
                      ),
                      onTap: () {
                        setState(() {
                          flag = !flag;
                        });
                      },
                    ),
                  ],
                ),
        );
      }
    }
    
    0 讨论(0)
提交回复
热议问题