본문 바로가기
웹 & 앱 개발/Frontend 개발

[Flutter] Tab 좌우 슬라이드 화면 전환이 안되는 이유 & 해결 방법(DefaultTabController, TabBarView, RefreshIndicator, GestureDetector, SingleChildScrollView)

by newstellar 2023. 1. 11.
반응형

 

1. 문제

 

  아래처럼 TabBarView 위젯의 자식들로 CollectionMine과 CollectionRecommended을 두고, 터치 슬라이드를 통해 탭을 화면 전환하고 싶은 상황입니다. TabBarView는 DefaultTabController 부모로 감싸 별도로 tabController를 구성할 필요 없이 TabBar와 연결하였습니다.

 

/// Dart

TabBarView(
  physics: BouncingScrollPhysics(),
  children: [
    CollectionMine(),
    CollectionRecommended(),
  ],
),

 

 


2-1. 디버깅

 

  우리의 친구 https://chat.openai.com/chat 에게 물어봅니다.

 

Q. Sliding tab to tab horizontally in flutter using DefaultTabController is not working. why and how to fix it?

 

There can be several reasons why the sliding tabs feature is not working when using DefaultTabController. Here are a few possibilities:

1. TabController not being properly initialized: Make sure that the TabController is properly initialized and linked to the TabBar and TabBarView. In your case you are using DefaultTabController, but it's not being initialized in the correct context.

2. TabBarView children are not properly linked to the TabBar tabs: The children in the TabBarView need to match the order of the tabs in the TabBar. Make sure that the children in the TabBarView correspond to the tabs in the TabBar.

3. TabBarView children are not in the same tree with DefaultTabController: When using DefaultTabController, it should be an ancestor of both TabBar and TabBarView, and also make sure that the TabController is being passed down to the children via Builder widget if you are using them.

You can fix these issues by making sure to initialize DefaultTabController as the ancestor of both TabBar and TabBarView, and also making sure that the children in the TabBarView correspond to the tabs in the TabBar. Here's an example:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: DefaultTabController(
            length: 3,
            child: Scaffold(
                appBar: AppBar(
                    bottom: TabBar(
                        tabs: [
                            Tab(text: "Tab 1"),
                            Tab(text: "Tab 2"),
                            Tab(text: "Tab 3"),
                        ],
                    ),
                ),
                body: Builder(
                    builder: (context) => TabBarView(
                        controller: DefaultTabController.of(context),
                        children: [
                            Tab1(),
                            Tab2(),
                            Tab3(),
                        ],
                    ),
                ),
            ),
        ),
    );
  }
}​

Here, DefaultTabController is being set as the ancestor of both TabBar and TabBarView, and the TabBarView's controller is being set to the `Tab`.

  답변을 살펴봐도 제가 사용한 코드와 거의 유사하여 해결책을 찾지는 못했습니다. 두 가지 다른 점이 있다면 Builder를 TabBarView로 감싸주어 contex를 넘겨 controller를 assign하는 것인데요, 이 역시 해결책은 아니었습니다.


  이때, TabBarView의 자식들을 Text()로 구성하면 슬라이드가 되는 것을 확인했습니다. 본디 코드에서 children으로 지정한 CollectionMine에는 RefreshIndicator 위젯을 사용하는데요, 아래쪽으로 화면을 pull하여 refresh 함수를 지정하는 위젯인만큼 slide 동작에 영향을 미치지는 않을까 하는 생각에 이르렀습니다.

 

/// Dart

TabBarView(
  physics: BouncingScrollPhysics(),
  children: [
    Text("123"),
    Text("456"),
  ],
),

 

 

반응형

2-2. 디버깅

 

  우리의 친구 https://chat.openai.com/chat 에게 다시 물어봅니다.

 

Q. Sliding tab to tab horizontally in flutter using DefaultTabController is not working if one of TabBarViews is a RefreshIndicator or  a GestureDetector.

 

