繁体   English   中英

如何使浮动 SliverAppBar 仅显示折叠栏?

[英]How to make floating SliverAppBar to show only collapsed bar?

我正在使用带有背景图像的 Flutter Web 的 SliverAppBar,我希望该栏在用户向下滚动网页时消失,并在他们向上滚动时再次出现,但只有应用程序栏,不显示背景,除非他们到达顶峰。 这在 Flutter web 中可以实现吗?

我的 SliverAppBar:

class NavBar extends StatelessWidget {
  final Widget _background;

  const NavBar(this._background);

  @override
  SliverAppBar build(BuildContext context) {
    double _width = MediaQuery.of(context).size.width;
    double? _height = MediaQuery.of(context).size.height;

    List<Widget> _actions() {
      List<Widget> _list = [];
      List _titles = Navigation(context).routes.keys.toList();
      List _routes = Navigation(context).routes.values.toList();

      _selectView(String route) {
        Navigator.of(context).pushNamed(route);
      }

      Widget _singleItem(String text, String route) {
        return InkWell(
          onTap: () => _selectView(route),
          borderRadius: BorderRadius.circular(15),
          child: Container(
            padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
            alignment: Alignment.center,
            child: Text(
              text,
              style: const TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                  color: Colors.white),
            ),
          ),
        );
      }

      for (int i = 0; i < Navigation(context).showingLinks; i++) {
        _list.add(_singleItem(_titles[i], _routes[i]));
      }

      return _list;
    } // navBarItems

    return SliverAppBar(
      backgroundColor: Theme.of(context).primaryColor,
      expandedHeight: _height,
      pinned: true,
      elevation: 0,
      //TODO make actions appear only when SliverAppBar collapses
      actions: _width > 800 ? _actions() : [],
      flexibleSpace: FlexibleSpaceBar(
        background: _background,
      ),
    );
  }
}

对于我在所有视图中使用的一般结构,这是我的 HomeView 的一个示例:

class HomeView extends StatelessWidget {
  final double paddingHorizontal = 60;
  final double paddingVertical = 60;
  ScrollController _scrollController = ScrollController();
  final _key = GlobalKey();

  HomeView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final double width = MediaQuery.of(context).size.width;

     Widget navBarBackground() {
         return Stack(...)
     }

     return Scaffold(
      backgroundColor: Colors.white,
      endDrawer: EndDrawer(),
      body: CustomScrollView(
        controller: _scrollController,
        slivers: [
          NavBar(navBarBackground()),
          SliverList(
            delegate: SliverChildListDelegate(
              [
                highlights(),
                androidIosDesktop(),
                multiplatform(),
                catchPhrase(),
                contact(),
                const Footer(),
              ],
            ),
          )
        ],
      ),
    );
  }
} //HomeView

这是显示的内容:

背景图片应用栏

我希望它只显示这个:

没有背景图片的应用栏

是的。 但是,我想不是直接的。

您可以使用ScrollController来实现此目的。 将一个ScrollController附加到CustomScrollView ,然后观察该位置所在的控制器的offset 并基于它,您可以实现所需的输出。

我编写了一个简单的演示代码,这正是您需要的东西

试试 dartpad =>这里

我做了什么? (在试用 dartpad 之后看看)

  1. ScrollController添加到CustomScrollView
  2. 添加一个需要扩展的 const 变量moreHeight
  3. 添加在收听滚动时将设置的可变expandedHeight高度
  4. 向控制器添加一个侦听器,该侦听器根据滚动offset更改变量expandedHeight
  5. 使用这个expandedHeight我们将更改SliverAppBar中的值

编辑:

下面的代码是在分离作为无状态小部件的应用栏小部件之后,并从包含滚动视图的视图/页面传递参数并且可以使用滚动控制器(行为没有变化)

注意:以下代码可用于全高扩展应用栏

import 'package:flutter/material.dart';

const Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: darkBlue,
      ),
      debugShowCheckedModeBanner: false,
      home: const Scaffold(
        body: Center(
          child: MyStatefulWidget(),
        ),
      ),
    );
  }
}

class MyStatefulWidget extends StatefulWidget {
  const MyStatefulWidget({Key? key}) : super(key: key);

  @override
  State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  late ScrollController _scrollController;
  // variable height passed to SliverAppBar expanded height
  double? _expandedHeight;

  @override
  initState() {
    super.initState();
    // initialize and add scroll listener
    _scrollController = ScrollController();
    _scrollController.addListener(_scrollListen);
    // initially expanded height is full
    WidgetsBinding.instance.addPostFrameCallback((_) {
      setState(() {
        _expandedHeight = MediaQuery.of(context).size.height;
      });
    });
  }

  @override
  dispose() {
    // dispose the scroll listener and controller
    _scrollController.removeListener(_scrollListen);
    _scrollController.dispose();
    super.dispose();
  }

