Проблема с прокруткой Flutter: Пользовательская Sliver панель приложения и заголовок с вкладками и оставшимся виджетом Silver fill.

Вопрос или проблема

Вот мой код, я хочу реализовать настраиваемый заголовок, который будет подниматься, когда пользователь прокручивает. И прикрепить таббар к верхней части экрана, а в нижней части виджета будет показано содержимое. Для этого я использую Sliver Widgets.

Вот фрагменты кода.

class ProfileMyGroupDetailsScreen extends StatefulWidget {
  const ProfileMyGroupDetailsScreen({Key? key}) : super(key: key);

  @override
  State<ProfileMyGroupDetailsScreen> createState() => _ProfileMyGroupDetailsScreenState();
}

class _ProfileMyGroupDetailsScreenState extends State<ProfileMyGroupDetailsScreen> with SingleTickerProviderStateMixin {
  TabController? _tabController;

  @override
  void initState() {
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      _tabController = TabController(initialIndex: 0, length: 4, vsync: this);
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: CustomChatAppBar(
        title: AppLabels.myGroupDetails,
        systemOverlayStyle: Global.whiteOverLay,
        actions: [],
      ),
      body: buildDataTabWidget(),
    );
  }

  DefaultTabController buildDataTabWidget() {
    return DefaultTabController(
      length: 4,
      child: CustomScrollView(
        slivers: [
          SliverToBoxAdapter(
            child: Column(
              children: [
                ListTileType1(
                  padding: padXY(24, 8.5),
                  imageUrl: AppImages.dummyProfileImage1,
                  imageWidth: 44,
                  imageHeight: 44,
                  title1: 'Команда мемов',
                  title2: 'Джек и Крис',
                ),
                Padding(
                  padding: padXY(24, 16),
                  child: CustomImage(
                    imageUrl: AppImages.dummyProfileImage1,
                    height: MediaQuery.of(context).size.width - (24 * 2),
                    width: MediaQuery.of(context).size.width,
                    fit: BoxFit.cover,
                    borderRadius: 12,
                  ),
                ),
              ],
            ),
          ),
          SliverPersistentHeader(
            pinned: true,
            delegate: _SliverAppBarDelegate(
              TabBar(
                controller: _tabController,
                dividerColor: AppColors.kBorderColor,
                dividerHeight: 2,
                isScrollable: true,
                padding: EdgeInsets.zero,
                tabAlignment: TabAlignment.center,
                indicatorSize: TabBarIndicatorSize.tab,
                indicatorWeight: 1,
                labelColor: AppColors.kSecondaryColor,
                unselectedLabelColor: AppColors.kAppGrey,
                labelStyle: context.bodyLarge.w500,
                unselectedLabelStyle: context.bodyLarge,
                onTap: (index) {
                  // moduleDetailController.selectedIndex.value = index;
                  // moduleDetailController.selectedIndex.refresh();
                },
                tabs: [
                  Tab(text: AppLabels.basicInformation),
                  Tab(text: AppLabels.myLikes),
                  Tab(text: AppLabels.matches),
                  Tab(text: AppLabels.sentRequests),
                ],
              ),
            ),
          ),
          SliverFillRemaining(
            child: TabBarView(
              controller: _tabController,
              children: [
                BuildBasicInfoTab(),
                MyLikeTab(),
                MatchesTab(),
                SentRequestTab(),
              ],
            ),
          )
        ],
      ),
    );
  }
}

И я создал собственные методы делегата, чтобы прикрепить таббар.

class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  _SliverAppBarDelegate(this._tabBar);

  final TabBar _tabBar;

  @override
  double get minExtent => _tabBar.preferredSize.height;

  @override
  double get maxExtent => _tabBar.preferredSize.height;

  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    return Container(color: AppColors.kWhiteColor, child: _tabBar);
  }

  @override
  bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
    return false;
  }
}

Но основная проблема в том, что когда таббар закреплен вверху, затем он прокручивается под ним.

И тогда невозможно прокрутить контент вниз.

Пожалуйста, помогите мне, как мы можем добиться такого пользовательского интерфейса?

вставьте описание изображения здесь

