我要在键入TextField widget
时设置文本格式。例如,当我在字段中使用下划线_
时,它们之间的字符应变为斜体。
我想要的是一种 markdown式文本格式,可以实时在 Flutter 的同一小部件中进行。
RichText可以提供帮助,但是它们不可编辑。为此,我需要一个RichTextField,但是Flutter中不存在类似的东西。
我的结果应该类似于WhatsApp的消息字段,该字段应用粗体,斜体,删除线等。
答案 0 :(得分:4)
我发现解决方案很简单。
我只需要扩展/实现TextEditingController
并创建TextSpan buildTextSpan({TextStyle style , bool withComposing})
的实现。
此buildTextSpan
方法负责创建TextField中显示的文本范围。
答案 1 :(得分:3)
请参阅有关在现有“ TextField”上实现答案的示例代码。
import 'dart:ui';
import 'package:flutter/material.dart';
final Color darkBlue = const Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class TextFieldColorizer extends TextEditingController {
final Map<String, TextStyle> map;
final Pattern pattern;
TextFieldColorizer(this.map)
: pattern = RegExp(
map.keys.map((key) {
return key;
}).join('|'),
multiLine: true);
@override
set text(String newText) {
value = value.copyWith(
text: newText,
selection: TextSelection.collapsed(offset: newText.length),
composing: TextRange.empty,
);
}
@override
TextSpan buildTextSpan({TextStyle style, bool withComposing}) {
final List<InlineSpan> children = [];
String patternMatched;
String formatText;
TextStyle myStyle;
text.splitMapJoin(
pattern,
onMatch: (Match match) {
myStyle = map[match[0]] ??
map[map.keys.firstWhere(
(e) {
bool ret = false;
RegExp(e).allMatches(text)
..forEach((element) {
if (element.group(0) == match[0]) {
patternMatched = e;
ret = true;
return true;
}
});
return ret;
},
)];
if (patternMatched == r"_(.*?)\_") {
formatText = match[0].replaceAll("_", " ");
} else if (patternMatched == r'\*(.*?)\*') {
formatText = match[0].replaceAll("*", " ");
} else if (patternMatched == "~(.*?)~") {
formatText = match[0].replaceAll("~", " ");
} else if (patternMatched == r'```(.*?)```') {
formatText = match[0].replaceAll("```", " ");
} else {
formatText = match[0];
}
children.add(TextSpan(
text: formatText,
style: style.merge(myStyle),
));
return "";
},
onNonMatch: (String text) {
children.add(TextSpan(text: text, style: style));
return "";
},
);
return TextSpan(style: style, children: children);
}
}
class MyWidget extends StatefulWidget {
const MyWidget();
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
final TextEditingController _controller = TextFieldColorizer(
{
r"@.\w+": TextStyle(color: Colors.blue, shadows: kElevationToShadow[2]),
'red': const TextStyle(
color: Colors.red, decoration: TextDecoration.underline),
'green': TextStyle(color: Colors.green, shadows: kElevationToShadow[2]),
'purple': TextStyle(color: Colors.purple, shadows: kElevationToShadow[2]),
r'_(.*?)\_': TextStyle(
fontStyle: FontStyle.italic, shadows: kElevationToShadow[2]),
'~(.*?)~': TextStyle(
decoration: TextDecoration.lineThrough,
shadows: kElevationToShadow[2]),
r'\*(.*?)\*': TextStyle(
fontWeight: FontWeight.bold, shadows: kElevationToShadow[2]),
r'```(.*?)```': TextStyle(
color: Colors.yellow,
fontFeatures: [const FontFeature.tabularFigures()],
shadows: kElevationToShadow[2]),
},
);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(10),
child: TextField(
maxLines: 5,
onChanged: (text) {
final val = TextSelection.collapsed(offset: _controller.text.length);
_controller.selection = val;
},
style: const TextStyle(fontSize: 32),
controller: _controller,
),
);
}
}