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