CustomScrollView: Body 在 SliverAppBar 下滚动

CustomScrollView: Body scrolls under SliverAppBar

Flutter DartPad

我在CustomScrollView内有多个SliverAppBar,屏幕的body在SliverFillRemaining内。


顶部的 SliverAppBar 已固定
中间的 SliverAppBar 是一个图像,会随着用户滚动而折叠
Bottom SliverAppBar 是一个固定的 TabBar,在 Image 完全折叠后将保留在 First SliverAppBar 下方

目前的经验是,最初滚动时,body滚动到最低的SliverAppBar下。我已经尝试使用 SliverOverlapAbsorber/Injector,但这只是在 body 的顶部添加了一个 space,这样 space 就会重叠,而不是 body ],但这不是我想要的。


我希望 body 和 SliverAppBars 一起滚动,直到中间的 SliverAppBar 完全折叠,然后我希望 body 滚动。

我已经为此工作了几个小时,你如何阻止 body 在滚动条上重叠?

要实现这种滚动行为,使用起来更容易 NestedScrollView 并注意主应用栏不再位于条子中

import 'package:flutter/material.dart';

final Color darkBlue = Color.fromARGB(255, 18, 32, 47);

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

class MyWidget extends StatefulWidget {
  @override
  MyWidgetState createState() => MyWidgetState();
}

class MyWidgetState extends State<MyWidget>
    with SingleTickerProviderStateMixin {
  TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(vsync: this, length: 2);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('title'),
        elevation: 0,
        leading: IconButton(
          icon: const Icon(Icons.arrow_back),
          onPressed: () {},
        ),
      ),
      body: NestedScrollView(
        floatHeaderSlivers: true,
        physics: const BouncingScrollPhysics(),
        body: TabBarView(
          controller: _tabController,
          physics: const NeverScrollableScrollPhysics(),
          children: [
            SingleChildScrollView(
              physics: const NeverScrollableScrollPhysics(),
              child: Column(
                children: List.generate(
                  1000,
                  (index) => Text('Tab One: $index'),
                ),
              ),
            ),
            SingleChildScrollView(
              physics: const NeverScrollableScrollPhysics(),
              child: Column(
                  children: List.generate(
                1000,
                (index) => Text('Tab Two: $index'),
              )),
            )
          ],
        ),
        headerSliverBuilder: (context, innerBoxIsScrolled) {
          return <Widget>[
            SliverAppBar(
              pinned: true,
              floating: false,
              elevation: 0,
              toolbarHeight: 0,
              collapsedHeight: null,
              automaticallyImplyLeading: false,
              expandedHeight: MediaQuery.of(context).size.height * .4,
              flexibleSpace: const FlexibleSpaceBar(
                  collapseMode: CollapseMode.parallax,
                  background: Placeholder()),
              titleSpacing: 0,
              primary: false,
            ),
            SliverAppBar(
              pinned: true,
              forceElevated: true,
              primary: false,
              automaticallyImplyLeading: false,
              expandedHeight: 50,
              collapsedHeight: null,
              toolbarHeight: 50,
              titleSpacing: 0,
              title: Align(
                alignment: Alignment.topCenter,
                child: TabBar(
                    controller: _tabController,
                    isScrollable: true,
                    tabs: [
                      const Text('Tab One'),
                      const Text('Tab Two'),
                    ]),
              ),
            ),
          ];
        },
      ),
    );
  }
}