Вместо CustomScrollView внутри DefaultTabController вам следует использовать NestedScrollView. Внутри headerSliverBuilder поместите ваш виджет CustomChatAppBar внутрь SliverAppBar, за которым следует виджет SliverPersistentHeader. Наконец, в теле добавьте виджет TabBarView.

Ответ или решение

Для решения вашей проблемы с использованием CustomScrollView и TabBar, я рекомендую заменить CustomScrollView на NestedScrollView. Это обеспечит правильное поведение при прокрутке, когда TabBar будет "прикреплен" к верхней части экрана, а контент будет прокручиваться под ним.

Вот как вы можете изменить ваш код:

class ProfileMyGroupDetailsScreen extends StatefulWidget {
  const ProfileMyGroupDetailsScreen({Key? key}) : super(key: key);

  @override
  State<ProfileMyGroupDetailsScreen> createState() => _ProfileMyGroupDetailsScreenState();
}

class _ProfileMyGroupDetailsScreenState extends State<ProfileMyGroupDetailsScreen> with SingleTickerProviderStateMixin {
  late TabController _tabController;

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: NestedScrollView(
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
          return <Widget>[
            SliverAppBar(
              title: CustomChatAppBar(
                title: AppLabels.myGroupDetails,
                systemOverlayStyle: Global.whiteOverLay,
                actions: [],
              ),
              pinned: true,
              floating: false,
              expandedHeight: 200.0, // высота вашего заголовка
              flexibleSpace: FlexibleSpaceBar(
                background: Container(
                  // Добавьте здесь ваш контент, который будет находиться в заголовке
                  child: Column(
                    children: [
                      ListTileType1(
                        padding: padXY(24, 8.5),
                        imageUrl: AppImages.dummyProfileImage1,
                        imageWidth: 44,
                        imageHeight: 44,
                        title1: 'Meme team',
                        title2: 'Jack &amp; Chris',
                      ),
                      Padding(
                        padding: padXY(24, 16),
                        child: CustomImage(
                          imageUrl: AppImages.dummyProfileImage1,
                          height: MediaQuery.of(context).size.width - (24 * 2),
                          width: MediaQuery.of(context).size.width,
                          fit: BoxFit.cover,
                          borderRadius: 12,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
            SliverPersistentHeader(
              pinned: true,
              delegate: _SliverAppBarDelegate(
                TabBar(
                  controller: _tabController,
                  isScrollable: true,
                  indicatorColor: AppColors.kSecondaryColor,
                  labelColor: AppColors.kSecondaryColor,
                  unselectedLabelColor: AppColors.kAppGrey,
                  tabs: [
                    Tab(text: AppLabels.basicInformation),
                    Tab(text: AppLabels.myLikes),
                    Tab(text: AppLabels.matches),
                    Tab(text: AppLabels.sentRequests),
                  ],
                ),
              ),
            ),
          ];
        },
        body: TabBarView(
          controller: _tabController,
          children: [
            BuildBasicInfoTab(),
            MyLikeTab(),
            MatchesTab(),
            SentRequestTab(),
          ],
        ),
      ),
    );
  }
}

class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  _SliverAppBarDelegate(this._tabBar);

  final TabBar _tabBar;

  @override
  double get minExtent => _tabBar.preferredSize.height;

  @override
  double get maxExtent => _tabBar.preferredSize.height;

  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    return Container(color: AppColors.kWhiteColor, child: _tabBar);
  }

  @override
  bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
    return false;
  }
}

Объяснение изменений

  1. Использование NestedScrollView: Это позволяет вам иметь прокручиваемый контент, который будет правильно вести себя вместе с SliverAppBar и TabBar.

  2. headerSliverBuilder: В этот метод я добавил SliverAppBar, который содержит ваш пользовательский заголовок. После этого добавляется SliverPersistentHeader для TabBar.

  3. TabBarView: Теперь он помещен в body NestedScrollView, что позволяет ему прокручиваться под прикрепленным TabBar.

Это изменение должно устранить проблемы с прокруткой вашего контента под закрепленным TabBar, позволяя пользователю беспрепятственно взаимодействовать с содержимым.

Оцените материал
Добавить комментарий

Капча загружается...