flutter3.0学习笔记

NestedScrollView

Preview

NestedScrollView一个滚动视图,其中可以嵌套其他滚动视图,它们的滚动位置是内在链接的。这意味着使用NestedScrollView,将获得两个滚动区域。一个是标题部分,另一个是它的正文部分。NestedScrollView连接这两个部分,因此它们的行为就像一个一致的可滚动区域。这两个部分将一起滚动,并且它们的滚动位置将被链接。构造函数:

const NestedScrollView({
    super.key,
    this.controller,
    this.scrollDirection = Axis.vertical,
    this.reverse = false,
    this.physics,
    required this.headerSliverBuilder,
    required this.body,
    this.dragStartBehavior = DragStartBehavior.start,
    this.floatHeaderSlivers = false,
    this.clipBehavior = Clip.hardEdge,
    this.restorationId,
    this.scrollBehavior,
  })

它可以在需要嵌套滚动视图的许多不同方式中使用,例如,可以在 body 中有一个网格和一个列表,在headerSliverBuilder中有一个SliverAppBar

import 'package:flutter/material.dart';

class NestedScrollViewPage extends StatelessWidget {
  const NestedScrollViewPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: ExampleOne(),
    );
  }
}

class ExampleOne extends StatelessWidget {
  const ExampleOne({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return NestedScrollView(
      headerSliverBuilder: (BuildContext context,bool innerBoxIsScrolled){
        return <Widget>[
           SliverAppBar(
            expandedHeight: 300,
            pinned: true,
            flexibleSpace: FlexibleSpaceBar(
              title: const Text('NestedScrollView'),
              background: Image.asset('images/liu.webp',fit: BoxFit.cover,),
            ),
          )
        ];
      },
      body: ListView.builder(
         physics: const ClampingScrollPhysics(),
        itemBuilder: (BuildContext context , int index){
          return Container(
            height: 100,
            color: Colors.primaries[index % Colors.primaries.length],
            child: Center(
              child: Text('Item $index',style: const TextStyle(color: Colors.white,fontSize: 30),),
            ),
          );
        },
        itemCount: 30,
      )
    );
  }
}

nest.gif

常见的用例是一个SliverAppBar ,在 body 中有一个TabBar和一个TabBarView,其中可滚动的内容根据所选的选项卡而有所不同。在这种情况下,NestedScrollView可以根据嵌套滚动视图中的操作来保持SliverAppBar折叠或展开,从而节省了大量工作。使用SliverOverlapAbsorber包裹另一个的Sliver,强制其布局范围被视为重叠。

import 'package:flutter/material.dart';

class NestedScrollViewPage extends StatelessWidget {
  const NestedScrollViewPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: ExampleTwo(),
    );
  }
}
class ExampleTwo extends StatelessWidget {
  const ExampleTwo({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final List<String> tabs = <String>['Tab 1','Tab 2'];
    return DefaultTabController(
      length: tabs.length,
      child: Scaffold(
        body: NestedScrollView(
          headerSliverBuilder: (BuildContext context,bool innerBoxIsScrolled){
            return <Widget> [
              SliverOverlapAbsorber(
                handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                sliver: SliverAppBar(
                  backgroundColor: Colors.red,
                  pinned: true,
                  expandedHeight: 150.0,
                  forceElevated: innerBoxIsScrolled,

                  bottom: TabBar(
                    tabs: tabs.map((String name) => Tab(text:name)).toList(),
                  ),
                ),
              )
            ];
          },
          body: TabBarView(
            children: tabs.map((String name)  {
              return SafeArea(
                top: false,
                bottom: false,
                child: Builder(
                  builder: (BuildContext context){
                    return CustomScrollView(
                      slivers: [
                        SliverOverlapInjector(
                           handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                        ),
                        SliverPadding(
                          padding: EdgeInsets.all(10),
                          sliver: SliverFixedExtentList(
                            itemExtent: 50,
                            delegate: SliverChildBuilderDelegate(
                                (BuildContext context,int index){
                                  return ListTile(
                                    title: Text('Item $index'),
                                  );
                                },
                              childCount: 50,
                            ),
                          ),
                        )
                      ],
                    );
                  },
                ),
              );
            }).toList(),
          ),
        ),
      )
    );
  }
}

Simulator Screen Shot - iPhone 14 Pro Max - 2022-11-10 at 16.18.09.png