Search code examples
flutterdartanimationscrollappbar

How to scroll and animate an object onto an app bar?


Based on the image below, how does one scroll the text from a children in a body to the app bar above? How to make it transition from 2 lines of text to 1 line of text? As shown in the image.

Currently in my code, the bottom container consist of a SingleChildScrollView inside an Expanded widget. Instead of this, now I'd like to make it scrollable all the way to the top. And as one scrolls, it animates the text object.

enter image description here

  class _FirstScreenState extends State<FirstScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Column(
        children: [
          Column(
            children: [
              Container(),
              Container(
                padding: EdgeInsets.only(),
                child: Column(
                  children: [
                    Text('Some Text...'),
                    Text('Some Secondary Text...'),
                  ],
                ),
              ),
            ],
          ),
          Container(
            child: Expanded(
              child: PageView(
                controller: _pageController,
                onPageChanged: (page) {
                  setState(() {
                    currentIndex = page;
                  });
                },
                children: [
                  SingleChildScrollView(),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Update:

enter image description here


Solution

  • class ExampleWidget extends StatefulWidget {
      ExampleWidget({Key? key}) : super(key: key);
    
      @override
      State<ExampleWidget> createState() => _ExampleWidgetState();
    }
    
    class _ExampleWidgetState extends State<ExampleWidget> {
      double maxHeaderHeight = 200;
      late ScrollController _scrollController;
      final ValueNotifier<double> opacity = ValueNotifier(0);
    
      @override
      void initState() {
        super.initState();
    
        _scrollController = ScrollController();
        _scrollController.addListener(scrollListener);
      }
    
      scrollListener() {
        if (maxHeaderHeight > _scrollController.offset && _scrollController.offset > 1) {
          opacity.value = 1 - _scrollController.offset / maxHeaderHeight;
        } else if (_scrollController.offset > maxHeaderHeight && opacity.value != 1) {
          opacity.value = 0;
        } else if (_scrollController.offset < 0 && opacity.value != 0) {
          opacity.value = 1;
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.grey,
          body: CustomScrollView(
            controller: _scrollController,
            slivers: [
              SliverAppBar(
                backgroundColor: Colors.white,
                title: ValueListenableBuilder<double>(
                    valueListenable: opacity,
                    builder: (context, value, child) {
                      return AnimatedOpacity(
                        duration: const Duration(milliseconds: 1),
                        opacity: 1 - value,
                        child: const Text("Some Text....", style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold)),
                      );
                    }),
                pinned: true,
                expandedHeight: maxHeaderHeight,
                flexibleSpace: FlexibleSpaceBar(
                  background: SafeArea(
                    child: Center(
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: const [
                          Text("Some Text....", style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold, fontSize: 18)),
                          Text("Some Primary Text....", style: TextStyle(color: Colors.black, fontSize: 18)),
                        ],
                      ),
                    ),
                  ),
                  collapseMode: CollapseMode.parallax,
                ),
              ),
              SliverList(
                  delegate: SliverChildListDelegate(
                [
                  Column(
                    children: List.generate(
                        100,
                        (index) => Padding(
                              padding: const EdgeInsets.symmetric(vertical: 20.0),
                              child: Text("Index $index"),
                            )),
                  )
                ],
              ))
            ],
          ),
        );
      }
    }
    

    enter image description here

    enter image description here