본문 바로가기
flutter

[flutter] tapbar 하위 위젯 리렌더링시 스크롤 고정

by 슈크림 붕어빵 2023. 7. 13.

첫번째는 scrollController를 사용했다. 

ScrollController _scrollController = ScrollController();
GestureDetector(
                          onTap: () {
                            
                          },
                          child: Container(
                            color: menuBar,
                            height: 45,
                            key: indexKey,
                            child: Row(
                              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                              children: [
                                GestureDetector(
                                  onTap: () {
                                     final double scrollPosition =
                                _scrollController.position.pixels;

                            // 상태 변경 및 리렌더링
                           			setState(() {
                                      index = 0;
                                    });

                            // 스크롤 위치 복원
                            _scrollController.jumpTo(scrollPosition);
                                   
                                  },
                                  child: Text("상세정보",
                                      style: index == 0
                                          ? TextStyle(
                                              fontWeight: FontWeight.bold,
                                              fontSize: 15,
                                              color: mallMainColor,
                                            )
                                          : TextStyle(
                                              fontWeight: FontWeight.bold,
                                              fontSize: 15,
                                              color: mallArrowColor,
                                            )),
                                ),
                                VerticalDivider(
                                  thickness: 1,
                                  indent: 15, //Spacing at the top of divider.
                                  endIndent:
                                      15, //Spacing at the bottom of divider.
                                ),
                                GestureDetector(
                                  onTap: () {
                                    final double scrollPosition =
                                _scrollController.position.pixels;

                            // 상태 변경 및 리렌더링
                           			setState(() {
                                      index = 1;
                                    });

                            // 스크롤 위치 복원
                            _scrollController.jumpTo(scrollPosition);
                                  },
                                  child: Text(
                                    "리뷰 ${detail.reviewCount}",
                                    style: index == 1
                                        ? TextStyle(
                                            fontWeight: FontWeight.bold,
                                            fontSize: 15,
                                            color: mallMainColor,
                                          )
                                        : TextStyle(
                                            fontWeight: FontWeight.bold,
                                            fontSize: 15,
                                            color: mallArrowColor,
                                          ),
                                  ),
                                ),
                                VerticalDivider(
                                  thickness: 1,
                                  indent: 15, //Spacing at the top of divider.
                                  endIndent:
                                      15, //Spacing at the bottom of divider.
                                ),
                                GestureDetector(
                                  onTap: () {
                                     final double scrollPosition =
                                _scrollController.position.pixels;

                            // 상태 변경 및 리렌더링
                           			setState(() {
                                      index = 2;
                                    });

                            // 스크롤 위치 복원
                            _scrollController.jumpTo(scrollPosition);
                                  },
                                  child: Text(
                                    "문의하기 ${detail.inquireCount}",
                                    style: index == 2
                                        ? TextStyle(
                                            fontWeight: FontWeight.bold,
                                            fontSize: 15,
                                            color: mallMainColor,
                                          )
                                        : TextStyle(
                                            fontWeight: FontWeight.bold,
                                            fontSize: 15,
                                            color: mallArrowColor,
                                          ),
                                  ),
                                ),
                              ],
                            ),
                          ),
                        )

스크롤 위치를 저장하고 다시 그 위치로 돌아가는 방법이다. 

근데 안됐다. 위젯이 꼬여있어서 그런가..

 

 

글로버키도 사용했다. 이는 탭바를 누를 경우 탭바를 맨 상단으로 이동시키는 방법이다.

  final GlobalKey indexKey = GlobalKey();
