Flutter Text 设置文本渐变色
API是使用TextStyle中的foreground 设置文字颜色渐变,如下图:
https://api.flutter.dev/flutter/painting/TextStyle-class.html
头文件import 'dart:ui' as ui;
根据以上代码构造之后结果如下图行1所示,但是如果Text控件居中并缩短字符串长度后就会发现渐变颜色不同的,如下图行2所示。(最后有相关代码)
通过多次对比发现,渐变色是基于在整个window偏移的,代码中的offset 分别表示Offset from,Offset to 起点与终点偏移量,代码中const Offset(0, 20),const Offset(150, 20),表示从坐标(0,20),到(150,20,修改偏移量为const Offset(100, 20),const Offset(250, 20),结果图行3、4。
所以行2中Text居中后dx还是从0开始,颜色不是正红色开始,查看Gradient.linear,有一个colorStops属性,具体含义可查看相关注释,如下图
在偏移量超过其字符串最大dx的时候,设置colorStops,<double>[0.3,0.9],结果如结果图中的行5,通过调整colorStops,可以得到想要的渐变效果。
注意: 设置渐变色不能同时设置TextStyle的color属性,否则会报错,如下图:
设置渐变色还有另外一种写法:
Text( 'Greetings, planet!', style: TextStyle( fontSize: 30, foreground: Paint() ..shader = LinearGradient(colors: [ Colors.red, Colors.yellow, ]).createShader(Rect.fromLTWH(0, 0, 150, 0))), ) 或者 Gradient gradient = LinearGradient(colors: [ Colors.red, Colors.yellow, ]); Text( 'Greetings, planet!', style: TextStyle( fontSize: 30, foreground: Paint() ..shader = gradient.createShader(Rect.fromLTWH(0, 0, 100, 0))), )
createShader中的Rect和上面的偏移量类似,又不完全相同,Rect.fromLTWH(0, 0, 100, 0), width,表示渐变的宽度范围,结果如图行6。
这时候只是固定位置text设置渐变色,如果在不确定text坐标的时候如何使用渐变色
1、 设置Text的key值globalKey,通过globalKey 获取Text控件的size及position
final RenderBox box = globalKey.currentContext.findRenderObject();
final size = box.size;
final topLeftPosition = box.localToGlobal(Offset.zero);
2、如果设置的比较多可以封装组件widget,根据组件context获取位置大小,具体可查看以下的TextGradientColorWidget
验证相关代码:
import 'dart:ui' as ui; import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( // This is the theme of your application. // // Try running your application with "flutter run". You'll see the // application has a blue toolbar. Then, without quitting the app, try // changing the primarySwatch below to Colors.green and then invoke // "hot reload" (press "r" in the console where you ran "flutter run", // or simply save your changes to "hot reload" in a Flutter IDE). // Notice that the counter didn't reset back to zero; the application // is not restarted. primarySwatch: Colors.blue, // This makes the visual density adapt to the platform that you run // the app on. For desktop platforms, the controls will be smaller and // closer together (more dense) than on mobile platforms. visualDensity: VisualDensity.adaptivePlatformDensity, ), home: MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override void initState() { // TODO: implement initState super.initState(); } @override void didChangeDependencies() { // TODO: implement didChangeDependencies super.didChangeDependencies(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( // Here we take the value from the MyHomePage object that was created by // the App.build method, and use it to set our appbar title. title: Text(widget.title), ), body: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( margin: EdgeInsets.only(top: 20, bottom: 20), child: Text( '1.Greetings, planet!', style: TextStyle( fontSize: 30, foreground: Paint() ..shader = ui.Gradient.linear( const Offset(0, 20), const Offset(150, 20), <Color>[ Colors.red, Colors.yellow, ])), )), Container( margin: EdgeInsets.only(top: 20, bottom: 20), child: Center( child: Text( '2.Greetings!', style: TextStyle( fontSize: 30, foreground: Paint() ..shader = ui.Gradient.linear( const Offset(0, 20), const Offset(150, 20), <Color>[ Colors.red, Colors.yellow, ])), ))), Container( margin: EdgeInsets.only(top: 20, bottom: 20), child: Text( '3.Greetings, planet!', style: TextStyle( fontSize: 30, foreground: Paint() ..shader = ui.Gradient.linear(const Offset(100, 20), const Offset(250, 20), <Color>[ Colors.red, Colors.yellow, ])), )), Container( margin: EdgeInsets.only(top: 20, bottom: 20), child: Center( child: Text( '4.Greetings!', style: TextStyle( fontSize: 30, foreground: Paint() ..shader = ui.Gradient.linear( const Offset(100, 20), const Offset(250, 20), <Color>[ Colors.red, Colors.yellow, ])), )), ), Container( margin: EdgeInsets.only(top: 20, bottom: 20), child: Center( child: Text( '5.Greetings!', style: TextStyle( fontSize: 30, foreground: Paint() ..shader = ui.Gradient.linear(const Offset(100, 20), const Offset(250, 20), <Color>[ Colors.red, Colors.yellow, ], <double>[ 0.3, 0.9 ])), ))), Container( margin: EdgeInsets.only(top: 20, bottom: 20), child: Text( '6.Greetings, planet!', style: TextStyle( fontSize: 30, foreground: Paint() ..shader = LinearGradient(colors: [ Colors.red, Colors.yellow, ]).createShader(Rect.fromLTWH(0, 0, 150, 0))), )), Container( margin: EdgeInsets.only(top: 20, bottom: 20), child: Center( child: TextGradientColorWidget(data: "7.Greetings", colors: [ Colors.red, Colors.yellow, ]), ), ), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Center( child: TextGradientColorWidget(data: "Greetings", colors: [ Colors.red, Colors.yellow, ]), ), Center( child: TextGradientColorWidget(data: "Greetings", colors: [ Colors.red, Colors.yellow, ]), ) ], ) ], ) // This trailing comma makes auto-formatting nicer for build methods. ); } } //根据实际需求设置相关参数 class TextGradientColorWidget extends StatefulWidget { String data; TextStyle style; List<Color> colors; TextGradientColorWidget({Key key, this.data, this.colors}) : super(key: key); @override _TextGradientColorState createState() => _TextGradientColorState(); } class _TextGradientColorState extends State<TextGradientColorWidget> { WidgetsBinding widgetsBinding = WidgetsBinding.instance; @override void initState() { // TODO: implement initState super.initState(); if (widget.colors != null) { widgetsBinding.addPostFrameCallback((timeStamp) { final RenderBox box = context.findRenderObject(); var left = 0.0; var width = 0.0; if (box != null) { final topLeftPosition = box.localToGlobal(Offset.zero); final size = box.size; left = topLeftPosition.dx; width = size.width; setState(() { widget.style = TextStyle( fontSize: 30, foreground: Paint() ..shader = LinearGradient(colors: widget.colors, stops: [0.3, 0.8]) .createShader(Rect.fromLTWH(left, 0, width, 0))); }); //注意 TextStyle中color和foreground 中colors不能同时设置 } }); } } @override Widget build(BuildContext context) { // TODO: implement build return Text(widget.data, style: widget.style); } }