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);
}
});
}
}