Flutter 滚动布局2

543 阅读2分钟

Flutter 中常用的滑动布局

  • SingleChildScrollView 主要处理简单可滑动的页面布局视图。当页面内容一屏不能完全展示时,就需要滑动处理。

  • NestedScrollView 主要处理复杂情况下的滑动应用场景。如需要要折叠隐藏一部分内容时就需要使用到 NestedScrollView 与 SliverAppBar 的结合使用。

  • CustomScrollView 用来处理更为复杂的布局。 常需要结合 SliverAppBar,SliverList和SliverGrid SliverPadding SliverToBoxAdapter SliverPersistentHeader, SliverFillRemaining,SliverFillViewport 等来使用。

SingleChildScrollView

类似于 iOS/Android 中的ScrollView,通常只应在期望的内容不会超过屏幕太多时使用

Scrollbar(

  child: SingleChildScrollView(

    scrollDirection: Axis.vertical,

    padding: EdgeInsets.all(10),

    reverse: false, //是否滚动到末尾
 
    child: Column(children:
      str.split("").map((e) => Text(e)).toList(),
    ),
  ),
);

CustomScrollView

CustomScrollView是使用Sliver组件创建自定义滚动效果的滚动组件。

使用场景:

  1. ListView和GridView相互嵌套场景,ListView嵌套GridView时,需要给GridView指定高度,但我们希望高度随内容而变化(不指定),ListView和GridView作为整体滚动效果。

  2. 一个页面顶部是AppBar,然后是GridView,最后是ListView,这3个区域以整体来滚动,AppBar具有吸顶效果。


CustomScrollView(
  slivers: [
    _buildBox(), //tag1
    SliverPadding(
      padding: EdgeInsets.all(8),
      sliver: _buildSliverGrid(),
    ),
    _buildSliverList(),
  ],
),

//通常只有 `Sliver` 开头的组件才可以作为CustomScrollView滑动的内容
//所以需要在外层套上 SliverToBoxAdapter

Widget _buildBox() {
  return SliverToBoxAdapter(
    child: Container(
      height: 60,
      color: Colors.amber,
    ),
  );
}

NestedScrollView

滚动隐藏AppBar

NestedScrollView(
  headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
    return <Widget>[SliverAppBar(
      title: Text('标题'),
    )];
  },
  body: ListView.builder(itemBuilder: (BuildContext context,int index){
    return Container(
      height: 80,
      color: Colors.primaries[index % Colors.primaries.length],
      alignment: Alignment.center,
      child: Text(
        '$index',
        style: TextStyle(color: Colors.white, fontSize: 20),
      ),
    );
  },itemCount: 20,),
)

SliverAppBar展开折叠

NestedScrollView(
  headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
    return <Widget>[SliverAppBar(
      expandedHeight: 230.0,
      pinned: true,
      flexibleSpace: FlexibleSpaceBar(
        title: Text('复仇者联盟'),
        background: Image.network(
          'http://img.haote.com/upload/20180918/2018091815372344164.jpg',
          fit: BoxFit.fitHeight,
        ),
      ),
    )];
  },
  body: ListView.builder(itemBuilder: (BuildContext context,int index){
    return Container(
      height: 80,
      color: Colors.primaries[index % Colors.primaries.length],
      alignment: Alignment.center,
      child: Text(
        '$index',
        style: TextStyle(color: Colors.white, fontSize: 20),
      ),
    );
  },itemCount: 20,),
)

与TabBar 结合

NestedScrollView(
  headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
    return <Widget>[
      SliverAppBar(
        expandedHeight: 230.0,
        pinned: true,
        flexibleSpace: Padding(
          padding: EdgeInsets.symmetric(vertical: 8),
          child: PageView( 
                children: [
                  Image.network('http://0918/.jpg'),
                  Image.network('http://44164.jpg'),
                ],
          ),
        ),
      ),
      SliverPersistentHeader(
        pinned: true,
        delegate: StickyTabBarDelegate(
          child: TabBar(
            labelColor: Colors.black,
            controller: this._tabController,
            tabs: <Widget>[
              Tab(text: '资讯'),
              Tab(text: '技术'),
            ],
          ),
        ),
      ),
    ];
  },
  body: TabBarView(
    controller: this._tabController,
    children: <Widget>[
      RefreshIndicator(
        onRefresh: (){
          print(('onRefresh'));
        },
        child: _buildTabNewsList(_newsKey, _newsList),
      ),

      _buildTabNewsList(_technologyKey, _technologyList),
    ],
  ),
)

StickyTabBarDelegate 代码


class StickyTabBarDelegate extends SliverPersistentHeaderDelegate {
  final TabBar child;

  StickyTabBarDelegate({required this.child});

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return Container(
      color: Theme.of(context).backgroundColor,
      child: child,
    );
  }

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

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

  @override
  bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
    return true;
  }
}