Search code examples
flutterdartgesturetabbarsliverappbar

Flutter appbar with image, customized containers and tabbar


The screen and when scrolled. I have used dependency appbar_animated: ^0.0.3 for this effect, so when user scrolls then the image and my custom container which has info about the restaurant disappears and it becomes a normal app bar. Now the problem is I want a tabbar below all of these like this screenshot and when user scrolls the tabbar attaches to the appbar like this screenshot.

In Summary i want this

gif

This is my code so far

// ignore_for_file: prefer_const_constructors, prefer_const_literals_to_create_immutables

import 'package:appbar_animated/appbar_animated.dart';

import 'package:flutter/material.dart';
import 'package:firebase_getx/widgets/expanded_restaurant.dart';

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

  @override
  State<TalabadScreen3> createState() => _TalabadScreen3State();
}

class _TalabadScreen3State extends State<TalabadScreen3> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey[200],
      body: ScaffoldLayoutBuilder(
        backgroundColorAppBar:
            const ColorBuilder(Colors.transparent, Colors.white),
        textColorAppBar: const ColorBuilder(Colors.white),
        appBarBuilder: appbar,
        child: SingleChildScrollView(
          child: Stack(
            children: [
              Image.asset(
                "img/cover.jpg",
                width: MediaQuery.of(context).size.width,
                height: MediaQuery.of(context).size.height * 0.35,
                fit: BoxFit.cover,
              ),
              Container(
                margin: EdgeInsets.only(
                  top: MediaQuery.of(context).size.height * 0.31,
                ),
                height: 900,
                decoration: const BoxDecoration(
                  borderRadius: BorderRadius.vertical(),
                  color: Colors.white,
                ),
                child: Container(
                  margin: EdgeInsets.only(
                    top: MediaQuery.of(context).size.height * 0.15,
                  ),
                ),
              ),
              Positioned(
                top: MediaQuery.of(context).size.height * 0.22,
                left: 10,
                right: 10,
                child: restaurantInfoContainer(),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

I have also tried this screenshot and when scrolled scrolledScreenshot. The problem with this is that i don't know how to add background image when expaned and how to place my restaurantInfoContainer .

this is the code:

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

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

class _SliverTabExampleState extends State<SliverTabExample>
    with SingleTickerProviderStateMixin {
  final _tabs = List.generate(10, (index) => 'Tab#${index + 1}');

  late final TabController _tabCont;
  @override
  void initState() {
    _tabCont = TabController(length: 10, vsync: this);
    super.initState();
  }

  bool isVis = true;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NestedScrollView(
        headerSliverBuilder: (_, __) {
          return [
            SliverAppBar(
              backgroundColor: Colors.transparent,
              scrolledUnderElevation: 0.0,
              elevation: 0,
              leading: CircleAvatar(
                backgroundColor: Colors.white,
                child: IconButton(
                  onPressed: () {
                    Navigator.pop(context);
                  },
                  icon: Icon(
                    Icons.arrow_back,
                    color: Colors.black87,
                    size: 30,
                  ),
                ),
              ),

              actions: [
                CircleAvatar(
                  radius: 28,
                  backgroundColor: Colors.white,
                  child: IconButton(
                    padding: EdgeInsets.all(0),
                    icon: Icon(
                      Icons.ios_share_rounded,
                      color: Colors.black87,
                      size: 30,
                    ),
                    onPressed: () {},
                  ),
                ),
                CircleAvatar(
                  radius: 28,
                  backgroundColor: Colors.white,
                  child: IconButton(
                    padding: EdgeInsets.all(0),
                    icon: Icon(
                      Icons.search,
                      weight: 0.001,
                      color: Colors.black87,
                      size: 30,
                    ),
                    onPressed: () {},
                  ),
                ),
              ],

              expandedHeight: 300,
              // collapsedHeight: 100,

              pinned: true,
              floating: false,
              // title: Text('TabBar Example'),
              centerTitle: true,
              bottom: TabBar(
                controller: _tabCont,
                isScrollable: true,
                tabs: [
                  ..._tabs.map(
                    (label) => Tab(
                      child: Text(label),
                    ),
                  ),
                ],
              ),
            ),
          ];
        },
        body: TabBarView(
          controller: _tabCont,
          children: [
            ..._tabs.map(
              (label) => SamplePage(
                label: label,
              ),
            ),
          ],
        ),
      ),
    );
  }
}


Solution

  • I have actually solved the problem without using third party dependencies. I have used flexibleSpace: of SliverAppBar to place restaurant's cover photo and my customContainer(restaurantInfoContainer).

    This is the solution:

    class TalabadScreen3 extends StatefulWidget {
      const TalabadScreen3({super.key});
    
      @override
      State<TalabadScreen3> createState() => _TalabadScreen3State();
    }
    
    class _TalabadScreen3State extends State<TalabadScreen3>
        with SingleTickerProviderStateMixin {
      final _tabs = List.generate(10, (index) => 'Tab#${index + 1}');
    
      late final TabController _tabCont;
      @override
      void initState() {
        _tabCont = TabController(length: 10, vsync: this);
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: NestedScrollView(
            headerSliverBuilder: (_, __) {
              return [
                SliverAppBar(
                  backgroundColor: Colors.white,
                  scrolledUnderElevation: 0.0,
                  elevation: 0,
                  automaticallyImplyLeading: false,
                  leading: CircleAvatar(
                    backgroundColor: Colors.white,
                    child: IconButton(
                      onPressed: () {
                        Navigator.pop(context);
                      },
                      icon: const Icon(
                        Icons.arrow_back,
                        color: Colors.black87,
                        size: 25,
                      ),
                    ),
                  ),
                  flexibleSpace: FlexibleSpaceBar(
                      collapseMode: CollapseMode.pin,
                      background: Stack(
                        children: [
                          Image.asset(
                            "img/cover.jpg",
                            width: MediaQuery.of(context).size.width,
                            height: MediaQuery.of(context).size.height * 0.3,
                            fit: BoxFit.cover,
                          ),
                          Positioned(
                            top: MediaQuery.of(context).size.height * 0.18,
                            left: 10,
                            right: 10,
                            child: restaurantInfoContainer(),
                          ),
                        ],
                      )),
                  actions: [
                    Container(
                      padding: const EdgeInsets.symmetric(horizontal: 15),
                      decoration: const BoxDecoration(
                        color: Colors.white,
                        shape: BoxShape.circle,
                      ),
                      child: IconButton(
                        padding: const EdgeInsets.all(0),
                        icon: const Icon(
                          Icons.ios_share_rounded,
                          color: Colors.black87,
                          size: 25,
                        ),
                        onPressed: () {},
                      ),
                    ),
                    Container(
                      decoration: const BoxDecoration(
                        color: Colors.white,
                        shape: BoxShape.circle,
                      ),
                      child: IconButton(
                        padding: const EdgeInsets.all(0),
                        icon: const Icon(
                          Icons.search,
                          weight: 0.001,
                          color: Colors.black87,
                          size: 25,
                        ),
                        onPressed: () {},
                      ),
                    ),
                  ],
    
                  expandedHeight: MediaQuery.of(context).size.height * 0.45,
                  collapsedHeight: 70,
    
                  pinned: true,
                  floating: false,
                  // title: Text('TabBar Example'),
                  centerTitle: true,
                  bottom: TabBar(
                    controller: _tabCont,
                    isScrollable: true,
                    tabs: [
                      ..._tabs.map(
                        (label) => Tab(
                          child: Text(label),
                        ),
                      ),
                    ],
                  ),
                ),
              ];
            },
            body: TabBarView(
              controller: _tabCont,
              children: [
                ..._tabs.map(
                  (label) => SamplePage(
                    label: label,
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }