如何在SliverAppBar上重叠SliverList

kev*_*kev 6 flutter flutter-layout

我正在尝试在上重叠SliverList几个像素SliverAppBar。类似于这个帖子。我希望其中的图片位于我的图片FlexibleSpaceBar下方SliverList。我正在尝试实现以下目标。

在此处输入图片说明

我只能这样获得半径。没有能力重叠SliverListSliverAppBar在此处输入图片说明

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
            floating: false,
            expandedHeight: MediaQuery.of(context).size.height * 0.50,
            flexibleSpace: FlexibleSpaceBar(
              background: Image.network(pet.photos.first)
            ),
          ),
          SliverList(
            delegate: SliverChildListDelegate([
              Container(
                height: 40,
                decoration: BoxDecoration(
                  color: Colors.red,
                  borderRadius: BorderRadius.only(
                    topLeft: Radius.circular(30),
                    topRight: Radius.circular(30),
                  ),
                ),
              ),
            ]),
          )
        ],
      ),
    );
  }
Run Code Online (Sandbox Code Playgroud)

任何方向将不胜感激,谢谢!

Ric*_*ick 10

您可以使用以下小部件,它使用 Stack 和滚动侦听器来实现类似屏幕截图的功能。它基于https://pub.dartlang.org/packages/sliver_fab

小部件:

import 'package:flutter/material.dart';

class DetailScaffold extends StatefulWidget {
  final ScrollController controller;
  final ScrollPhysics physics;
  final List<Widget> slivers;

  final double expandedHeight;

  /// Changes edge behavior to account for [SliverAppBar.pinned].
  ///
  /// Hides the edge when the [ScrollController.offset] reaches the collapsed
  /// height of the [SliverAppBar] to prevent it from overlapping the app bar.
  final bool hasPinnedAppBar;

  DetailScaffold({
    @required this.expandedHeight,
    this.controller,
    this.physics,
    this.slivers,
    this.hasPinnedAppBar = false,
  }) {
    assert(expandedHeight != null);
    assert(hasPinnedAppBar != null);
  }

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

class _DetailScaffoldState extends State<DetailScaffold> {
  ScrollController ctrl;

  @override
  void initState() {
    super.initState();

    ctrl = widget.controller ?? ScrollController();
    ctrl.addListener(() => setState(() {}));
  }

  @override
  void dispose() {
    if (widget.controller == null) {
      ctrl.dispose();
    }

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        CustomScrollView(
          controller: ctrl,
          physics: widget.physics,
          slivers: widget.slivers,
        ),
        _buildEdge(),
      ],
    );
  }

  _buildEdge() {
    var edgeHeight = 12.0;
    var paddingTop = MediaQuery.of(context).padding.top;

    var defaultOffset =
        (paddingTop + widget.expandedHeight) - edgeHeight;

    var top = defaultOffset;
    var edgeSize = edgeHeight;

    if (ctrl.hasClients) {
      double offset = ctrl.offset;
      top -= offset > 0 ? offset : 0;

      if (widget.hasPinnedAppBar) {
        // Hide edge to prevent overlapping the toolbar during scroll.
        var breakpoint =
            widget.expandedHeight - kToolbarHeight - edgeHeight;

        if (offset >= breakpoint) {
          edgeSize = edgeHeight - (offset - breakpoint);
          if (edgeSize < 0) {
            edgeSize = 0;
          }

          top += (edgeHeight - edgeSize);
        }
      }
    }

    return Positioned(
      top: top,
      left: 0,
      right: 0,
      child: Container(
        height: edgeSize,
        decoration: BoxDecoration(
          color: Theme.of(context).canvasColor,
          borderRadius: BorderRadius.vertical(
            top: Radius.circular(12),
          ),
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

使用它:

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var expandedHeight = 128.0;

    return DetailScaffold(
      expandedHeight: expandedHeight,
      slivers: <Widget>[
        SliverAppBar(
          expandedHeight: expandedHeight,
          flexibleSpace: FlexibleSpaceBar(
            background: Container(color: Colors.purple),
          ),
        ),
        SliverList(
          delegate: SliverChildBuilderDelegate((_, i) {
            return ListTile(title: Text('Item $i'));
          }, childCount: 50),
        ),
      ],
    );
  }
}
Run Code Online (Sandbox Code Playgroud)


Edu*_*tal 7

我找到了一种更简单的方法来实现这一目标:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'How to overlap SliverList on a SliverAppBar',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Home(),
    );
  }
}

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
            bottom: PreferredSize(
              child: Container(),
              preferredSize: Size(0, 20),
            ),
            pinned: false,
            expandedHeight: MediaQuery.of(context).size.height * 0.4,
            flexibleSpace: Stack(
              children: [
                Positioned(
                    child: Image(
                      fit: BoxFit.cover,
                      image: NetworkImage(
                        "https://images.pexels.com/photos/62389/pexels-photo-62389.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260",
                      ),
                    ),
                    top: 0,
                    left: 0,
                    right: 0,
                    bottom: 0),
                Positioned(
                  child: Container(
                    height: 30,
                    decoration: BoxDecoration(
                      color: Colors.lightBlue[100],
                      borderRadius: BorderRadius.vertical(
                        top: Radius.circular(50),
                      ),
                    ),
                  ),
                  bottom: -1,
                  left: 0,
                  right: 0,
                ),
              ],
            ),
          ),
          SliverFixedExtentList(
            itemExtent: 50.0,
            delegate: SliverChildBuilderDelegate(
              (BuildContext context, int index) {
                return Container(
                  alignment: Alignment.center,
                  color: Colors.lightBlue[100 * (index + 1 % 9)],
                  child: Text('List Item $index'),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

Run Code Online (Sandbox Code Playgroud)

你可以在 DartPad 中看到:https ://dartpad.dev/e2976ad6ede98813b75ee44b1217cc96

重要的部分是在 flexibleSpace 中使用一个堆栈,其中包含您想要显示为一个定位小部件和另一个带有边框装饰的位置小部件的内容。

您还需要使用 SliverAppBar 的底部部分将首选大小设置为与带边框装饰的小部件相同。

DartPad 没有正确呈现小部件(它在底部显示了一个边距,但在 iOS/Android 中不会发生这种情况)。