Flutter实现类似朋友圈文字显示控件功能:1.文字较少时,直接显示原文本。2.文字超过指定的最大行数时,默认只显示这几行文本,后面...结尾,并且在文本下方有一个“全文”展开按钮;点击“全文”按钮后文本全部显示完整,按钮变成“收起”;点击“收起”按后又恢复成只显示部分行数内容。
测试页面效果图如下:
实现原理:
使用LayoutBuilder控件,实现控件延迟加载。先用TextPainter判断文本内容是否超过指定的行数(例如3行),如果未超过,直接以普通的Text控件显示;如果超过,则显示一个Column控件,其内容分别为Text文本控件和展开/收起按钮控件,并根据展开状态决定Text控件的maxLines属性,以及按钮的文本。
源码:
1.测试页面源码:
import 'expandable_text.dart';
import 'package:flutter/material.dart';
class ExpandableTextPage extends StatefulWidget {
static void show(BuildContext context) {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return ExpandableTextPage();
}));
}
@override
State<StatefulWidget> createState() {
return _ExpandableTextPageState();
}
}
class _ExpandableTextPageState extends State<ExpandableTextPage> {
@override
Widget build(BuildContext context) {
String shortText = '不超过最大行数三行的多行文本不超过最大行数三行的多行文本';
String longText = '超过最大行数三行的多行文本超过最大行数三行的多行文本超过最大行数三行的多行文本'
'超过最大行数三行的多行文本超过最大行数三行的多行文本超过最大行数三行的多行文本超过最大行数三行的多行文本';
return Scaffold(
appBar: AppBar(
title: Text('仿朋友圈多行文字展开收起'),
),
body: Container(
padding: EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('短文本测试:'),
Container(
color: Colors.yellow,
child: ExpandableText(text: shortText, maxLines: 3, style: TextStyle(fontSize: 15, color: Colors.black),),
),
Padding(padding: EdgeInsets.only(top: 20),),
Text('长文本测试:'),
Container(
color: Colors.yellow,
child: ExpandableText(text: longText, maxLines: 3, style: TextStyle(fontSize: 15, color: Colors.black),),
),
],
),
),
);
}
}
2.封装的功能控件源码:
import 'package:flutter/material.dart';
class ExpandableText extends StatefulWidget {
final String text;
final int maxLines;
final TextStyle style;
final bool expand;
const ExpandableText({Key key, this.text, this.maxLines, this.style, this.expand}) : super(key: key);
@override
State<StatefulWidget> createState() {
return _ExpandableTextState(text, maxLines, style, expand);
}
}
class _ExpandableTextState extends State<ExpandableText> {
final String text;
final int maxLines;
final TextStyle style;
bool expand;
_ExpandableTextState(this.text, this.maxLines, this.style, this.expand) {
if (expand == null) {
expand = false;
}
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, size) {
final span = TextSpan(text: text ?? '', style: style);
final tp = TextPainter(
text: span, maxLines: maxLines, textDirection: TextDirection.ltr);
tp.layout(maxWidth: size.maxWidth);
if (tp.didExceedMaxLines) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
expand ?
Text(text ?? '', style: style) :
Text(text ?? '', maxLines: maxLines,
overflow: TextOverflow.ellipsis,
style: style),
GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {
setState(() {
expand = !expand;
});
},
child: Container(
padding: EdgeInsets.only(top: 2),
child: Text(expand ? '收起' : '全文', style: TextStyle(
fontSize: style != null ? style.fontSize : null,
color: Colors.blue)),
),
),
],
);
} else {
return Text(text ?? '', style: style);
}
});
}
}