  _scrollListen() {
    final offset = _scrollController.offset;
    final height = MediaQuery.of(context).size.height;
    if (offset > height) {
      // if offset is more height, disable expanded height
      if (_expandedHeight != null) {
        setState(() {
          _expandedHeight = null;
        });
      }
    } else {
      // if offset is less, keep increasing the height to offset 0
      setState(() {
        _expandedHeight = height - offset;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        controller: _scrollController,
        slivers: <Widget>[
          AppBarWidget(
            expandedHeight: _expandedHeight,
          ),
          SliverToBoxAdapter(
            child: SizedBox(
              height: 2000,
              child: Center(
                child: Container(
                  color: Colors.blue,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class AppBarWidget extends StatelessWidget {
  const AppBarWidget({super.key, this.expandedHeight});

  final double? expandedHeight;

  // constant more height that is given to the expandedHeight
  // of the SliverAppBar
//   static double moreHeight = 200;

  @override
  Widget build(BuildContext context) {
    final height = MediaQuery.of(context).size.height;
    return SliverAppBar(
      pinned: false,
      floating: true,
      expandedHeight: expandedHeight,
      actions: [
        TextButton(
          onPressed: () {},
          child: const Text('test'),
        ),
      ],
      flexibleSpace: FlexibleSpaceBar(
        // animate the opacity offset when expanded height is changed
        background: AnimatedOpacity(
          opacity: expandedHeight != null ? expandedHeight! / height : 0,
          duration: const Duration(milliseconds: 300),
          child: const FlutterLogo(),
        ),
      ),
    );
  }
}

编辑 2:我对您的代码进行了细微更改

正如我所见,您需要为背景展开全高,我已包含在上面的代码以及下面的代码中。

导航栏

  1. 添加新字段扩展高度
  2. 制作了所有命名参数
  3. 根据展开高度向灵活条添加动画不透明度
class NavBar extends StatelessWidget {
  const NavBar({this.background, this.expandedHeight});
  
   final double? expandedHeight;
  
   final Widget? background;

  @override
  SliverAppBar build(BuildContext context) {
    double _width = MediaQuery.of(context).size.width;
    double? _height = MediaQuery.of(context).size.height;

    List<Widget> _actions() {
      List<Widget> _list = [];
      List _titles = Navigation(context).routes.keys.toList();
      List _routes = Navigation(context).routes.values.toList();

      _selectView(String route) {
        Navigator.of(context).pushNamed(route);
      }

      Widget _singleItem(String text, String route) {
        return InkWell(
          onTap: () => _selectView(route),
          borderRadius: BorderRadius.circular(15),
          child: Container(
            padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
            alignment: Alignment.center,
            child: Text(
              text,
              style: const TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                  color: Colors.white),
            ),
          ),
        );
      }

      for (int i = 0; i < Navigation(context).showingLinks; i++) {
        _list.add(_singleItem(_titles[i], _routes[i]));
      }

      return _list;
    } // navBarItems

    return SliverAppBar(
      backgroundColor: Theme.of(context).primaryColor,
      expandedHeight: expandedHeight,
      pinned: true,
      elevation: 0,
      //TODO make actions appear only when SliverAppBar collapses
      actions: _width > 800 ? _actions() : [],
      flexibleSpace: FlexibleSpaceBar(
        // animate the opacity offset when expanded height is changed
        background: AnimatedOpacity(
          opacity: expandedHeight != null ? expandedHeight! / _height : 0,
          duration: const Duration(milliseconds: 300),
          child: background,
        ),
      ),
    );
  }
}

主页视图

  1. 使它成为有状态的小部件
  2. 添加滚动控制器、监听器、expandedHeight
class HomeView extends StatefulWidget {
  const HomeView({Key? key}) : super(key: key);

  @override
  State<HomeView> createState() => _HomeView();
}

class _HomeView extends State<HomeView> {
  final double paddingHorizontal = 60;
  final double paddingVertical = 60;
  
  late ScrollController _scrollController;
  // variable height passed to SliverAppBar expanded height
  double? _expandedHeight;
  
  final _key = GlobalKey();
  
  @override
  initState() {
    super.initState();
    // initialize and add scroll listener
    _scrollController = ScrollController();
    _scrollController.addListener(_scrollListen);
    // initially expanded height is full
    WidgetsBinding.instance.addPostFrameCallback((_) {
      setState(() {
        _expandedHeight = MediaQuery.of(context).size.height;
      });
    });
  }
  
  @override
  dispose() {
    // dispose the scroll listener and controller
    _scrollController.removeListener(_scrollListen);
    _scrollController.dispose();
    super.dispose();
  }

  _scrollListen() {
    final offset = _scrollController.offset;
    final height = MediaQuery.of(context).size.height;
    if (offset > height) {
      // if offset is more height, disable expanded height
      if (_expandedHeight != null) {
        setState(() {
          _expandedHeight = null;
        });
      }
    } else {
      // if offset is less, keep increasing the height to offset 0
      setState(() {
        _expandedHeight = height - offset;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    final double width = MediaQuery.of(context).size.width;

     Widget navBarBackground() {
         return Stack(...)
     }

     return Scaffold(
      backgroundColor: Colors.white,
      endDrawer: EndDrawer(),
      body: CustomScrollView(
        controller: _scrollController,
        slivers: [
          NavBar(
            background: navBarBackground(),
            expandedHeight: _expandedHeight,
          ),
          SliverList(
            delegate: SliverChildListDelegate(
              [
                highlights(),
                androidIosDesktop(),
                multiplatform(),
                catchPhrase(),
                contact(),
                const Footer(),
              ],
            ),
          )
        ],
      ),
    );
  }
} //HomeView

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM