如何使用NestedScrollView实现此目的?

问题描述 投票:0回答:1

我将代码简化为一个小示例应用程序,因此请考虑以下代码:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  TabController _tabController;
  ScrollController _scrollController;
  ScrollController _sliverScrollController;

  var tabs = ['Tab1', 'Tab2', 'Tab3'];

  @override
  void initState() {
    super.initState();
    _tabController = TabController(vsync: this, length: 3);
    _scrollController = ScrollController();
    _sliverScrollController = ScrollController();
  }

  @override
  void dispose() {
    // "Unmount" the controllers:
    _tabController.dispose();
    _scrollController.dispose();
    _sliverScrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
        length: 3,
        child: Scaffold(
          body: NestedScrollView(
            controller: _scrollController,
            headerSliverBuilder:
                (BuildContext context, bool innerBoxIsScrolled) {
              return [
                SliverOverlapAbsorber(
                    handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
                        context),
                    sliver: SliverAppBar(
                        backgroundColor: Colors.black,
                        pinned: true,
                        expandedHeight: 400,
                        elevation: 0,
                        flexibleSpace: FlexibleSpaceBar(
                            collapseMode: CollapseMode.pin,
                            background: Flex(
                              direction: Axis.vertical,
                              children: <Widget>[
                                Padding(
                                    padding: EdgeInsets.only(
                                        top: 75,
                                        left: 10,
                                        right: 10,
                                        bottom: 5),
                                    child: Column(
                                      crossAxisAlignment:
                                          CrossAxisAlignment.start,
                                      children: <Widget>[
                                        Text('Testing',
                                            overflow: TextOverflow.ellipsis,
                                            maxLines: 2,
                                            style: TextStyle(
                                                color: Colors.white,
                                                fontSize: 18,
                                                fontWeight: FontWeight.bold)),
                                        Padding(
                                            padding:
                                                EdgeInsets.only(bottom: 5)),
                                        Text('More Text',
                                            style: TextStyle(
                                                color: Colors.white,
                                                fontSize: 18,
                                                fontWeight: FontWeight.bold))
                                      ],
                                    ))
                              ],
                            )))),
                SliverPersistentHeader(
                    delegate: _SliverAppBarDelegate(PreferredSize(
                        preferredSize:
                            Size(MediaQuery.of(context).size.width, 30),
                        child: TabBar(
                          labelColor: Colors.white,
                          tabs: tabs
                              .map((String name) => Container(
                                  color: Colors.blue, child: Tab(text: name)))
                              .toList(),
                        ))),
                    pinned: true)
              ];
            },
            body: TabBarView(
              children: [_buildPanel1(), _buildPanel2(), _buildPanel3()],
            ),
          ),
        ));
  }

  _wrapTabWidget(tabWidget) {
    return Builder(
      builder: (BuildContext context) {
        return CustomScrollView(
          controller: _sliverScrollController,
          slivers: <Widget>[
            SliverOverlapInjector(
              handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
            ),
            SliverToBoxAdapter(
                child: Padding(
                    padding: EdgeInsets.only(top: 10, left: 10, right: 10),
                    child: tabWidget))
          ],
        );
      },
    );
  }

  _buildPanel1() {
    return _wrapTabWidget(ListView(
        shrinkWrap: true,
        children: new List<Widget>.generate(
            100,
            (i) => ListTile(
                leading: Icon(Icons.shopping_cart), title: Text('item $i')))));
  }

  _buildPanel2() {
    return _wrapTabWidget(Text('Panel 2'));
  }

  _buildPanel3() {
    return _wrapTabWidget(Text('Panel 3'));
  }
}

class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  _SliverAppBarDelegate(this._tabBar);

  final PreferredSizeWidget _tabBar;

  @override
  double get minExtent => _tabBar.preferredSize.height;

  @override
  double get maxExtent => _tabBar.preferredSize.height;

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return new Container(
      color: Colors.blue,
      child: _tabBar,
    );
  }

  @override
  bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
    return false;
  }
}

我真正想做的是有效地将相同的“滚动控制器”应用于整个页面,并能够滚动tab1中的列表小部件,并使其向上滚动TabBar并将其锚定到顶部,然后继续滚动在ListView中。

所以,如果我要点击并按住此处:

enter image description here

然后开始滚动,它将滚动整个屏幕,直到TabBar到达顶部,然后将TabBar固定在此处,并继续滚动底部的ListView。

我希望我对此解释得足够好! :)

flutter flutter-layout
1个回答
1
投票

这是您想要的吗?

class TestWidget extends StatefulWidget {
  @override
  _TestWidgetState createState() => _TestWidgetState();
}

class _TestWidgetState extends State<TestWidget>
    with SingleTickerProviderStateMixin {
  bool isAbsorbing = false;

  TabController _primaryTC;

  final List<Tab> tabs = [
    Tab(
      text: "tab1",
    ),
    Tab(
      text: "tab2",
    ),
    Tab(
      text: "tab3",
    )
  ];

  @override
  void initState() {
    super.initState();
    _primaryTC = TabController(length: tabs.length, vsync: this);
    _primaryTC.index = tabs.length - 1;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        child: NestedScrollView(
            headerSliverBuilder:
                (BuildContext context, bool innerBoxIsScrolled) {
              return []..add(SliverAppBar(
                  flexibleSpace: FlexibleSpaceBar(
                    title: Text(""),
                  ),
                  expandedHeight: 500,
                  pinned: true,
                  floating: true,
                  snap: false,
                ));
            },
            body: Container(
              child: Column(
                children: <Widget>[
                  TabBar(
                    tabs: tabs,
                    controller: _primaryTC,
                  ),
                  Expanded(flex:1,child: TabBarView(
                      controller: _primaryTC,
                      children: tabs
                          .map((tab) => Container(
                        child: ListView.builder(
                            itemCount: 30,
                            itemBuilder: (context, index) {
                              return Container(height: 50,alignment: Alignment.center,child: Text("test"),);
                            }),
                        width: double.infinity,
                        height: double.infinity,
                        alignment: Alignment.center,
                      ))
                          .toList()))
                ],
              ),
            )),
      ),
    );
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.