本文大量致敬了张风捷特烈的文章。
在 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,
}