In Flutter, using a RefreshIndicator or a GestureDetector within a TabBarView can cause issues with scrolling between tabs when using a DefaultTabController. This is because the RefreshIndicator and GestureDetector widgets can consume the swipe gesture used to scroll between tabs. To work around this issue, you can wrap the TabBarView in a NotificationListener and listen for the ScrollEndNotification. When the notification is received, you can check if the user has swiped far enough to trigger a tab change and manually change the tab using the DefaultTabController.

Here is an example of how you might do this:
import 'package:flutter/material.dart';

class TabbedPage extends StatefulWidget {
  @override
  _TabbedPageState createState() => _TabbedPageState();
}

class _TabbedPageState extends State<TabbedPage> with SingleTickerProviderStateMixin {
  TabController _tabController;
  double _tabSwipeThreshold = 50.0;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(vsync: this, length: 2);
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        bottom: TabBar(
          controller: _tabController,
          tabs: <Widget>[
            Tab(text: 'Tab 1'),
            Tab(text: 'Tab 2'),
          ],
        ),
      ),
      body: NotificationListener<ScrollEndNotification>(
        onNotification: (ScrollEndNotification notification) {
          var dx = notification.metrics.pixels - notification.metrics.extentBefore;
          if (dx > _tabSwipeThreshold) {
            _tabController.animateTo(_tabController.index + 1);
          } else if (dx < -_tabSwipeThreshold) {
            _tabController.animateTo(_tabController.index - 1);
          }
          return true;
        },
        child: TabBarView(
          controller: _tabController,
          children: <Widget>[
            // Tab 1 content
            RefreshIndicator(
              child: ListView(...),
              onRefresh: () async {
                // ...
              },
            ),
            // Tab 2 content
            GestureDetector(
              child: Container(...),
              onTap: () {
                // ...
              },
            ),
          ],
        ),
      ),
    );
  }
}


You can adjust the value of _tabSwipeThreshold as you see fit based on the width of your tabs, or you can use Material Design Swipe Treshold. This is how you can use Material Design Swipe Treshold :

import 'package:flutter/material.dart';

class TabbedPage extends StatefulWidget {
  @override
  _TabbedPageState createState() => _TabbedPageState();
}

class _TabbedPageState extends State<TabbedPage> with Single

3. 해결

 

  원인과 해결책을 찾았습니다. 에상하던대로 RefreshIndicator 또는 GestureDetector로 인해 DefaultTabController의 슬라이드 동작과 충돌했던 것이 이유였습니다. 따라서 자식 위젯에 사용한 RefreshIndicator나 GestureDetector 자체를 빼버리거나, SingleChildScrollView로 감싸주면 됩니다.

 

 


 

 

 

[Firebase] Flutter에서 Firebase Cloud Messaging 구현 (iOS, Android, Push Notification, 파이어베이스 푸시알림메시

참고자료 - https://firebase.flutter.dev/docs/messaging/overview/ - https://firebase.google.com/docs/cloud-messaging - https://mookypoo.medium.com/flutter-fcm-푸쉬-알림-7e0699729462 - https://kanoos-stu.tistory.com/72 - https://arcanine33.github.io/

newstellar.tistory.com

 

[안드로이드/Android] scrcpy로 안드로이드 스마트폰 화면 컴퓨터에 미러링하기 (Mac OS 기준)

Android Studio 등의 IDE를 통해서 개발용 Simulator/Emulator를 컴퓨터에 띄우는 것은 가능합니다. 하지만 자신의 스마트폰을 PC 화면에 미러링시키고 싶다면 scrcpy를 설치해야 합니다. 본인의 Mac OS에 brew

newstellar.tistory.com

 

[GitHub] 깃허브 잔디 심는 법 - GitHub Contributions

깃허브를 하다 보면 어떤 날은 특별한걸 하지도 않은 것 같은데 contribution 수가 많고(진한 초록색) 또 어떤 날은 깃허브를 붙잡고 몇 시간을 썼는데도 contribution이 없거나 연한 초록색으로 남아

newstellar.tistory.com

 

반응형

댓글