0

I'm using CustomScrollView and sliver_tools package to display a list view with a SliverPinnedHeader.
In the SliverPinnedHeader, I have a tab view kind of layout where the user is allowed to select a tab and the content below the SliverPinnedHeader will change accordingly.

What I want to achieve
Now what I want is that when the user changes the tab, the content below the SliverPinnedHeader always start from the first element.

Current behaviour
Right now, on an active tab when the user scrolls down the complete list and if it is the longest list, then switching to another tab starts the another list from somewhere mid and not from the first element itself.

Demonstration code

import 'package:flutter/material.dart';
import 'package:sliver_tools/sliver_tools.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  MyApp({Key? key}) : super(key: key);

  final GlobalKey headerKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: HomePage());
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int active = 1;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('App bar')),
      body: CustomScrollView(
        slivers: [
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (context, index) {
                return ListTile(
                  leading: Text(index.toString()),
                );
              },
              childCount: 10,
            ),
          ),
          SliverPinnedHeader(
            child: Container(
              padding: const EdgeInsets.all(10),
              color: Colors.grey,
              child: Row(
                children: [
                  GestureDetector(
                    onTap: () {
                      setState(() {
                        active = 1;
                      });
                    },
                    child: Container(
                      decoration: BoxDecoration(
                        border: Border.all(
                          width: 2,
                          color: const Color(0xFF2F2F2F),
                        ),
                        color: active == 1 ? const Color(0xFF2F2F2F) : null,
                      ),
                      padding: const EdgeInsets.all(10),
                      child: Text(
                        '1',
                        style:
                            TextStyle(color: active == 1 ? Colors.white : null),
                      ),
                    ),
                  ),
                  const SizedBox(
                    width: 10,
                  ),
                  GestureDetector(
                    onTap: () {
                      setState(() {
                        active = 2;
                      });
                    },
                    child: Container(
                      decoration: BoxDecoration(
                        border: Border.all(
                          width: 2,
                          color: const Color(0xFF2F2F2F),
                        ),
                        color: active == 2 ? const Color(0xFF2F2F2F) : null,
                      ),
                      padding: const EdgeInsets.all(10),
                      child: Text(
                        '2',
                        style:
                            TextStyle(color: active == 2 ? Colors.white : null),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
          if (active == 1)
            SliverList(
              delegate: SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  return ListTile(
                    leading: Text((index).toString()),
                  );
                },
                childCount: 30,
              ),
            ),
          if (active == 2)
            SliverList(
              delegate: SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  return ListTile(
                    leading: Text((index).toString()),
                  );
                },
                childCount: 20,
              ),
            ),
        ],
      ),
    );
  }
}

Here when the first tab is selected, we show 30 list tiles, but when the second tab is selected, we show 20 list tiles. So when I scroll down the 30 list tiles and reach the end and when I switch to the second tab, the below list tiles start from somewhere mid whereas I want it to start from the first list tile.
Is there any possible way to achieve this?

dipansh
  • 331
  • 3
  • 15

1 Answers1

0

Do you know the position of your SliverPinnedHeader within the CustomScrollView?

If yes, you can add a ScrollController and whenever your variable active changes, animate or jump to that position.

Here is an example that you could make nicer (and the Listener in initState is obviously not needed, but there to show you what's going on):

import 'package:flutter/material.dart';
import 'package:sliver_tools/sliver_tools.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int active = 1;
  ScrollController controller = ScrollController();

  @override
  void initState() {
    controller.addListener(() {
      print(controller.position.pixels);
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('App bar')),
      body: CustomScrollView(
        controller: controller,
        slivers: [
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (context, index) {
                return SizedBox(
                  height: 50.0,
                  child: Center(child: Text(index.toString())),
                );
              },
              childCount: 10,
            ),
          ),
          SliverPinnedHeader(
            child: Container(
              padding: const EdgeInsets.all(10),
              color: Colors.grey,
              child: Row(
                children: [
                  GestureDetector(
                    onTap: () {
                      setState(() {
                        active = 1;
                        if (controller.position.pixels > 500) {
                          controller.animateTo(500.0,
                              duration: const Duration(milliseconds: 300),
                              curve: Curves.easeOut);
                        }
                      });
                    },
                    child: Container(
                      decoration: BoxDecoration(
                        border: Border.all(
                          width: 2,
                          color: const Color(0xFF2F2F2F),
                        ),
                        color: active == 1 ? const Color(0xFF2F2F2F) : null,
                      ),
                      padding: const EdgeInsets.all(10),
                      child: Text(
                        '1',
                        style:
                            TextStyle(color: active == 1 ? Colors.white : null),
                      ),
                    ),
                  ),
                  const SizedBox(
                    width: 10,
                  ),
                  GestureDetector(
                    onTap: () {
                      setState(() {
                        active = 2;
                        if (controller.position.pixels > 500) {
                          controller.animateTo(500.0,
                              duration: const Duration(milliseconds: 300),
                              curve: Curves.easeOut);
                        }
                      });
                    },
                    child: Container(
                      decoration: BoxDecoration(
                        border: Border.all(
                          width: 2,
                          color: const Color(0xFF2F2F2F),
                        ),
                        color: active == 2 ? const Color(0xFF2F2F2F) : null,
                      ),
                      padding: const EdgeInsets.all(10),
                      child: Text(
                        '2',
                        style:
                            TextStyle(color: active == 2 ? Colors.white : null),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
          active == 1
              ? SliverList(
                  delegate: SliverChildBuilderDelegate(
                    (BuildContext context, int index) {
                      return ListTile(
                        textColor: const Color(0xffcc0000),
                        leading: Text((index).toString()),
                      );
                    },
                    childCount: 30,
                  ),
                )
              : SliverList(
                  delegate: SliverChildBuilderDelegate(
                    (BuildContext context, int index) {
                      return ListTile(
                        leading: Text((index).toString()),
                      );
                    },
                    childCount: 20,
                  ),
                ),
        ],
      ),
    );
  }
}

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) => const MaterialApp(home: HomePage());
}
Stephan
  • 399
  • 1
  • 9