트러블 등장
개인 프로젝트 풀밭을 개발하며 CampusView의 UI와 기능을 개발하였는데 문제가 생겼다. 자세한 개발 내용은 위의 링크를 누르면 볼 수 있다.
CampusView는 DynamicTabBar on Scroll으로 Scroll과 TabBar를 서로 연동하여 Tab을 눌렀을 때 자동으로 스크롤이 되거나 혹은 스크롤을 하였을때 Tab이 자동으로 변경되는 기능이 있다.
이 때 발생한 문제는 Tab을 눌러 스크롤이 자동으로 설정된 값을 스크롤 될 때 다른 Tab을 누르게 되면 scrollToIndex함수가 중복 실행이 되어 코드가 꼬이는 문제가 발생하였다. 한 번 어떤 상황인지 화면을 봐보자.
예시 화면
스크롤 이동이 끝난 후 Tab을 눌러 잘 작동 되는 모습 , 스크롤 이동이 끝나기 전 Tab을 눌러 오류가 있는 화면
문제 해결 과정
1. 스크롤을 하는 도중에는 Tab을 못 누르게 하자.
TabBar 코드를 보면 onTap시 해당 Tab의 index를 scrollToIndex함수에 파라미터 값으로 넣고 실행이 된다.
그러면 scrollToIndex가 실행한다면 bool isRunning을 true로 하고 실행이 끝나면 false로 바꾸자.
scrollToIndex함수는 milliseconds: 600 duration이니 onTapDynamicTabBar 함수를 만들어 지연을 시키자!
// TabBar 코드
TabBar(
tabs: [
1번 텍스트 탭,
2번 텍스트 탭,
3번 텍스트 탭,
],
// Tap시 실행되는 함수.
onTap: (int index) => scrollToIndex(index),
),
/// Animate To Tab
void animateToTab() {
late RenderBox box;
for (var i = 0; i < campusCategories.length; i++) {
box = campusCategories[i].currentContext!.findRenderObject() as RenderBox;
Offset position = box.localToGlobal(Offset.zero);
if (scrollController.offset >= position.dy) {
DefaultTabController.of(tabContext!).animateTo(
i,
duration: const Duration(milliseconds: 100),
);
}
}
}
/// Scroll to Index
void scrollToIndex(int index) async {
scrollController.removeListener(animateToTab);
final categories = campusCategories[index].currentContext!;
await Scrollable.ensureVisible(
categories,
duration: const Duration(milliseconds: 600),
);
scrollController.addListener(animateToTab);
}
1.1 onTapDynamicTabBar 함수 생성
isRunning 변수값을 스크롤이 이동되는 시간만큼 지연시켜 변경해주어 에러를 방지한다. 입력 후 0.6초를 기다리고 0.6초동안 스크롤이 실행되다보니 화면 이동이 느려 0.1초로 변경하였다.
// TabBar 코드
TabBar(
tabs: [
1번 텍스트 탭,
2번 텍스트 탭,
3번 텍스트 탭,
],
// Tap시 실행되는 함수.
onTap: (int index) => onTapDynamicTabBar(index), // 함수 변경
),
void onTapDynamicTabBar(int index) {
setState(() {
isRunning = true; // 실행 중 상태로 설정
});
// 비동기적으로 잠시 대기 후 scrollToIndex 호출
Future.delayed(const Duration(milliseconds: 100), () { // 0.1초로 변경
if (isRunning) {
scrollToIndex(index); // index로 스크롤
setState(() {
isRunning = false; // 실행 종료 상태로 설정
});
}
});
}
1.2 **AbsorbPointer 클래스** 생성
isRunning이 true일때 화면 입력을 감지하지만 무시할 수 있는 **AbsorbPointer** 사용하여 실행중에 화면입력을 방지한다. 사실 지연을 시켜 입력을 무시할 필요는 없지만 혹시 모를 에러를 대비하여 AbsorbPointer를 사용하였다.
if (isRunning)
AbsorbPointer(
absorbing: true, // 터치 이벤트 차단
child: Container(
color: Colors.black.withOpacity(0.2), // Test 예시 투명으로 처리해야함.
child: Center(
child: Text(
"Running Mode",
style: TextStyle(color: Colors.white, fontSize: 20),
),
),
),
),
1.3 결과
아무리 빠르게 Tap을 해도 방지를 하니 입력이 안되어서 코드가 중복 실행이 되는 상황은 없다.
2. 새로운 문제 발견
그렇게 문제를 해결한 줄 알고 다른 것을 테스트해보니 새로운 문제가 발생하였다..! 바로 스크롤 도중에 Tab을 누르면 TabBar 이동이 버그가 난다는 것.
TabBar는 스크롤이 되고 있으니 스크롤 값을 감지하며 이동하고 있는데 갑자기 다른 Tab이 눌리면서 문제가 발생하였다.. 나는 스크롤과 동시에 텝이 진행되어 문제가 생긴 줄 알았다.
2.1 스크롤을 감지하자
이 문제를 해결하기 위해 여러 방법을 적용시켜 봤다. 처음에는 scrollController 클래스만 사용하여 문제를 해결하려고 하였는데 원하는대로 동작하지 않아 PointerEvent를 사용했었다. PointerEvent는 사용자의 입력을 잘 감지하였지만 문제는 관성이었다. 스크롤은 Swipe 제스처를 한다면 스크롤이 속도에 비례하여 자동으로 움직이는 특징있다. 이것 때문에 PointerEvent는 포기하였다.
그래서 scrollController.addListener() 를 사용하여 스크롤 이벤트가 발생시 **isScrolling**을 true로 하여 스크롤을 감지하였다.
2.2 스크롤시 TabBar 사용 금지
TabBar에만 AbsorbPointer를 사용하여 사용자경험을 향상시켰다. 아래 화면을 보면 스크롤은 계속 가능하고 Tab만 안되는 것을 볼 수 있다.
3. 최종 문제 해결
그런데 자꾸 테스트를 해보니 1.1onTapDynamicTabBar 함수를 사용하지 않는다면 2. 새로운 문제 발견 문제가 생기지 않는 것,, 원인은 1.1onTapDynamicTabBar함수는 지연을 시키게 되어 꼬이게 되는 것이었다. 결국 스크롤 이동시간만 milliseconds 100으로 설정한다면 빠르게 이동하여 모든 문제가 해결된것,,
결론
결국 문제를 해결하는 과정에서 오히려 더 문제를 만들어버렸고 간단하게 수정할 수 있는 것을 돌아왔다… 앞으로 문제 원인을 정확히 파악하는 것이 제일 중요하다고 생각한다.
기타
AbsorbPointer
- 공식문서에 보면 hitTesting 중에 Tap을 흡수하는 위젯이다.
- 만약 Tap을 감지한다면 하위 트리가 Pointer Event를 수신하지 못하도록 한다. 즉, Tap을 감지하지만 무시할 수 있다는 이야기
'Flutter' 카테고리의 다른 글
[Flutter] Dynamic TabBar on ScrollView & NaverMap URL Scheme 사용 방법 (0) | 2024.11.22 |
---|---|
[Flutter] WhiteScreen 해결(iPhone 무선빌드) & Load Sequence Flutter UI (3) | 2024.11.21 |
[피플] 디자인 QA (0) | 2023.02.24 |
[피플]플러터 Google Map Api 사용 방법 (0) | 2023.02.20 |
[피플] 응원해요 기능 수정 (0) | 2023.02.08 |