本文大量致敬了張風捷特烈的文章。
在 Flutter 中,如果想要為 Container
的四個邊分別設定不同的顏色,方法很簡單:
Container(
width: 200,
height: 200,
decoration: BoxDecoration(
border: Border(
left: BorderSide(color: Colors.red, width: 5),
top: BorderSide(color: Colors.blue, width: 5),
right: BorderSide(color: Colors.orange, width: 5),
bottom: BorderSide(color: Colors.green, width: 5),
),
// borderRadius: BorderRadius.circular(20),
),
child: Center(
child: Text(
'Hello World',
style: TextStyle(fontSize: 20),
),
),
),
複製程式碼
實現效果:
邊和邊之間的過渡非常生硬,並且一旦想要吧 Container
設定為圓角,編譯器就會報錯:
======== Exception caught by rendering library =====================================================
The following assertion was thrown during paint():
A borderRadius can only be given for a uniform Border.
\
The following is not uniform:
BorderSide.color
The relevant error-causing widget was:
...
複製程式碼
那麼如何才能實現“彩邊圓角“的 Container
呢?先來看一下我們最終實現的效果:
首先我們要自己實現一個 Decoration
類:
class ColorDecoration extends Decoration {
@override
BoxPainter createBoxPainter([ui.VoidCallback? onChanged]) =>
ColorBoxPainter();
}
複製程式碼
IDE 提示我們要實現一個 createBoxPainter
方法,並且要返回一個 BoxPainter
類。所以我們再繼承 BoxPainter
實現一個 ColorBoxPainter
類,並且實現 paint
方法。
class ColorBoxPainter extends BoxPainter {
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
}
}
複製程式碼
我們在該 Paint
方法中實現對containter 的繪製。
class ColorBoxPainter extends BoxPainter {
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
var colors = [
Color(0xFFF60C0C),
Color(0xFFF3B913),
Color(0xFFE7F716),
Color(0xFF3DF30B),
Color(0xFF0DF6EF),
Color(0xFF0829FB),
Color(0xFFB709F4),
];
var pos = [1.0 / 7, 2.0 / 7, 3.0 / 7, 4.0 / 7, 5.0 / 7, 6.0 / 7, 1.0];
// 矩形中心位置
var center_pos = Offset(
offset.dx + configuration.size!.width / 2,
offset.dy + configuration.size!.height / 2,
);
// 矩形右下角位置
var right_bottom_pos = Offset(
offset.dx + configuration.size!.width,
offset.dy + configuration.size!.height,
);
// 定義 painter
Paint painter = Paint()
..style = PaintingStyle.stroke
..strokeWidth = borderWidth
..ui.Gradient.sweep(center_pos, colors, pos);
// 定義矩形
var rect = Rect.fromCenter(
center: center_pos,
width: configuration.size!.width,
height: configuration.size!.height,
);
var rrect = RRect.fromRectXY(rect, radius.x, radius.y);
// 繪畫
canvas.drawRRect(rrect, painter);
}
}
複製程式碼
實現這個之後,其實最根本的東西就已經做出來了,下面只需要在類的建構函式里加上幾個引數,允許使用者一定程度上進行自定義即可。
下面是全部程式碼:
import 'dart:ui' as ui;
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized(); // 確定初始化
SystemChrome.setPreferredOrientations(// 使裝置橫屏顯示
[DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]);
SystemChrome.setEnabledSystemUIOverlays([]); // 全屏顯示
runApp(Paper());
}
class Paper extends StatelessWidget {
final List<Color> colors = [
Colors.red,
Colors.orange,
Colors.yellow,
Colors.green,
Colors.blue,
Colors.indigo,
Colors.purple
];
@override
Widget build(BuildContext context) {
return MediaQuery(
data: MediaQueryData(),
child: Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: Container(
width: 200,
height: 200,
decoration: ColorDecoration(
colors: colors,
radius: Radius.circular(5),
gradientMethod: GradientMethod.sweep,
borderWidth: 5,
),
child: Center(
child: Text(
'Hello World',
style: TextStyle(fontSize: 20),
),
),
),
),
),
);
}
}
class ColorDecoration extends Decoration {
Radius radius;
List<Color> colors;
GradientMethod gradientMethod;
double borderWidth;
ColorDecoration(
{required this.colors,
required this.radius,
required this.gradientMethod,
required this.borderWidth});
@override
BoxPainter createBoxPainter([ui.VoidCallback? onChanged]) {
return ColorBoxPainter(
colors: colors,
radius: this.radius,
gradientMethod: this.gradientMethod,
borderWidth: this.borderWidth,
);
}
}
class ColorBoxPainter extends BoxPainter {
Radius radius;
List<Color> colors;
GradientMethod gradientMethod;
double borderWidth;
ColorBoxPainter(
{required this.colors,
required this.radius,
required this.gradientMethod,
required this.borderWidth});
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
var color_stop = List<double>.generate(
colors.length,
(index) => index / colors.length,
);
var center_pos = Offset(
offset.dx + configuration.size!.width / 2,
offset.dy + configuration.size!.height / 2,
);
var right_bottom_pos = Offset(
offset.dx + configuration.size!.width,
offset.dy + configuration.size!.height,
);
var border_len_average =
(configuration.size!.height + configuration.size!.width) / 2;
Paint painter = Paint()
..style = PaintingStyle.stroke
..strokeWidth = borderWidth;
if (gradientMethod == GradientMethod.sweep)
painter.shader = ui.Gradient.sweep(center_pos, colors, color_stop);
else if (gradientMethod == GradientMethod.liner)
painter.shader =
ui.Gradient.linear(offset, right_bottom_pos, colors, color_stop);
else if (gradientMethod == GradientMethod.radial)
painter.shader = ui.Gradient.radial(
center_pos, border_len_average, colors, color_stop);
else {
throw Exception('Gradient Can NOT be Empty.');
}
var rect = Rect.fromCenter(
center: center_pos,
width: configuration.size!.width,
height: configuration.size!.height,
);
var rrect = RRect.fromRectXY(rect, radius.x, radius.y);
canvas.drawRRect(rrect, painter);
}
}
enum GradientMethod {
liner,
sweep,
radial,
}
複製程式碼