Flutter sliver horizontal navigation

159 Views Asked by At

I am new to flutter development and trying to make a horizontal navigation bar that sticks to the top of the page when scrolling. I have worked out that I need to use a customScrollView with Slivers and have installed sliver_tools but I believe I have bitten off more than I can chew with my design.

My design consists of a medium sliver app bar displaying a few icons and a title. Then an image with the horizontal navigation positioned to the bottom of the image. (image can be seen below)

sliver + horizontal nav

Any help with getting the navigation overlaying the bottom of the image and sticky would be amazing. P.S. Any help with moving the pink bar under the selected navigation page would also be amazing

This is my clostest attempt to getting this working but of course, I am no way near.

import 'package:find_your_event/models/festival.dart';
import 'package:flutter/material.dart';
import 'package:sliver_tools/sliver_tools.dart';

class FestivalInformation extends StatelessWidget {
  const FestivalInformation({required this.festival, super.key});

  final Festival festival;
  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: [
        SliverAppBar.medium(
          pinned: true,
          title: Text(festival.name),
        ),
        MultiSliver(
          pushPinnedChildren: true,
          children: [
            SliverToBoxAdapter(
              child: Stack(
                children: [
                  Image(
                    image: NetworkImage(festival.bannerURL),
                  ),
                  SliverPinnedHeader(key: ValueKey('header'), child: Text('About')),
                ],
              ),
            ),
            SliverList(
              delegate: SliverChildListDelegate(
                [
                  Image(image: NetworkImage(festival.bannerURL)),
                  Container(
                    height: 900,
                    color: Colors.white,
                  )
                ],
              ),
            ),
          ],
        ),
      ],
    );
  }
}

1

There are 1 best solutions below

0
On

You can simply use the CustomSrollView with SliverAppBar with pinned property set to true and having a background image & a bottom child containing Tabbar widget which will stick to the top when scrolled. See this minimal example code on dartpad.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage();

  @override
  HomePageState createState() => HomePageState();
}

class HomePageState extends State<HomePage> with TickerProviderStateMixin {
  late TabController tabController;
  @override
  void initState() {
    super.initState();
    tabController = TabController(
      initialIndex: 0,
      length: 2,
      vsync: this,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: [
          SliverAppBar(
            expandedHeight: 200.0,
            floating: true,
            snap: true,
            pinned: true,
            flexibleSpace: FlexibleSpaceBar(
              title: const Text('Bottom Title'),
              background: Image.network(
                'https://images.pexels.com/photos/691668/pexels-photo-691668.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2',
                fit: BoxFit.cover,
              ),
            ),
            bottom: PreferredSize(
                preferredSize: const Size.fromHeight(60.0),
                child: ColoredBox(
                  color: Colors.grey.withOpacity(0.2),
                  child: TabBar(
                    controller: tabController,
                    tabs: const [
                      Tab(text: 'Tab 1'),
                      Tab(text: 'Tab 2'),
                    ],
                  ),
                )),
            leading: IconButton(
              icon: const Icon(Icons.arrow_back),
              onPressed: () {
                // Handle back button press
              },
            ),
            actions: [
              IconButton(
                icon: const Icon(Icons.more_vert),
                onPressed: () {
                  // Handle action button press
                },
              ),
            ],
          ),
          SliverToBoxAdapter(
            child: SizedBox(
              height: 600.0, // Adjust the height as needed
              child: Column(
                children: [
                  Expanded(
                    child: TabBarView(
                      controller: tabController,
                      children: [
                        // Content for Tab 1
                        ListView(
                          padding: const EdgeInsets.only(top: 20, bottom: 100),
                          children: List.generate(
                            10,
                            (i) => Container(
                              margin: const EdgeInsets.symmetric(vertical: 20),
                              color: Colors.grey,
                              child: Center(
                                child: Text('Tab 1 Content $i'),
                              ),
                            ),
                          ),
                        ),
                        // Content for Tab 2
                        ListView(
                          padding: const EdgeInsets.only(top: 20, bottom: 100),
                          children: List.generate(
                            10,
                            (i) => Container(
                              margin: const EdgeInsets.symmetric(vertical: 20),
                              color: Colors.greenAccent,
                              child: Center(
                                child: Text('Tab 2 Content $i'),
                              ),
                            ),
                          ),
                        )
                      ],
                    ),
                  ),
                ],
              ),
            ),
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate(
              (context, index) {
                return ListTile(title: Text('Item $index'));
              },
              childCount: 20,
            ),
          ),
        ],
      ),
    );
  }
}

Initial state
Scrolled State with sticky tabbar