如何使用可滚动小部件绘制一个 UI 复杂 header?

How to use scrollable widget to draw one UI with complex header?

我是Flutter的新手。现在我必须为一个项目实施一个 UI (Link)。我想我应该使用 CustomerScrollViewNestedScrollView 来完成它。我尽了最大努力,但我在实现 header SliverAppBarADBar 方面很糟糕。谁能帮忙给点建议吗?

我尝试使用NestedScrollView,并将所有项目放入SliverAppBar,但遇到了一个问题。如果我使用 FlexibleSpaceBar,所有项目在初始化时都被缩小到 phone,直到你向上滚动一段距离,你才能看到任何东西。如果我放弃 FlexibleSpaceBar 而使用普通的 Widget,它似乎还可以,但是如果我尝试向上滚动并且一旦 Ad Bar 与文本项重叠,错误 "BOTTOM OVERFLOWED XX PIXELS" 就会消失。

class _MyPageState extends State<MyPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appbar: AppBar(/*build my AppBar*/),
      body: NestedScrollView(
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
          return <Widget>[
            SliverAppBar(
              ...,
              expandedHeight: 180,
              flexibleSpace: FlexibleSpaceBar(  //if use, all texts are out of phone when initializing
                                                //if not use, Error('BOTTOM OVERFLOWED xx PIXELS') while scrolling up
                title: Padding(
                  padding: EdgeInsets.all(10),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: <Widget>[
                      Column(
                        mainAxisAlignment: MainAxisAlignment.start,
                        children: <Widget>[
                          Text('Text 1'),
                          Text('Text 2'),
                          Text('Text 3'),
                        ],
                      ),
                      Column(
                        mainAxisAlignment: MainAxisAlignment.start,
                        children: <Widget>[
                          Text('Text 4'),
                          Text('Text 5'),
                          Text('Text 6'),
                        ],
                      ),
                    ],
                  )
                ),
              ),
              //implement AD Bar
              SliverPersistentHeader(
                delegate: _SliverPersistentHeaderDelegate(_buildADBar())
              ),
            )
          ];
        },
        body: //build body
          GridView.count(
            crossAxisCount: 2,
            children: ...
          ),
      ),
    );
  }
}

作为一个选项

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('App Bar'),
          centerTitle: true,
          elevation: 0,
        ),
        body: CustomScrollView(
          slivers: <Widget>[
            SliverAppBar(
              expandedHeight: 180,
              floating: false,
              elevation: 0,
              backgroundColor: Colors.green,
              flexibleSpace: SingleChildScrollView(
                child: Padding(
                  padding: EdgeInsets.all(10),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: <Widget>[
                      Column(
                        mainAxisAlignment: MainAxisAlignment.start,
                        children: <Widget>[
                          Text('Text 1'),
                          Text('Text 2'),
                          Text('Text 3'),
                        ],
                      ),
                      Column(
                        mainAxisAlignment: MainAxisAlignment.start,
                        children: <Widget>[
                          Text('Text 4'),
                          Text('Text 5'),
                          Text('Text 6'),
                        ],
                      ),
                    ],
                  ),
                ),
              ),
            ),
            SliverAppBar(
              pinned: true,
              elevation: 0,
              flexibleSpace: Container(
                alignment: Alignment.center,
                height: kToolbarHeight,
                color: Colors.redAccent,
                child: Text('AD Bar'),
              ),
            ),
            SliverGrid.count(
              crossAxisCount: 2,
              children: List.generate(20, (index) {
                return Card(
                  color: Colors.white,
                );
              }),
            ),
          ],
        ),
      ),
    );
  }
}

这是你需要做的:

import 'package:flutter/material.dart';
import 'dart:math' as math;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Collapsing List Demo')),
        body: CollapsingList(),
      ),
    );
  }
}
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  _SliverAppBarDelegate({
    @required this.minHeight,
    @required this.maxHeight,
    @required this.child,
  });
  final double minHeight;
  final double maxHeight;
  final Widget child;
  @override
  double get minExtent => minHeight;
  @override
  double get maxExtent => math.max(maxHeight, minHeight);
  @override
  Widget build(
      BuildContext context, 
      double shrinkOffset, 
      bool overlapsContent) 
  {
    return new SizedBox.expand(child: child);
  }
  @override
  bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
    return maxHeight != oldDelegate.maxHeight ||
        minHeight != oldDelegate.minHeight ||
        child != oldDelegate.child;
  }
}
class CollapsingList extends StatelessWidget {
  SliverPersistentHeader makeHeader(String headerText) {
    return SliverPersistentHeader(
      pinned: true,
      delegate: _SliverAppBarDelegate(
        minHeight: 60.0,
        maxHeight: 200.0,
        child: Container(
            color: Colors.lightBlue, child: Center(child:
                Text(headerText))),
      ),
    );
  }
  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: <Widget>[
        makeHeader('Header Section 1'),
        SliverGrid.count(
          crossAxisCount: 3,
          children: [
            Container(color: Colors.red, height: 150.0),
            Container(color: Colors.purple, height: 150.0),
            Container(color: Colors.green, height: 150.0),
            Container(color: Colors.orange, height: 150.0),
            Container(color: Colors.yellow, height: 150.0),
            Container(color: Colors.pink, height: 150.0),
            Container(color: Colors.cyan, height: 150.0),
            Container(color: Colors.indigo, height: 150.0),
            Container(color: Colors.blue, height: 150.0),
          ],
        ),
        makeHeader('Header Section 2'),
        SliverFixedExtentList(
          itemExtent: 150.0,
          delegate: SliverChildListDelegate(
            [
              Container(color: Colors.red),
              Container(color: Colors.purple),
              Container(color: Colors.green),
              Container(color: Colors.orange),
              Container(color: Colors.yellow),
            ],
          ),
        ),
        makeHeader('Header Section 3'),
        SliverGrid(
          gridDelegate: 
              new SliverGridDelegateWithMaxCrossAxisExtent(
            maxCrossAxisExtent: 200.0,
            mainAxisSpacing: 10.0,
            crossAxisSpacing: 10.0,
            childAspectRatio: 4.0,
          ),
          delegate: new SliverChildBuilderDelegate(
            (BuildContext context, int index) {
              return new Container(
                alignment: Alignment.center,
                color: Colors.teal[100 * (index % 9)],
                child: new Text('grid item $index'),
              );
            },
            childCount: 20,
          ),
        ),
        makeHeader('Header Section 4'),
        // Yes, this could also be a SliverFixedExtentList. Writing 
        // this way just for an example of SliverList construction.
        SliverList(
          delegate: SliverChildListDelegate(
            [
              Container(color: Colors.pink, height: 150.0),
              Container(color: Colors.cyan, height: 150.0),
              Container(color: Colors.indigo, height: 150.0),
              Container(color: Colors.blue, height: 150.0),
            ],
          ),
        ),
      ],
    );
  }
}

我绝对会推荐您阅读这篇文章Slivers Demystified

希望对您有所帮助。