앱/Flutter

[Flutter] iOS의 scroll to top 기능은 어떻게 구현되어 있는가 (feat. 안될 때 확인해볼 것!)

paran21 2023. 7. 18. 17:51

최초작성일 : 2022년 11월 12일

1. ios의 scroll to top 기능이 flutter에서 어떻게 구현되어 있는가

1. Scaffold

안드로이드와 달리 ios에서는 화면 상단에 있는 status bar를 누르면 가장 위로 화면이 스크롤되는 기능이 있다.
flutter에서는 특별하게 이 기능을 구현하지 않아도 ios에서는 적용이 되는데, 그 이유는 `Scaffold`에 해당 기능이 구현되어 있기 때문이다.

 

class Scaffold extends StatefulWidget {
...
  void _handleStatusBarTap() {
    final ScrollController? primaryScrollController = PrimaryScrollController.of(context);
    if (primaryScrollController != null && primaryScrollController.hasClients) {
      primaryScrollController.animateTo(
        0.0,
        duration: const Duration(milliseconds: 1000),
        curve: Curves.easeOutCirc,
      );
    }
  }
...
}

이 코드를 살펴보면 ios의 scroll to top 기능은 PrimaryScrollController를 사용해서 _handleStatusBarTap()이 실행되면 animateTo(0.0) 즉 화면의 가장 위쪽으로 이동하는 식으로 코드가 구현되어있다는 것을 알 수 있다.

 

2. PrimaryScrollController

여기서 사용하는 PrimaryScrollController공식문서를 참고하면 다음과 같이 설명되어 있다.

... A ScrollView that doesn't have a controller or the primary flag set will inherit the PrimaryScrollController, ...
Inheriting this ScrollController can provide default behavior for scroll views in a subtree. For example, the Scaffold uses this mechanism to implement the scroll-to-top gesture on iOS.

PrimaryScrollController는 ScrollView가 컨트롤러가 없을 때 상속받아 사용하며, ios의 scroll-to-top 등 ScrollView의 기본적인 기능을 수행한다.

그래서 flutter에서는 별도로 기능을 구현하지 않아도, ios에서는 scroll to top 기능이 적용된다. 하지만 scroll to top이 동작하지 않을 때도 있다.

 

2. scroll to top이 동작하지 않을 때 확인해볼 것

1. Widget이 Scaffold에 child인지

앞에서 살펴봤듯이, scoll to top은 Scaffold에 구현되어 있다. 따라서 Widget이 Scaffold의 child가 아니라면 기능이 동작하지 않을 수 있다.

 

2. ScrollController가 있는 경우

무한스크롤 등 ScrollController를 별도로 선언하여 기능을 사용하는 경우에는 PrimaryScrollController가 아니라 새로 만든 ScrollController를 사용하기 때문에 scroll to top 기능이 동작하지 않는다.
이 경우에는 PrimaryScrollController가 controller로 사용되도록 코드를 수정해야 scroll to top이 동작한다.

...
child: CustomScrollView(
      ...
      controller: PrimaryScrollController.of(context),
      ...
      )
...

이렇게 코드를 수정하면 `PrimaryScrollController`를 사용하기 때문에 scroll to top이 동작한다.

만약에 무한스크롤 등 Listener를 사용하고 있었다면 다음과 같이 코드를 수정할 수 있다.

... 
return NotificationListener(
  onNotification: (ScrollNotification scrollNotification) {
    final maxScroll = scrollNotification.metrics.maxScrollExtent;
    final currentScroll = scrollNotification.metrics.pixels;
    final delta = MediaQuery.of(context).size.height * 0.50;
    if (maxScroll - currentScroll <= delta) {
      loadCardList(loadMore: true);
    }
    return false;
  },
  child: ...
...

NotificationListener을 사용해서 Listener의 콜백 함수를 등록할 수 있고, scroll이 가능한 위젯에서는 ScrollNotification을 매개변수로 받아서 무한스크롤을 구현하는데 필요한 스크롤 위치 정보를 알 수 있다.
이렇게하면 별도로 ScrollController가 없이도 무한스크롤을 구현할 수 있다.

 

참고자료

https://velog.io/@den/Flutter
https://velog.io/@sangh518/Flutter-NotificationListener

 

3. SafeArea가 Scaffold의 parent일 때

(23.1.13. 추가)
최근에 새로 만든 페이지에서만 scroll to top이 동작하지 않는 문제가 있었다.
정상적으로 동작하는 다른 페이지와 차이를 살펴보다가 발견했는데, SafeAreaScaffold의 parent일 때 scroll to top이 동작하지 않는다.

더 구체적으로 말하면 scroll to top이 동작하기 위해서는 Scaffold 빌드 과정에서 Scaffoldstatusbar_handleStatusBarTap() 메서드가 등록된다.

그런데 SafeArea가 parent가 되면 statusbar는 위젯의 범위에서 제외되기 때문에 동작하지 않는 것 같다.

 

(유의사항) hot reload!!!

scroll to top는 hot reload에서 제대로 동작하지 않을 수 있다.

참고

 

[IOS] Scroll To Top by tapping on status bar with CupertinoPageScaffold doesn't work sometimes until the app is killed. · Issue

This is a duplicate of #24777, since it is closed with no references but the issue still persists. Steps to Reproduce Run the code sample on an iOS device. Tap the status bar. Expected results: The...

github.com

이슈가 닫혀있기는 한데 최신 버전(3.3.7)에서도 코드 수정 후 hot reload에서는 제대로 동작하지 않았다.
완전히 시뮬레이터를 끄고 다시 실행해서 테스트를 하는 게 좋을 것 같다!