떨림: 어떻게 덮개층에 구멍을 잘라요
이 점을 실현할 수 있는 몇 가지 방법이 있다.그것들을 좀 봅시다.
우리의 목표
이것은 우리의 목표가 되었다. 배경에 화면이 있는 응용 프로그램(이 예에서는 하나ListView
와 맨 위에 있는 응용 프로그램FloatingActionButton
은 반투명한 덮어쓰기로 둘러싸여 있고 옆에 설명 텍스트가 있다.
개념
이 문제를 해결하기 위해 이 배후의 개념을 생각해 보자.
우리는 서로 다른 층을 한데 쌓기를 희망한다.
이 문제를 해결하기 위해 이 배후의 개념을 생각해 보자.
우리는 서로 다른 층을 한데 쌓기를 희망한다.
주의해라, 이것은 크리스털 공장에만 적용된다.만약 우리가 강좌에서 a
FlatButton
나 aTextInput
를 설명한다면 우리는 이 원소를 맨 밑에서 사용할 것이다.크리스털 공장은 일부분이 아닌 덮어쓰는 일반적인 화포로 그려져 있다.지금 이것은 중요하지 않다. 왜냐하면 이것은 우리의 빈틈이기 때문이다.따라서 위에서 아래로 단추가 z축에 있는 위치를 말할 수 없다.
실시
전진은 수평 중첩Row
, 수직 중첩Column
과 z방향 중첩Stack
을 제공한다.
우리는 그것으로 상술한 개념을 실현할 것이다.
Stack(children: <Widget>[
_getContent(),
_getOverlay(),
_getHint()
]);
이것은 주요한 작은 부품이다.
Stack(children: <Widget>[
_getContent(),
_getOverlay(),
_getHint()
]);
_getContent()
우리가 덮어쓰고자 하는 모든 내용을 되돌려야 한다(위에서 언급한 예시에는 ListView
_getOverlay()
오른쪽 아래 모서리_getHint()
표시 버튼 반환 프롬프트Scaffold
의 일부분이다. 이것이 바로 그것이 창고의 작은 부품 안에 있지 않은 이유다.레이어 덮어쓰기
가장 까다로운 부분은
_getOverlay()
방법이다.나는 여러 가지 가능성을 소개하고 싶다.클리파스
파일:
Calls a callback on a delegate whenever the widget is to be painted. The callback returns a path and the widget prevents the child from painting outside the path.
그래서 기본적으로 우리는 그려야 할 형상을 가지고 그 다음에
ClipPath
어디에 그려야 하는지를 정의한다.오직 한 가지 문제: 우리는 상반된 결과를 원한다.덮어쓰기가 그려지지 않은 영역을 설명하려고 합니다.그러나 우리가 이 문제를 처리하기 전에
ClipPath
통상적으로 어떤 모습일지 봅시다.사용자 정의 재단기를 만들기 위해서는 클래스
CustomClipper<T>
에서 확장해야 합니다. 이 예에서 형식을 Path로 설정합니다. 더 복잡한 모양 (직사각형 안의 타원) 이 필요하기 때문입니다.미리 정의된 기본 형태도 있습니다.편집이 간단할 때 (예: 타원형이나 필렛 사각형) 이 옵션을 사용할 수 있습니다. 코드가 적고 성능이 높기 때문입니다.class InvertedClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
return new Path();
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => true;
}
우리는 다음 두 가지 방법을 덮어써야 한다.getClip
: 입력은 RenderBox
크기이고 필요한 출력은 Path
, 그릴 주어진 RenderBox
내의 공간을 나타냅니다 shouldReclip
: Path
대상의 새로운 실례가 존재할 때 이 방법을 호출합니다.입력은 클립퍼의 이전 버전입니다.부울 출력을 통해 자르기를 다시 실행할지 여부를 결정할 수 있습니다.실제로 개발 목적에서 우리가true로 돌아온 것은 열을 다시 불러와 일을 할 수 있기를 바라기 때문이다.발표 버전에서, 성능 때문에false를 되돌려주고,clipper는 정적입니다getClip
방법입니다.return Path()
..addRect(Rect.fromLTWH(0, 0, size.width, size.height))
..addOval(Rect.fromCircle(center: Offset(size.width -44, size.height - 44), radius: 40))
..fillType = PathFillType.evenOdd;
당신의 이해 정도에 따라 해석이 필요한 부분이 있을 수 있습니다.우선, 문법에 관해서: 나는 두 가지를 사용했는데 Dart에서 캐스케이드 기호라고 부른다.만약 같은 종류의 실례에 일련의 방법이 호출된다면, 이것은 단지 문법 설탕일 뿐이다.
우리는 경로에 두 가지 모양을 추가했다. a
Rect
와 aOval
.사각형은 전체 덮어쓰기를 나타내기 때문에 전체 RenderBox 크기를 가집니다.타원형은 덮어쓰는 층의 구멍이기 때문에 결정 공장의 크기가 있다.마지막 말은 사실 매우 중요하다.만약 우리가 그것을 소홀히 한다면, 우리는 덮어쓰는 층을 볼 수 있지만, 구멍은 볼 수 없다.그런데 왜요?기억해라, 경로 안의 모든 것은 그려진 것이고, 경로 밖의 모든 것은 그렇지 않다.따라서 타원은 외부로 간주되고 주위의 사각형은 내부로 간주되는 경로를 만들어야 합니다.
PathFillType
바로 이 점을 확정했다.기본적으로 fillType은 PathFillType.nonZero
로 설정됩니다.이 경우 다음 조건이 충족되면 경로 내의 지정된 점으로 간주됩니다.a line drawn from the point to infinity crosses lines going clockwise around the point a different number of times than it crosses lines going counter-clockwise around that point
다음 경우 evenOdd는 내부로 간주됩니다.
a line drawn from the point to infinity crosses an odd number of lines
우리의 타원은 어떤 선도 건너지 않기 때문에 0은 짝수로 간주되고 타원은 외부로 간주된다.
만약 당신이 깊이 이해하고 싶다면, 위키백과에서 더 많은 알고리즘에 대한 정보를 찾을 수 있습니다. here과here.
이것은 이해하기 쉽지 않기 때문에 나는 더욱 간단한 방법을 가지고 있다. 우리는 하위 경로와 조작 경로로 유형을 채울 필요가 없다. 단지 주위의 직사각형을 그리고 타원을 빼면 된다.
Path.combine(
PathOperation.difference,
Path()..addRect(
Rect.fromLTWH(0, 0, size.width, size.height)
),
Path()
..addOval(Rect.fromCircle(center: Offset(size.width -44, size.height - 44), radius: 40))
..close(),
);
나는 성능이 어떤지 모르겠지만, 가독성과 선명도로 말하자면, 나는 다음 방법이 더 좋다고 생각한다. 그러나 이것은 아마도 나의 개인적인 관점일 것이다.남은 유일한 일은 어떤 작은 위젯을 편집할지 결정하는 것이다.이런 상황에서 우리는 반투명한 스크린 충전 용기가 필요하다.
Widget _getOverlay() {
return ClipPath(
clipper: InvertedClipper(),
child: Container(
color: Colors.black54,
),
);
}
사용자 정의 화사
ClipPath
를 사용하여 반투명 덮어쓰기를 선택하고 구멍을 제외한 모든 내용을 그리기로 결정했습니다.CustomPainter
로 우리가 원하는 것만 그려보는 건 어때?좋은 소식은 위의 코드의 거의 모든 부분을 다시 사용할 수 있기 때문에 나는 즉시 결과를 나타낼 것이다.
class OverlayWithHolePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.black54;
canvas.drawPath(
Path.combine(
PathOperation.difference,
Path()..addRect(
Rect.fromLTWH(0, 0, size.width, size.height)
),
Path()
..addOval(Rect.fromCircle(center: Offset(size.width -44, size.height - 44), radius: 40))
..close(),
),
paint
);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
그러나 잠깐만, 만약 우리가 그것을 번역한다면 덮어쓰는 것을 볼 수 없을 것이다.왜?설정
CustomPaint
의 하위 속성 (아래에 그려진 모든 내용을 정의) 이 없으면 크기는 기본적으로 0입니다.이러한 상황에서 우리는size 속성을 수동으로 설정해야 합니다. 다음과 같습니다:size:MediaQuery.of(context).size
.두 가지 방법을 비교하다
클리파스
Widget _getOverlay() {
return ClipPath(
clipper: InvertedClipper(),
child: Container(
color: Colors.black54,
),
);
}
class InvertedClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
…
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => true;
}
사용자 정의 화사
Widget _getOverlay(BuildContext context) {
return CustomPaint(
size: MediaQuery.of(context).size,
painter: HolePainter()
);
}
class OverlayWithHolePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.black54;
canvas.drawPath(
…,
paint
);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
LOC에서는 클리버가 앞서고 있다.가독성에 관해서 나는 CustomPainter
방식을 더 좋아한다.어쨌든 이 두 가지 방법은 완전히 같은 결과를 초래할 것이다.아래쪽 작은 위젯에서 사용자의 제스처 이벤트를 수신하려면
IgnorePointer
에 모든 내용을 봉인해야 합니다.색상 필터링
세 번째 방법을 소개하고 싶습니다.흥미로운 것은 그림과 상호작용할 필요가 전혀 없다는 것이다.작은 위젯 트리에만 머물 수 있습니다.홍보하는 것 같지 않아요?
우리가 사용할 것은
ColorFiltered
작은 부품이다.말 그대로 이 작은 부품은 ColorFilter
하위 부품에 응용될 것이다.A color filter is a function that takes two colors, and outputs one color
이 두 색상은 먼저 사용자가 지정한 색상(ColorFilter.mode 구조 함수에 색상 속성이 있음)이고 그 다음은 하위 객체에 해당하는 픽셀 색상입니다.혼합 모드로 사용
BlendMode.srcOut
.다음과 같은 효과가 있습니다.Show the source image, but only where the two images do not overlap. The destination image is not rendered, it is treated merely as a mask. The color channels of the destination are ignored, only the opacity has an effect
우리의 예에서 원본 이미지는 색깔
Colors.black54
이고 목표는 우리가 하위 매개 변수로 제공한 모든 내용이다.따라서 기본적으로 반투명 덮어쓰기를 그렸습니다. 하위 창의 작은 위젯에서 투명도가 0보다 큰 모든 픽셀은 원본 이미지가 겹치는 곳에 그려지지 않기 때문에 구멍을 생성합니다.기본적으로 우리는 지금 알파 마스크가 하나 생겼다.Widget _getOverlay() {
return ColorFiltered(
colorFilter: ColorFilter.mode(
Colors.black54,
BlendMode.srcOut
),
child: Stack(
children: [
Container(
decoration: BoxDecoration(
color: Colors.transparent,
),
child: Align(
alignment: Alignment.bottomRight,
child: Container(
margin: const EdgeInsets.only(right: 4, bottom: 4),
height: 80,
width: 80,
decoration: BoxDecoration(
color: Colors.black, // Color does not matter but should not be transparent
borderRadius: BorderRadius.circular(40),
),
),
),
),
],
),
);
}
우리는 Container
과 투명한 배경색을 사용하여 덮어쓰기를 그립니다.겹치는 픽셀이 없기 때문에 전체 화면에 그려집니다Colors.black54
.우리는 타원형Container
을 다른 Container
에 넣어 구멍을 생성합니다.중요한 것은 이 작은 위젯은 불투명한 배경색을 가지고 있는데, 이것이 중첩되어 마스크가 모양을 그릴 수 없기 때문이다.결과적으로 우리는 모든 작은 부품을 용기에 넣을 수 있는데, 이것은 구멍을 만들 것이다.이것은 텍스트, 이미지 또는 기타 내용일 수 있습니다.
텍스트 힌트
이제 남은 것은 크리스털 공장의 목적을 묘사하는 힌트를 보여주는 것이다.우리는 다음과 같은 방법으로
_getHint
방법을 실현한다.Positioned _getHint() {
return Positioned(
bottom: 26,
right: 96,
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(
Radius.circular(4)
)
),
child: Row(
children: [
Text("You can add news pages with a tap"),
Padding(
padding: EdgeInsets.only(left: 8),
child: Icon(Icons.arrow_forward, color: Colors.black54,)
)
]
),
)
);
}
이 강좌에서 알 수 있듯이flatter가 작은 위젯의 부분을 표시하는 것을 방지할 수 있는 많은 방법이 있습니다.나는 ClipPath
, CustomPainter
, ColorFiltered
과 관련된 방법을 소개했다.개인의 선호와 용례에 따라 하나 또는 다른 작은 부품이 있을 수 있다.전체 코드는 mygist에서 볼 수 있습니다.
Reference
이 문제에 관하여(떨림: 어떻게 덮개층에 구멍을 잘라요), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/flutterclutter/flutter-how-to-cut-a-hole-in-an-overlay-a0텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)