Flutter (Dart):将屏幕分成 6 个部分,每个部分在中心有一个角
Flutter (Dart): Split the screen into 6 sections, each with one corner in the center
我正在尝试将屏幕分成 6 个 sections/parts,并满足以下要求:
- 每个部分都应该在屏幕中央有一个角(连接到示例图中的中心圆,见下文)。
- 每个部分都应连接到屏幕的周边。
我想在每个部分中都有一个单独的 GestureDetector。
有人知道用 Flutter & Dart 做这个的好方法吗?
示例图
对于内圈,我使用 Container
and Stack
to place those widgets. And shape is made using ClipPath
。
Make use to place the circle button as last stack child, because UI prioritize bottom to top,
运行 dartpad。
路径
class BottomCentertPath extends CustomClipper<Path> {
@override
Path getClip(Size size) => Path()
..moveTo(size.width / 4, size.height)
..lineTo(size.width / 4 * 3, size.height)
..lineTo(size.width / 2, size.height / 2);
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}
class BottomLeftPath extends CustomClipper<Path> {
@override
Path getClip(Size size) => Path()
..moveTo(size.width, size.height / 3 * 2)
..lineTo(size.width, size.height)
..lineTo(size.width / 4 * 3, size.height)
..lineTo(size.width / 2, size.height / 2);
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}
class BottomRightPath extends CustomClipper<Path> {
@override
Path getClip(Size size) => Path()
..moveTo(0, size.height / 3 * 2)
..lineTo(0, size.height)
..lineTo(size.width / 4, size.height)
..lineTo(size.width / 2, size.height / 2);
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}
class CenterRighttPath extends CustomClipper<Path> {
@override
Path getClip(Size size) => Path()
..moveTo(size.width, size.height / 3)
..lineTo(size.width / 2, size.height / 2)
..lineTo(size.width, size.height / 3 * 2);
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}
class CenterLeftPath extends CustomClipper<Path> {
@override
Path getClip(Size size) => Path()
..moveTo(0, size.height / 3)
..lineTo(size.width / 2, size.height / 2)
..lineTo(0, size.height / 3 * 2);
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}
class TopRightPath extends CustomClipper<Path> {
@override
Path getClip(Size size) => Path()
..moveTo(size.width / 2, 0)
..lineTo(size.width, 0)
..lineTo(size.width, size.height / 3)
..lineTo(size.width / 2, size.height / 2);
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}
class TopLeftPath extends CustomClipper<Path> {
@override
Path getClip(Size size) => Path()
..lineTo(size.width / 2, 0)
..lineTo(size.width / 2, size.height / 2)
..lineTo(0, size.height / 3);
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}
测试小部件
class S7venIn1 extends StatelessWidget {
const S7venIn1({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: LayoutBuilder(builder: (context, constraints) {
return Stack(
children: [
GestureDetector(
onTap: () {
debugPrint("TopLeftPath:");
},
child: ClipPath(
clipper: TopLeftPath(),
child: Container(
color: Colors.cyanAccent,
),
),
),
GestureDetector(
onTap: () {
debugPrint("TopRightPath:");
},
child: ClipPath(
clipper: TopRightPath(),
child: Container(
color: Colors.deepPurple,
),
),
),
GestureDetector(
onTap: () {
debugPrint("CenterLeftPath:");
},
child: ClipPath(
clipper: CenterLeftPath(),
child: Container(
color: Colors.deepOrange,
),
),
),
ClipPath(
clipper: CenterRighttPath(),
child: Container(
color: Colors.greenAccent,
),
),
GestureDetector(
onTap: () {
debugPrint("CenterRighttPath:");
},
child: ClipPath(
clipper: CenterRighttPath(),
child: Container(
color: Colors.greenAccent,
),
),
),
GestureDetector(
onTap: () {
debugPrint("BottomRightPath");
},
child: ClipPath(
clipper: BottomRightPath(),
child: Container(
color: Colors.indigoAccent,
),
),
),
GestureDetector(
onTap: () {
debugPrint("BottomCentertPath");
},
child: ClipPath(
clipper: BottomCentertPath(),
child: Container(
color: Colors.pinkAccent,
),
),
),
GestureDetector(
onTap: () {
debugPrint("BottomLeftPath");
},
child: ClipPath(
clipper: BottomLeftPath(),
child: Container(
color: Colors.amberAccent,
),
),
),
Align(
alignment: Alignment.center,
child: GestureDetector(
onTap: () {
debugPrint("Center Widget");
},
child: Container(
height: constraints.maxWidth * .25,
decoration: const BoxDecoration(
shape: BoxShape.circle, color: Colors.white),
),
),
)
],
);
}),
);
}
}
在 CustomClipper<Path>
上玩大小游戏。检查 this 以了解更多信息。
结果
我正在尝试将屏幕分成 6 个 sections/parts,并满足以下要求:
- 每个部分都应该在屏幕中央有一个角(连接到示例图中的中心圆,见下文)。
- 每个部分都应连接到屏幕的周边。
我想在每个部分中都有一个单独的 GestureDetector。
有人知道用 Flutter & Dart 做这个的好方法吗?
示例图
对于内圈,我使用 Container
and Stack
to place those widgets. And shape is made using ClipPath
。
Make use to place the circle button as last stack child, because UI prioritize bottom to top,
运行 dartpad。
路径
class BottomCentertPath extends CustomClipper<Path> {
@override
Path getClip(Size size) => Path()
..moveTo(size.width / 4, size.height)
..lineTo(size.width / 4 * 3, size.height)
..lineTo(size.width / 2, size.height / 2);
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}
class BottomLeftPath extends CustomClipper<Path> {
@override
Path getClip(Size size) => Path()
..moveTo(size.width, size.height / 3 * 2)
..lineTo(size.width, size.height)
..lineTo(size.width / 4 * 3, size.height)
..lineTo(size.width / 2, size.height / 2);
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}
class BottomRightPath extends CustomClipper<Path> {
@override
Path getClip(Size size) => Path()
..moveTo(0, size.height / 3 * 2)
..lineTo(0, size.height)
..lineTo(size.width / 4, size.height)
..lineTo(size.width / 2, size.height / 2);
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}
class CenterRighttPath extends CustomClipper<Path> {
@override
Path getClip(Size size) => Path()
..moveTo(size.width, size.height / 3)
..lineTo(size.width / 2, size.height / 2)
..lineTo(size.width, size.height / 3 * 2);
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}
class CenterLeftPath extends CustomClipper<Path> {
@override
Path getClip(Size size) => Path()
..moveTo(0, size.height / 3)
..lineTo(size.width / 2, size.height / 2)
..lineTo(0, size.height / 3 * 2);
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}
class TopRightPath extends CustomClipper<Path> {
@override
Path getClip(Size size) => Path()
..moveTo(size.width / 2, 0)
..lineTo(size.width, 0)
..lineTo(size.width, size.height / 3)
..lineTo(size.width / 2, size.height / 2);
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}
class TopLeftPath extends CustomClipper<Path> {
@override
Path getClip(Size size) => Path()
..lineTo(size.width / 2, 0)
..lineTo(size.width / 2, size.height / 2)
..lineTo(0, size.height / 3);
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}
测试小部件
class S7venIn1 extends StatelessWidget {
const S7venIn1({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: LayoutBuilder(builder: (context, constraints) {
return Stack(
children: [
GestureDetector(
onTap: () {
debugPrint("TopLeftPath:");
},
child: ClipPath(
clipper: TopLeftPath(),
child: Container(
color: Colors.cyanAccent,
),
),
),
GestureDetector(
onTap: () {
debugPrint("TopRightPath:");
},
child: ClipPath(
clipper: TopRightPath(),
child: Container(
color: Colors.deepPurple,
),
),
),
GestureDetector(
onTap: () {
debugPrint("CenterLeftPath:");
},
child: ClipPath(
clipper: CenterLeftPath(),
child: Container(
color: Colors.deepOrange,
),
),
),
ClipPath(
clipper: CenterRighttPath(),
child: Container(
color: Colors.greenAccent,
),
),
GestureDetector(
onTap: () {
debugPrint("CenterRighttPath:");
},
child: ClipPath(
clipper: CenterRighttPath(),
child: Container(
color: Colors.greenAccent,
),
),
),
GestureDetector(
onTap: () {
debugPrint("BottomRightPath");
},
child: ClipPath(
clipper: BottomRightPath(),
child: Container(
color: Colors.indigoAccent,
),
),
),
GestureDetector(
onTap: () {
debugPrint("BottomCentertPath");
},
child: ClipPath(
clipper: BottomCentertPath(),
child: Container(
color: Colors.pinkAccent,
),
),
),
GestureDetector(
onTap: () {
debugPrint("BottomLeftPath");
},
child: ClipPath(
clipper: BottomLeftPath(),
child: Container(
color: Colors.amberAccent,
),
),
),
Align(
alignment: Alignment.center,
child: GestureDetector(
onTap: () {
debugPrint("Center Widget");
},
child: Container(
height: constraints.maxWidth * .25,
decoration: const BoxDecoration(
shape: BoxShape.circle, color: Colors.white),
),
),
)
],
);
}),
);
}
}
在 CustomClipper<Path>
上玩大小游戏。检查 this 以了解更多信息。
结果