Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a correct way to switch between two widgets (app bars in my case) by swiping up and down?

I'm currently working on a page in my Flutter app and facing challenges implementing a specific logic. When the app launches, I want the page to initially display the 'appbardown' widget (the larger one) and remain in that state until a user decides to swipe upwards or scroll through the list of 'Ones' within one of the tabs.

Currently, when I attempt to swipe upwards, the widget shifts to the 'appbarup' widget, but the transition is neither smooth nor efficient. Additionally, when I try to scroll the list of 'Ones,' I want the 'appbardown' widget to smoothly transition to the 'appbarup' widget before allowing me to scroll the list.

Conversely, when I swipe downward, the widget should shift from 'appbarup' to 'appbardown.' Similarly, when I scroll downwards in the list of 'Ones,' the appbar should shift accordingly.

If you have any insights or a better solution for achieving this functionality, I would greatly appreciate your assistance.

import 'package:flutter/material.dart';

import 'widgets/quick_links.dart';

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

  @override
  State<TempScreen> createState() => _TempScreenState();
}

class _TempScreenState extends State<TempScreen> {
  final controller = PageController(initialPage: 1);
  bool isSecondOrder = false;

  @override
  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;
    return Scaffold(
      body: GestureDetector(
        onVerticalDragEnd: (details) {
          if (details.primaryVelocity! > 0) {
            // Swiped downwards
            setState(() {
              isSecondOrder = false;
            });
          } else if (details.primaryVelocity! < 0) {
            //Swiped Upwards
            setState(() {
              isSecondOrder = true;
            });
          }
        },
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            AnimatedSwitcher(
              switchInCurve: Curves.easeInOut, // Use your preferred curve
              switchOutCurve: Curves.easeInOut, // Use your preferred curve
              transitionBuilder: (child, animation) {
                return Container(
                  color: Theme.of(context).primaryColor,
                  child: FadeTransition(
                    opacity: animation,
                    child: child,
                  ),
                );
              },
              duration: const Duration(milliseconds: 1),
              child: isSecondOrder
                  ? AppBarUpt(size: size)
                  : AppBarDownt(size: size),
            ),
            Padding(
              padding: EdgeInsets.only(
                  left: size.width * 0.05, top: size.height * 0.035),
              child: const Text(
                'Quick Links',
                style: TextStyle(
                  fontSize: 20.0,
                  color: Colors.black,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
            SingleChildScrollView(
              scrollDirection: Axis.horizontal,
              child: Padding(
                padding: EdgeInsets.only(
                  left: size.width * 0.05,
                ),
                child: const Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Text('A'),
                    Text('B'),
                    Text('C'),
                    Text('D'),
                    Text('E'),
                  ],
                ),
              ),
            ),
            Expanded(
              child: Padding(
                padding: EdgeInsets.only(
                  top: 20,
                  left: size.width * 0.04,
                  right: size.width * 0.04,
                ),
                child: DefaultTabController(
                  length: 3, // Number of tabs
                  child: Column(
                    children: [
                      Padding(
                        padding: EdgeInsets.only(
                          // top: 20,
                          left: size.width * 0.04,
                          right: size.width * 0.04,
                        ),
                        child: const TabBar(
                          indicatorWeight:
                              4.0, // Adjust indicator weight as needed
                          indicatorColor: Colors.black,
                          // labelPadding: EdgeInsets.symmetric(horizontal: 16.0),
                          labelStyle: TextStyle(
                            fontSize: 18.0,
                            fontWeight: FontWeight.w700,
                          ),
                          tabs: [
                            Tab(text: '1'),
                            Tab(text: '2'),
                            Tab(text: '3'),
                          ],
                          unselectedLabelColor:
                              Color.fromARGB(255, 186, 186, 186),
                        ),
                      ),
                      const Expanded(
                        child: TabBarView(
                          children: [
                            // Your Surah page content
                            One(),
                            Two(),
                            Three(),
                          ],
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class One extends StatelessWidget {
  const One({super.key});

  @override
  Widget build(BuildContext context) {
    return const SingleChildScrollView(
      padding: EdgeInsets.only(
        top: 10,
      ),
      child: Column(
        children: [
          Text(
            'one',
            style: TextStyle(
              fontSize: 50,
            ),
          ),
          Text(
            'one',
            style: TextStyle(
              fontSize: 50,
            ),
          ),
          Text(
            'one',
            style: TextStyle(
              fontSize: 50,
            ),
          ),
          Text(
            'one',
            style: TextStyle(
              fontSize: 50,
            ),
          ),
          Text(
            'one',
            style: TextStyle(
              fontSize: 50,
            ),
          ),
          Text(
            'one',
            style: TextStyle(
              fontSize: 50,
            ),
          ),
          Text(
            'one',
            style: TextStyle(
              fontSize: 50,
            ),
          ),
          Text(
            'one',
            style: TextStyle(
              fontSize: 50,
            ),
          ),
        ],
      ),
    );
  }
}
class Two extends StatelessWidget {
  const Two({super.key});

  @override
  Widget build(BuildContext context) {
    return const Center(
      child: Text('Two'),
    );
  }
}

class Three extends StatelessWidget {
  const Three({super.key});

  @override
  Widget build(BuildContext context) {
    return const Center(
      child: Text('Three'),
    );
  }
}

class AppBarDownt extends StatelessWidget {
  const AppBarDownt({
    super.key,
    required this.size,
  });

  final Size size;

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      height: size.height * 0.29,
      decoration: BoxDecoration(
        color: Theme.of(context).primaryColor,
        borderRadius: const BorderRadius.only(
          bottomLeft: Radius.circular(20),
          bottomRight: Radius.circular(20),
        ),
      ),
    );
  }
}

class AppBarUpt extends StatelessWidget {
  const AppBarUpt({
    super.key,
    required this.size,
  });

  final Size size;

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      height: size.height * 0.15,
      decoration: BoxDecoration(
        color: Theme.of(context).primaryColor,
        borderRadius: const BorderRadius.only(
          bottomLeft: Radius.circular(20),
          bottomRight: Radius.circular(20),
        ),
      ),
    );
  }
}
like image 810
Ahmad Maaz Avatar asked Dec 22 '25 06:12

Ahmad Maaz


1 Answers

Result

enter image description here

Explanation

Since you are dealing with an AppBar, you are probably looking for a NestedScrollView /SliverAppBar.

To detect whether you are currently scrolling you can use a NotificationListener.

Code

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool isScrolling = false;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Material App',
      home: Scaffold(
        body: Scaffold(
          body: NestedScrollView(
            headerSliverBuilder: (context, _) => [
              isScrolling
                  ? SliverAppBar(
                      pinned: true,
                      flexibleSpace: Container(
                        color: Colors.green,
                        child: FlexibleSpaceBar(
                          title: Text(
                            'YES scrolling',
                            style: TextStyle(color: Colors.black),
                          ),
                        ),
                      ),
                    )
                  : SliverAppBar(
                      pinned: true,
                      flexibleSpace: Container(
                        color: Colors.red,
                        child: FlexibleSpaceBar(
                          title: Text(
                            'NOT scrolling',
                            style: TextStyle(color: Colors.black),
                          ),
                        ),
                      ),
                    ),
            ],
            body: NotificationListener<ScrollNotification>(
              onNotification: (scrollNotification) {
                // check if the user is scrolling
                if (scrollNotification is ScrollStartNotification) {
                  setState(() {
                    isScrolling = true;
                  });
                } else if (scrollNotification is ScrollEndNotification) {
                  setState(() {
                    isScrolling = false;
                  });
                }
                return true;
              },
              child: ListView.builder(
                itemCount: 100,
                itemBuilder: (context, index) => ListTile(
                  title: Text('Item $index'),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}


If you want animations, you can use AnimtedSwitcher. For example:

 flexibleSpace: AnimatedSwitcher(
                duration: Duration(seconds: 1),
                child: isScrolling
                    ? Container(
                        key: ValueKey('AppBar1'),
                        color: Colors.green,
                        child: FlexibleSpaceBar(
                          title: Text('YES scrolling',
                              style: TextStyle(color: Colors.black)),
                        ),
                      )
                    : Container(
                        key: ValueKey('AppBar2'),
                        color: Colors.red,
                        child: FlexibleSpaceBar(
                          title: Text('NOT scrolling',
                              style: TextStyle(color: Colors.black)),
                        ),
                      ),
              ),
like image 194
MendelG Avatar answered Dec 23 '25 20:12

MendelG



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!