GestureDetector(
                          onTap: () {
                            Scrollable.ensureVisible(
                                indexKey
                                    .currentContext!,
                                duration: Duration(seconds: 1),
                                curve: Curves.easeInOut,
                                alignment: 0);
                          },
                          child: Container(
                            color: menuBar,
                            height: 45,
                            key: indexKey,
                            child: Row(
                              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                              children: [
                                GestureDetector(
                                  onTap: () {
                                    setState(() {
                                      index = 0;
                                    });
                                  },
                                  child: Text("상세정보",
                                      style: index == 0
                                          ? TextStyle(
                                              fontWeight: FontWeight.bold,
                                              fontSize: 15,
                                              color: mallMainColor,
                                            )
                                          : TextStyle(
                                              fontWeight: FontWeight.bold,
                                              fontSize: 15,
                                              color: mallArrowColor,
                                            )),
                                ),
                                VerticalDivider(
                                  thickness: 1,
                                  indent: 15, //Spacing at the top of divider.
                                  endIndent:
                                      15, //Spacing at the bottom of divider.
                                ),
                                GestureDetector(
                                  onTap: () {
                                    setState(() {
                                      index = 1;
                                    });
                                  },
                                  child: Text(
                                    "리뷰 ${detail.reviewCount}",
                                    style: index == 1
                                        ? TextStyle(
                                            fontWeight: FontWeight.bold,
                                            fontSize: 15,
                                            color: mallMainColor,
                                          )
                                        : TextStyle(
                                            fontWeight: FontWeight.bold,
                                            fontSize: 15,
                                            color: mallArrowColor,
                                          ),
                                  ),
                                ),
                                VerticalDivider(
                                  thickness: 1,
                                  indent: 15, //Spacing at the top of divider.
                                  endIndent:
                                      15, //Spacing at the bottom of divider.
                                ),
                                GestureDetector(
                                  onTap: () {
                                    setState(() {
                                      index = 2;
                                    });
                                  },
                                  child: Text(
                                    "문의하기 ${detail.inquireCount}",
                                    style: index == 2
                                        ? TextStyle(
                                            fontWeight: FontWeight.bold,
                                            fontSize: 15,
                                            color: mallMainColor,
                                          )
                                        : TextStyle(
                                            fontWeight: FontWeight.bold,
                                            fontSize: 15,
                                            color: mallArrowColor,
                                          ),
                                  ),
                                ),
                              ],
                            ),
                          ),
                        )

사용법은 이렇다. 글로벌키를 통해 원하는 위젯의 위치를 기억한 다음, 이벤트가 발생하면 그 위치를 찾아가는 방법이다.

 

duration은 거기까지 가는 시간을 결정한다. 이를 통해 얼마나 빠른 속도로 스크롤해서 이동할지 결정한다.
alignment: 0은 이 위젯을 화면 맨 위에 맞춘다는 의미이다. 1이라면 맨 아래에 맞도록 맞춘다. 

 

 

 

해결한 방법은 그 부분은 건들지 않고 리렌더링되는 위젯을 stackindex로 감싸줘서 해결했다.

 

before

if (index == 0) DetailInfo(detail: _productDetail),
                        if (index == 1)
                          ReviewInfo(
                            rating: _rating,
                            product_img: "assets/icons/product_default.png",
                            product_code: widget.productCode,
                          ),
                        if (index == 2)
                          InqueryInfo(
                            widget: widget,
                          )

after

IndexedStack(
                          index: index,
                          children: [
                            DetailInfo(detail: _productDetail),
                            ReviewInfo(
                              rating: _rating,
                              product_img: "assets/icons/product_default.png",
                              product_code: widget.productCode,
                            ),
                            InqueryInfo(
                              widget: widget,
                            ),
                          ],
                        ),
                      ],

 

 

구조는 아래와 같다. 필요했던 이유는 리뷰를 선택하거나 문의하기를 선택할 때 index가 바뀌면서 아래 정보를 위한 위젯이 리렌더링되고 바뀌는데 그에 따라 화면이 자꾸 처음 상태로 움직여서 화면 전체가 새로고침되는 느낌이었기 때문이다. 바뀜으로써, 같은 화면에서 하단 위젯만 바뀌는 것처럼 잘 해결되었다. 다음으로는 상세정보, 리뷰, 문의하기가 있는 바가 화면 밖으로 벗어난다면 상단에 고정하는 방법을 알아볼 것이다. index가 바뀔 때 하위 위젯만 리렌더링되기 때문에 가능하다.