FlutterのLinearGradientで終点にColors.transparentを置くと、白から透明へ抜けるはずのグラデーションが途中で灰色に沈みます。実装ミスではありません。Colors.transparentは「透明な白」ではなく透明な黒として定義されているためです。Flutter 3.47.9(2026年10月29日のstable)で描画結果のピクセルを読み戻した値をもとに、原因と書き方、角度指定・withOpacity・ボタン・AppBar・アニメーションでの注意点を示します。
まとめ
透明グラデーションが濁る原因は、Colors.transparentがColor(0x00000000)(RGBが黒のまま不透明度0)であることです。Colors.white.withValues(alpha: 0)かColor(0x00FFFFFF)で書けば直ります。ShaderMaskでBlendMode.dstInまたはBlendMode.dstOutを使う場合は、マスクの不透明度だけが参照されるため、RGBが違っても同じ結果です。
そのほかに引っかかりやすい点は3つあります。
transform: GradientRotation(...)は矩形の中心でグラデーションを回すだけで、長さは変わりません。既定のbegin・endを使い、幅400px・高さ100pxの矩形で90度回すと、色幅の中央25%だけが画面に出ます。縦や斜めはbegin・endで指定するのが基本です。LinearGradient.withOpacity(0.5)は各色の不透明度を0.5に上書きします。不透明度によるフェードが消え、白から透明な白へのグラデーションなら一色になります。このメソッド自体にanalyzerの非推奨警告は出ません。AnimatedContainerでLinearGradientからRadialGradientへ切り替えると、補間の中間点(t=0.5)でグラデーションが完全に透明になり、背景が見えます。同じ型どうしなら色とstopsは補間されます。ただし、GradientRotationなどのtransformは中間点で切り替わり、回転角度そのものは滑らかに補間されません。
LinearGradientの基本指定(begin・end・stops・tileMode)
begin・endで決まる方向と横長の矩形での実際の角度
LinearGradientはウィジェットではなく、BoxDecorationのgradientに渡す描画指定です。必須はcolorsだけで、方向はbeginとendにAlignmentを渡して決めます。既定値はbegin: Alignment.centerLeft、end: Alignment.centerRightの左から右です(LinearGradientのAPIリファレンス)。
Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: <Color>[Color(0xFF2196F3), Color(0xFF9C27B0)],
),
),
)
Alignmentでは矩形の左右端・上下端がそれぞれ-1.0と1.0に対応するので、topLeftからbottomRightへの指定は正方形のときだけ45度になります。幅400px・高さ100pxの矩形では、色が変化する向きは対角線と同じ水平から約14度です。黒から白へ塗ると右上の角は240、左下の角は15で、色はほぼ左右方向に変わります。その代わり、左上は0、右下は254と、指定した2色がどちらも画面に出ます。
stopsの件数不一致で出る実行時エラー
stopsを省略すると色は等間隔に並びます。配分を変える場合は0.0〜1.0の位置を指定しますが、要素数をcolorsと一致させる必要があります。
const LinearGradient(
colors: <Color>[Colors.red, Colors.yellow, Colors.green],
stops: <double>[0.0, 0.2, 1.0],
)
件数がずれてもコンパイルは通ります。Flutter 3.47.9で色3件・stops2件のままcreateShaderを呼ぶと、実行時に次の例外が出ました。色を後から1つ足したときに起きやすいエラーです。
Invalid argument(s): "colors" and "colorStops" arguments must have equal length.
tileModeで決まる描画範囲外の扱い
tileModeは、beginからendまでの範囲より外側をどう塗るかの指定です。値はclamp(既定・端の色を引き延ばす)、repeated(繰り返す)、mirror(反転しながら繰り返す)、decal(範囲外を透明にする)の4種類です。beginとendが矩形の端にある既定の使い方では違いが出ません。end: Alignment(-0.6, 0)のように範囲を短くしてrepeatedを指定すると、ストライプ状になります。
GradientRotationによる角度指定と色幅が欠ける条件
角度を数値で指定したい場合はtransformにGradientRotation(ラジアン)を渡します。度で考えるなら度 * math.pi / 180で変換します。
import 'dart:math' as math;
const LinearGradient diagonal = LinearGradient(
colors: <Color>[Colors.black, Colors.white],
transform: GradientRotation(math.pi / 4), // 時計回りに45度
);
注意点は、GradientRotationが「外接矩形の中心まわりにグラデーションを回す」だけの変換であることです(gradient.dartのGradientRotation定義)。グラデーションの長さは回転前のbeginからendまで、既定なら矩形の幅のままです。横長の矩形で90度回すと、幅400pxぶんのグラデーションを高さ100pxの窓から覗く形になります。
幅400px・高さ100pxで黒(0)から白(255)を塗り、上端中央(x=200、y=0)と下端中央(x=200、y=99)、左上・右下の画素値を読み戻した結果が次の表です。
| 指定 | 上端 | 下端 | 左上の角 | 右下の角 |
|---|---|---|---|---|
| topCenter → bottomCenter | 1 | 254 | 1 | 254 |
| GradientRotation(math.pi / 2) | 95 | 159 | 95 | 159 |
| topLeft → bottomRight | 120 | 135 | 0 | 254 |
| GradientRotation(math.pi / 4) | 105 | 150 | 15 | 240 |
90度回転では上端95・下端159で、黒から白の中央25%しか出ていません。400pxのグラデーションの中央100pxだけが見えている計算と一致します。縦方向が欲しいなら回転ではなくbegin: Alignment.topCenter・end: Alignment.bottomCenterと書いてください。斜めは、両端の色を必ず出したいならbegin・endを角に置き、複数のカードで傾きを揃えたいときだけGradientRotationを使います。後者はstopsを内側へ寄せて、見える範囲に色を収めます。
透明色の書き方とwithOpacityの非推奨(Flutter 3.27以降)
withValues(alpha:)への移行とanalyzerの警告
Flutter 3.27でColorの内部表現が8bit整数から浮動小数点に変わり、広色域(ColorSpace.displayP3など)に対応しました。8bitを前提にしたAPIはこのとき非推奨になっています。3.47.9のソースでもwithOpacityには@Deprecated('Use .withValues() to avoid precision loss.')が付いたままです(dart:uiのpainting.dart)。公式の移行ガイドでは、旧APIを削除するPhase 2の時期が「Not yet」のままです。
非推奨APIを4種類含む検証用ファイル(lib/snippets.dart)にflutter analyzeをかけると、次のinfoが出ます。行番号と列番号はそのファイルでの位置です。
info • 'withOpacity' is deprecated and shouldn't be used. Use .withValues() to avoid precision loss. Try replacing the use of the deprecated member with the replacement • lib/snippets.dart:31:32 • deprecated_member_use
info • 'opacity' is deprecated and shouldn't be used. Use .a. Try replacing the use of the deprecated member with the replacement • lib/snippets.dart:32:57 • deprecated_member_use
info • 'value' is deprecated and shouldn't be used. Use component accessors like .r or .g, or toARGB32 for an explicit conversion. Try replacing the use of the deprecated member with the replacement • lib/snippets.dart:33:35 • deprecated_member_use
info • 'red' is deprecated and shouldn't be used. Use (*.r * 255.0).round().clamp(0, 255). Try replacing the use of the deprecated member with the replacement • lib/snippets.dart:34:33 • deprecated_member_use
| 旧API(3.27で非推奨) | 新API | 戻り値の型 |
|---|---|---|
| color.withOpacity(0.5) | color.withValues(alpha: 0.5) | Color |
| color.opacity | color.a | double(0.0〜1.0) |
| color.value | color.toARGB32() | int |
| color.red | (color.r * 255.0).round() | int |
警告はinfoなのでビルドは止まりません。移行は戻り値がColorのまま変わらないwithOpacityから始めてください。.aも旧opacityも戻り値はdoubleです。ただし、旧APIは8bitへ丸めた不透明度を返すため、移行すると等値比較やMapのキーの一致結果が変わる場合があります。Flutter本体の版ごとの変更点はFlutter 3.47とは?material_ui移行・iOS 15と3.47.7までの修正【2026年10月時点】にまとめています。
ARGBリテラルで透明を書く場合の0x00FFFFFF
FlutterのColorリテラルは0xAARRGGBBの並びで、先頭2桁が不透明度です。0x00が完全透明、0xFFが完全不透明で、CSSのrgba(255, 255, 255, 0)に当たるのはColor(0x00FFFFFF)です。Color(0x00000000)と書くとRGBが黒になり、次章の濁りが出ます。約50%の白は0x80FFFFFF(0x80は128で、正確には50.2%)です。constの中に置くならリテラル、実行時に既存の色から作るならwithValuesと使い分けます。
LinearGradient.withOpacityがフェードを消す挙動
グラデーション全体を半透明にしたくてgradient.withOpacity(0.5)と書くと、意図と違う結果になります。3.47.9の実装は、各色にcolor.withOpacity(opacity)を入れ直すだけだからです。不透明度は掛け算ではなく上書きなので、「不透明な白から透明な白」が「50%の白から50%の白」に変わり、フェードが消えます。
青地の上に描いて左端・中央・右端を読み戻すと、元のグラデーションは254→127→0(赤・緑成分)でした。withOpacity(0.5)後は128・128・128の一色です。Color.withOpacityと違いLinearGradient.withOpacityには@Deprecatedが付いていないため、flutter analyzeも何も言いません。各色の不透明度に倍率を掛けたいときは、標準APIのscaleを使います。scale(0.5)は各色を「その色の透明版」へ半分だけ近づけるので、RGBはそのまま不透明度だけが0.5倍になります。結果は127→63→0で、フェードが残ったまま全体が半分の濃さになりました。
final LinearGradient base = LinearGradient(
colors: <Color>[Colors.white, Colors.white.withValues(alpha: 0)],
);
final LinearGradient half = base.scale(0.5); // 不透明度 1.0→0.5、0.0→0.0
ウィジェットごと薄くしてよいなら、OpacityやFadeTransitionで包むほうが簡単です。
白から透明が灰色に濁る原因と実測値
Colors.transparentは透明な黒という定義
Flutter 3.47.9のソースで、Colors.transparentは次のように定義されています(material/colors.dart)。
/// Completely invisible.
static const Color transparent = Color(0x00000000);
RGBはすべて0、つまり黒です。補間はRGBと不透明度を別々に線形補間するので、白と透明な黒の中間は「50%不透明の灰色」になり、背景の上では暗い帯に見えます。
Flutter側もこの挙動を把握しています。Color.lerpのドキュメントには、片方がnullのときは相手の色の透明版から補間すること、そしてそれが「Colors.transparent(const Color(0x00000000))から補間するより通常は望ましい。Colors.transparentはとりわけ透明な黒である」ことが書かれています。
青地に重ねた中点ピクセルの実測値
不透明な青(0xFF0000FF)の上に幅400px・高さ8pxで白からのグラデーションを描き、高さ方向の中央の行を読み戻した値です。Flutter 3.47.9のflutter test上でPictureRecorderに描いて計測しました。ディザリングにより、読む行によって値が±1ほど動きます。
| 位置 | Colors.transparent | white.withValues(alpha: 0) |
|---|---|---|
| 0.00 | 254, 254, 254 | 254, 254, 255 |
| 0.25 | 142, 142, 207 | 190, 190, 255 |
| 0.50 | 63, 63, 191 | 127, 127, 255 |
| 0.75 | 15, 15, 207 | 63, 63, 255 |
| 1.00 | 0, 0, 255 | 0, 0, 255 |
中点では、正しい書き方の赤・緑成分127に対しColors.transparent版は63です。白と黒を50%で混ぜた色がそのまま乗り、青の成分も255から191へ落ちています。3.44.9で測った値と同じで、3.47でも挙動は変わっていません。
// リテラルで書く(const にできる)
const LinearGradient fadeOut = LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[Colors.white, Color(0x00FFFFFF)],
);
// 既存の色から作る
final Color baseColor = Theme.of(context).colorScheme.surface;
final LinearGradient fade = LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[baseColor, baseColor.withValues(alpha: 0)],
);
実務では後者を標準にしてください。テーマ色を差し替えたときに、片方の端だけ古い色が残る事故を防げます。
ShaderMaskのマスクなら濁らない境界
「Colors.transparentは使うな」と覚えると行き過ぎです。ShaderMaskでBlendMode.dstInを使う場合、参照されるのはマスクの不透明度だけで、RGBは結果に出ません。
RGBが正反対の2つのマスク、黒→Colors.transparent(RGBは黒)と黒→Colors.white.withValues(alpha: 0)(RGBは白)で同じ白い前景を抜き、青地に重ねて比べました。位置0.25で191,191,255、0.50で127,127,255、0.75で63,63,255と、両者は1バイトも違いませんでした。比較相手にColors.black.withValues(alpha: 0)を選ぶと、Colors.transparentと同じ0x00000000どうしを比べることになるので検証になりません。
グラデーションを色として塗る(BoxDecorationのgradient、Paint.shader)ならColors.transparentは使わず、マスクとして使う(ShaderMaskのdstIn・dstOut)ならどちらでも構いません。迷ったらwithValues(alpha: 0)で書けば両方で正しく動きます。
背景・ボタン・AppBar・テキストへの適用
画面全体の背景とextendBodyBehindAppBar
ScaffoldのbackgroundColorは単色しか受け付けないので、bodyに置いたContainerで塗ります。AppBarの下まで塗るにはextendBodyBehindAppBar: trueを指定し、AppBarをbackgroundColor: Colors.transparent・elevation: 0で透過させます。ここは色を塗らない指定なのでColors.transparentで問題ありません。
3.47.9のM3既定では、内容がAppBarの下へスクロールで潜ると高さがscrolledUnderElevation: 3.0に変わります。既定のshadowColorとsurfaceTintColorはどちらもColors.transparentなので、既定のままなら影や色は付きません。テーマで影色や表面色を設定しているアプリでは、その色がスクロール時に境界へ出るので、scrolledUnderElevation: 0で高さを変えないようにしておくと確実です。
Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
scrolledUnderElevation: 0,
),
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[Color(0xFF1E88E5), Color(0xFF6A1B9A)],
),
),
),
)
AppBarだけをグラデーションにするflexibleSpace
AppBarにもgradientプロパティはありません。AppBarだけを塗りたいときは、flexibleSpaceにDecoratedBoxを渡します。flexibleSpaceはタイトルの背面・背景色の上に積まれるので、半透明の色を含めるときだけbackgroundColor: Colors.transparentにします。scrolledUnderElevation: 0はスクロール時に高さを変えない指定です。
AppBar(
title: const Text('設定'),
backgroundColor: Colors.transparent,
scrolledUnderElevation: 0,
flexibleSpace: const DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: <Color>[Color(0xFF00897B), Color(0xFF1E88E5)],
),
),
),
)
ボタンのグラデーションとInkによる波紋の表示
ボタンはElevatedButtonのButtonStyle.backgroundBuilderで背景をグラデーションにできます。無効時の操作抑止やフォーカス、押下時の波紋を利用できるので、自前で組むより先にこちらを検討してください。backgroundBuilderが返すウィジェットはボタンのMaterialの子になり、ボタンの中身はさらにその子として渡されます。
ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.transparent,
shadowColor: Colors.transparent,
foregroundColor: Colors.white,
backgroundBuilder:
(BuildContext context, Set<WidgetState> states, Widget? child) {
return Ink(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: <Color>[Color(0xFFFF6D00), Color(0xFFD500F9)],
),
),
child: child,
);
},
),
child: const Text('送信する'),
)
ここでInkの代わりにDecoratedBoxやContainerを返すと、タップしても波紋が見えません。Inkのドキュメントにあるとおり、波紋は下のMaterialに描かれ、その上に重なった不透明な描画に隠れるためです。InkはdecorationをMaterialそのものに描くので、波紋がグラデーションの上に出ます。
3.47.9のウィジェットテストでボタンを押したまま300ms進め、ボタンを囲む200px×60pxのRepaintBoundary内のRGB値の合計を押す前と比べると、DecoratedBoxでは差が約0.01%、Inkでは約2.6%でした。MaterialとInkWellで自前のボタンを組む場合も、Containerにgradientを入れると差は0、Inkなら約22%で、同じ結果です。
ShaderMaskによるテキスト・アイコンの着色と画像のフェード
TextやIconのcolorも単色なので、グラデーションを乗せるにはShaderMaskで包みます。文字やアイコンを塗るなら既定のBlendMode.modulate、画像の下端を透明へ抜くならBlendMode.dstInです。modulateでグラデーションの色をそのまま出したい文字・アイコンは、子ウィジェットの色を白にしてください。dstInで画像をフェードさせる場合、画像を白くする必要はありません。modulateは前景とグラデーションの色を掛け合わせるため、子が中間灰色(0xFF808080)だと中央の色が233,54,124から117,27,62へ半減し、青だと赤と緑が0になりました。
ShaderMask(
shaderCallback: (Rect bounds) => const LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: <Color>[Color(0xFFFF6D00), Color(0xFFD500F9)],
).createShader(bounds),
child: const Icon(Icons.favorite, size: 64, color: Colors.white),
)
ShaderMask(
shaderCallback: (Rect bounds) => const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[Colors.black, Colors.transparent],
).createShader(bounds),
blendMode: BlendMode.dstIn,
child: Image.asset('assets/photo.png'),
)
shaderCallbackが受け取るboundsは子ウィジェットの矩形なので、そのままcreateShaderに渡します。固定のRectを渡すと、画面サイズが変わったときに範囲がずれます。半透明を重ねるすりガラス表現をFlutterで作る選択肢はFlutterのLiquid Glass対応状況|公式実装は作業中、今使えるパッケージと選び方で比べています。
グラデーションのアニメーションとGradient.lerpの補間
グラデーションを切り替えるときは、AnimatedContainerのdecorationを差し替えるだけで補間されます。BoxDecoration.lerpが内部でGradient.lerpを呼ぶためです。
AnimatedContainer(
duration: const Duration(milliseconds: 400),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: selected
? const <Color>[Color(0xFF2196F3), Color(0xFF9C27B0)]
: const <Color>[Color(0xFFFF9800), Color(0xFFE91E63)],
),
),
)
selectedはStatefulWidgetの状態として持ち、setStateで切り替えます(状態の持ち方はStatelessWidgetとStatefulWidgetの違いと使い分け|Flutter 3.44の実装で確認する判定基準)。補間の結果は、前後の型が同じかどうかで変わります。
- LinearGradientどうし:色数やstopsが違ってもつながります。色2つ(stopsなし)と色3つ(stops 0.0・0.3・1.0)の中間を
Gradient.lerpで取り出すと、stopsは両者を合わせた0.0・0.3・1.0の3点になり、各点の色が補間されていました。 - LinearGradientからRadialGradientへ:互いに補間できないため、前半で元のグラデーションを透明へ、後半で次のグラデーションを透明から戻します。白地で測ると、t=0.25では色の不透明度が0.5、t=0.5では全色の不透明度が0で、白地がそのまま見えました。
型をまたぐ切り替えで画面が一瞬抜けて見えるのはこのためです。避けたいなら、型を揃える(RadialGradientどうしでcenterやradiusを動かす)か、AnimatedSwitcherで2枚を重ねてクロスフェードさせます。
RadialGradient・SweepGradientとの使い分け
線形のほかに2種類のグラデーションがあり、どちらもBoxDecorationのgradientに同じ形で渡せます。
| クラス | 色の変化 | 位置指定 | 主な用途 |
|---|---|---|---|
| LinearGradient | 直線方向 | begin / end | 背景・ボタン・フェード |
| RadialGradient | 中心から外周へ | center / radius | スポットライト・円形ボタン |
| SweepGradient | 中心まわりの角度 | center / startAngle | 進捗リング・カラーホイール |
RadialGradientのradiusの基準と既定値0.5
RadialGradientのradiusは、ソースのドキュメントでは「描画する矩形の短辺の長さに対する比率」です。幅100px・高さ200pxならradius: 1.0は中心から100pxで、短辺の端にちょうど外側の色が来る0.5が既定値です。この矩形で1.0へ上げると、外周の色に届くのは上下端の中央と四隅だけで、左右端の中央は中間の色のままです。「横方向に外周の色が出ない」ときはこの基準を確認し、中心をずらすならcenterにAlignmentを渡します。
SweepGradientの開始位置とGradientRotationの角度
SweepGradientは3時の位置から時計回りに色が進みます。startAngleとendAngleはラジアンで、既定は0.0からmath.pi * 2の全周です。
開始位置を変えるときは公式ドキュメントの説明に注意してください。GradientTransformのAPIリファレンスは「6時から始めるにはpi/4ラジアン(45度)のGradientRotationを渡す」と書き、GradientRotationのサンプルはmath.pi/4を「4分の1回転」と説明しています。3.47.9で黒から白のSweepGradientを200px四方に描くと、math.pi / 4では黒(0)が4時半の位置に来ました。6時から始まったのはmath.pi / 2のときです。6時開始にするならmath.pi / 2、12時開始なら-math.pi / 2を渡します。
Web側で3Dのグラデーション背景を作る場合はShaderGradientとは?React・Vue・Next.jsで3Dグラデーション背景を実装する手順が別の選択肢になります。
縞(バンディング)と継ぎ目を抑える配色
グラデーションが縞に見える現象をカラーバンディングと呼びます。原因は階調の不足です。Flutter 3.47.9で、幅を変えながら1行に現れる色の数を数えました(高さ4pxで描画し、4行それぞれを集計)。
| 配色 | 幅200px | 幅400px | 幅1200px |
|---|---|---|---|
| #1A1A1A から #000000 | 27色 | 27色 | 27色 |
| #FFFFFF から #000000 | 150〜200色 | 220〜256色 | 256色 |
今回の8bitグレースケール出力で表現できる階調の上限は両端の色差で決まり、実際に1行へ現れる色数は幅やディザリングにも左右されます。#1A1A1Aと#000000の間には整数値が26段しかないため、1200pxに広げても27色のまま、1色あたり約44pxの帯になります。白黒の行ごとの差はディザリングによるものです。暗い背景で近い2色を広い面に引き延ばすのは避け、色差を広げてください。
もう1つの見え方の問題が、透明へ抜けるフェードの始点に出る「線」です。直線補間は始点で不透明度の減り方が急に変わるため、その位置が境目として見えることがあります。イージング曲線に沿った中間色をColor.lerpで作って並べると、変化がなだらかになります。
List<Color> easedColors(Color from, Color to,
{int steps = 8, Curve curve = Curves.easeInOut}) {
return <Color>[
for (int i = 0; i <= steps; i++)
Color.lerp(from, to, curve.transform(i / steps))!,
];
}
final LinearGradient shadow = LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: easedColors(Colors.black, Colors.black.withValues(alpha: 0)),
);
stopsを省略すると9色が等間隔に並びます。このときの不透明度は1.000・0.969・0.871・0.708・0.500・0.292・0.129・0.031・0.000で、両端の変化が緩やかになりました。同じ考え方をまとめたパッケージにeasing_gradientがあります(1.0.0・2026年10月22日公開・Flutter 3.47.0以上)。依存を増やすほどの用途でなければ、上の十数行で足ります。
グラデーションを使った画面をiOS・Androidで同じ見た目に揃えるところまで含めて開発を任せたい場合は、Flutter / React Native開発で相談を受けています。画面とウィジェットの分け方はFlutterアーキテクチャのベストプラクティス|公式ガイドのレイヤー設計・ディレクトリ構成・設計パターン【Flutter 3.47対応】で扱っています。
よくある質問
Flutterで透明のカラーコードはどう書きますか?
Color(0x00FFFFFF)のように、先頭2桁の不透明度を00にします。FlutterのColorは0xAARRGGBBの順で、CSSのrgba()とは不透明度の位置が違います。0x00000000は透明な黒(Colors.transparentと同値)で、グラデーションでは灰色の濁りの原因になります。
withOpacityは使えなくなりましたか?
Flutter 3.47.9でも動きます。3.27で非推奨になりましたが削除時期は未定で、flutter analyzeでinfoが出るだけです。内部で8bitに丸めるため、withValues(alpha:)へ移行してください。なおLinearGradient.withOpacityは非推奨ではありませんが、各色の不透明度を上書きするのでフェードが消えます。
グラデーションを斜めや任意の角度にするにはどうしますか?
45度前後の斜めならbegin: Alignment.topLeft・end: Alignment.bottomRightのように角を指定します。両端の色が必ず出ますが、傾きは矩形の縦横比で変わります。角度を固定したいならtransform: GradientRotation(角度 * math.pi / 180)を使います。こちらはグラデーションの長さが回転前のままなので、矩形の縦横比と回転角度によっては端の色が画面に出ません。
円形・放射状のグラデーションはどう作りますか?
LinearGradientをRadialGradientに置き換え、begin・endの代わりにcenterとradiusを指定します。radiusは矩形の短辺の長さを1.0とした比率で、既定値は0.5です。角度で一周させるならSweepGradientを使います。
白から透明のグラデーションが灰色になるのはバグですか?
仕様どおりの動作です。Colors.transparentはRGBが黒のColor(0x00000000)なので、白から補間すると中間が灰色になります。Colors.white.withValues(alpha: 0)かColor(0x00FFFFFF)に置き換えれば直ります。ShaderMaskのdstInマスクでは不透明度しか見ないので起きません。