如何在 Flutter 中实现这个 UI?
How to achieve this UI in Flutter?
我需要一个可重新排列的三角形作为容器小部件上方的指示器,它还应用了容器的渐变。
现在红色三角形是一种定制油漆,但我愿意用另一种方式来做。它的位置必须是可定制的。
在渐变发挥作用之前一切都很好,因为我也无法将它附加到三角形。
所以我需要的是一个小部件,它结合了两个元素(三角形和容器)并允许我为它定义装饰,就像为普通容器一样。
关于如何解决类似问题的任何想法?
使用 ClipPath() 和自定义形状按钮我建议检查 that
ClipPath(
clipper: CustomButton(),
child: Container(
height: 150,
width: 600,
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.blue,
Colors.deepPurple,
],
begin: FractionalOffset(0.0, 0.0),
end: FractionalOffset(1.0, 1.0),
stops: [0.0, 1.0],
tileMode: TileMode.clamp,
)
),
),
)
...
class ArrowClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
final Path path = Path();
path.moveTo(size.width*0.2788164,0);
path.lineTo(size.width*0.2797843,0);
path.cubicTo(size.width*0.2866704,size.height*0.03013193,size.width*0.2916482,size.height*0.06854881,size.width*0.2975996,size.height*0.1026649);
path.cubicTo(size.width*0.2998894,size.height*0.1151187,size.width*0.3014602,size.height*0.1321108,size.width*0.3046405,size.height*0.1401319);
path.cubicTo(size.width*0.3129093,size.height*0.1453562,size.width*0.3213164,size.height*0.1418997,size.width*0.3296460,size.height*0.1425330);
path.cubicTo(size.width*0.5416704,size.height*0.1424011,size.width*0.7536947,size.height*0.1425594,size.width*0.9657190,size.height*0.1424538);
path.cubicTo(size.width*0.9740321,size.height*0.1419789,size.width*0.9831139,size.height*0.1488391,size.width*0.9893418,size.height*0.1775462);
path.cubicTo(size.width*0.9951493,size.height*0.1999208,size.width*0.9987611,size.height*0.2330343,size.width,size.height*0.2678628);
path.lineTo(size.width,size.height*0.8773879);
path.cubicTo(size.width*0.9988827,size.height*0.9051979,size.width*0.9963827,size.height*0.9319525,size.width*0.9923894,size.height*0.9530871);
path.cubicTo(size.width*0.9878374,size.height*0.9787863,size.width*0.9811007,size.height*0.9936675,size.width*0.9742865,size.height);
path.lineTo(size.width*0.02569690,size.height);
path.cubicTo(size.width*0.01769358,size.height*0.9929551,size.width*0.01011615,size.height*0.9720317,size.width*0.005470133,size.height*0.9397361);
path.cubicTo(size.width*0.002323009,size.height*0.9201319,size.width*0.001111726,size.height*0.8954881,0,size.height*0.8718734);
path.lineTo(0,size.height*0.2732454);
path.cubicTo(size.width*0.001299779,size.height*0.2307124,size.width*0.005945796,size.height*0.1888918,size.width*0.01383850,size.height*0.1663852);
path.cubicTo(size.width*0.02036504,size.height*0.1438259,size.width*0.02874447,size.height*0.1420053,size.width*0.03649336,size.height*0.1422427);
path.cubicTo(size.width*0.1065653,size.height*0.1429024,size.width*0.1766427,size.height*0.1420317,size.width*0.2467201,size.height*0.1427441);
path.cubicTo(size.width*0.2495077,size.height*0.1397361,size.width*0.2535177,size.height*0.1469921,size.width*0.2556305,size.height*0.1350923);
path.cubicTo(size.width*0.2632688,size.height*0.08978892,size.width*0.2701604,size.height*0.04110818,size.width*0.2788164,0);
path.close();
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => true;
}
作为 ClipPath 的替代方案,您可以在按钮容器后面使用带有旋转容器的 Stack。这应该为响应式 UI 提供一点误差余地。 (为简洁起见,我没有设计蓝色按钮容器的样式,因为您似乎觉得没问题。)
您可以使用第一个 Positioned left:
属性控制指针的位置。
您可以在 DartPad 中运行下面的代码。
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 300,
height: 150,
child: Stack(
children: [
Positioned(
top: 12.5,
left: 100,
child: Transform.rotate(
angle: 0.7854,
child: Container(
width: 25,
height: 25,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.red,
Colors.orange,
],
begin: FractionalOffset(0.0, 0.0),
end: FractionalOffset(1.0, 1.0),
stops: [0.0, 1.0],
tileMode: TileMode.clamp,
)
),
)
),
),
Positioned(
top: 20,
child: Container(
width: 300,
height: 100,
color: Colors.blue,
child: const SizedBox(width: 300, height: 100)
),
)
])
);
}
}
我需要一个可重新排列的三角形作为容器小部件上方的指示器,它还应用了容器的渐变。
现在红色三角形是一种定制油漆,但我愿意用另一种方式来做。它的位置必须是可定制的。 在渐变发挥作用之前一切都很好,因为我也无法将它附加到三角形。
所以我需要的是一个小部件,它结合了两个元素(三角形和容器)并允许我为它定义装饰,就像为普通容器一样。
使用 ClipPath() 和自定义形状按钮我建议检查 that
ClipPath(
clipper: CustomButton(),
child: Container(
height: 150,
width: 600,
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.blue,
Colors.deepPurple,
],
begin: FractionalOffset(0.0, 0.0),
end: FractionalOffset(1.0, 1.0),
stops: [0.0, 1.0],
tileMode: TileMode.clamp,
)
),
),
)
...
class ArrowClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
final Path path = Path();
path.moveTo(size.width*0.2788164,0);
path.lineTo(size.width*0.2797843,0);
path.cubicTo(size.width*0.2866704,size.height*0.03013193,size.width*0.2916482,size.height*0.06854881,size.width*0.2975996,size.height*0.1026649);
path.cubicTo(size.width*0.2998894,size.height*0.1151187,size.width*0.3014602,size.height*0.1321108,size.width*0.3046405,size.height*0.1401319);
path.cubicTo(size.width*0.3129093,size.height*0.1453562,size.width*0.3213164,size.height*0.1418997,size.width*0.3296460,size.height*0.1425330);
path.cubicTo(size.width*0.5416704,size.height*0.1424011,size.width*0.7536947,size.height*0.1425594,size.width*0.9657190,size.height*0.1424538);
path.cubicTo(size.width*0.9740321,size.height*0.1419789,size.width*0.9831139,size.height*0.1488391,size.width*0.9893418,size.height*0.1775462);
path.cubicTo(size.width*0.9951493,size.height*0.1999208,size.width*0.9987611,size.height*0.2330343,size.width,size.height*0.2678628);
path.lineTo(size.width,size.height*0.8773879);
path.cubicTo(size.width*0.9988827,size.height*0.9051979,size.width*0.9963827,size.height*0.9319525,size.width*0.9923894,size.height*0.9530871);
path.cubicTo(size.width*0.9878374,size.height*0.9787863,size.width*0.9811007,size.height*0.9936675,size.width*0.9742865,size.height);
path.lineTo(size.width*0.02569690,size.height);
path.cubicTo(size.width*0.01769358,size.height*0.9929551,size.width*0.01011615,size.height*0.9720317,size.width*0.005470133,size.height*0.9397361);
path.cubicTo(size.width*0.002323009,size.height*0.9201319,size.width*0.001111726,size.height*0.8954881,0,size.height*0.8718734);
path.lineTo(0,size.height*0.2732454);
path.cubicTo(size.width*0.001299779,size.height*0.2307124,size.width*0.005945796,size.height*0.1888918,size.width*0.01383850,size.height*0.1663852);
path.cubicTo(size.width*0.02036504,size.height*0.1438259,size.width*0.02874447,size.height*0.1420053,size.width*0.03649336,size.height*0.1422427);
path.cubicTo(size.width*0.1065653,size.height*0.1429024,size.width*0.1766427,size.height*0.1420317,size.width*0.2467201,size.height*0.1427441);
path.cubicTo(size.width*0.2495077,size.height*0.1397361,size.width*0.2535177,size.height*0.1469921,size.width*0.2556305,size.height*0.1350923);
path.cubicTo(size.width*0.2632688,size.height*0.08978892,size.width*0.2701604,size.height*0.04110818,size.width*0.2788164,0);
path.close();
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => true;
}
作为 ClipPath 的替代方案,您可以在按钮容器后面使用带有旋转容器的 Stack。这应该为响应式 UI 提供一点误差余地。 (为简洁起见,我没有设计蓝色按钮容器的样式,因为您似乎觉得没问题。)
您可以使用第一个 Positioned left:
属性控制指针的位置。
您可以在 DartPad 中运行下面的代码。
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 300,
height: 150,
child: Stack(
children: [
Positioned(
top: 12.5,
left: 100,
child: Transform.rotate(
angle: 0.7854,
child: Container(
width: 25,
height: 25,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.red,
Colors.orange,
],
begin: FractionalOffset(0.0, 0.0),
end: FractionalOffset(1.0, 1.0),
stops: [0.0, 1.0],
tileMode: TileMode.clamp,
)
),
)
),
),
Positioned(
top: 20,
child: Container(
width: 300,
height: 100,
color: Colors.blue,
child: const SizedBox(width: 300, height: 100)
),
)
])
);
}
}