Flutter:在检测到滚动时隐藏和显示应用程序栏

时间:2019-03-07 23:25:54

标签: animation scroll flutter appbar

我在应用栏动画方面遇到麻烦,我在应用中使用SilverAppBar。因此,问题是当我在列表的中间并向上滚动时,应用程序栏不会出现,但仅在滚动到达项目列表的顶部时出现。我已经测试了snap参数,并给了它true,但是没有得到我期望的结果。我有关于为此创建自定义动画的想法,但是我对Flutter不太了解,而且如果可以添加参数的方法或其他适合我情况的小部件,那就太好了。

我正在使用的演示的实际代码:

  Widget _search() => Container(
        color: Colors.grey[400],
        child: SafeArea(
            child: Container(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: TextField(
              enabled: false,
              style: TextStyle(fontSize: 16, color: Colors.white),
              decoration: InputDecoration(
                prefix: SizedBox(width: 12),
                hintText: "Search",
                contentPadding:
                    EdgeInsets.symmetric(horizontal: 32.0, vertical: 14.0),
                border: InputBorder.none,
              ),
            ),
          ),
        )),
      );

  Container _buildBody() {
    return Container(
        child: new GridView.count(
      crossAxisCount: 2,
      children: List.generate(100, (index) {
        return Center(
          child: Text(
            'Item $index',
            style: Theme.of(context).textTheme.headline,
          ),
        );
      }),
    ));
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        resizeToAvoidBottomPadding: false,
        body: new NestedScrollView(
            headerSliverBuilder:
                (BuildContext context, bool innerBoxIsScrolled) {
              return <Widget>[
                new SliverAppBar(
                  title: Text("Demo",
                      style: TextStyle(
                        color: Colors.white,
                      )),
                  pinned: false,
                  floating: true,
                  forceElevated: innerBoxIsScrolled,
                ),
              ];
            },
            body: new Column(children: <Widget>[
              _search(),
              new Expanded(child: _buildBody())
            ])));
  }

我现在得到的结果是: Image 1

true赋予snap参数后得到的结果: Image 2

WhatsApp,Facebook,LinkedIn等许多应用程序都有这个动画应用程序栏。为了进一步说明我对这个动画应用程序栏的期望,我添加了一个Google Play商店示例,其中显示了所需的动画:Play Store example

3 个答案:

答案 0 :(得分:4)

通过使用带有 NestedScrollView 的 SliverAppbar,我能够使带有 Tabbar 的浮动 Appbar 类似于 WhatsApp 的浮动 Appbar。记得在 NestedScrollView 中添加 floatHeaderSliv​​ers: true。 Link to sample code

import 'dart:math';
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: CustomSliverAppbar(),
    );
  }
}

class CustomSliverAppbar extends StatefulWidget {
  @override
  _CustomSliverAppbarState createState() => _CustomSliverAppbarState();
}

class _CustomSliverAppbarState extends State<CustomSliverAppbar>
    with SingleTickerProviderStateMixin {
  TabController _tabController;

  @override
  void initState() {
    _tabController = TabController(
      initialIndex: 0,
      length: 2,
      vsync: this,
    );
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NestedScrollView(
        floatHeaderSlivers: true,
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
          return <Widget>[
            SliverAppBar(
              title: Text(
                "WhatsApp type sliver appbar",
              ),
              centerTitle: true,
              pinned: true,
              floating: true,
              bottom: TabBar(
                  indicatorColor: Colors.black,
                  labelPadding: const EdgeInsets.only(
                    bottom: 16,
                  ),
                  controller: _tabController,
                  tabs: [
                    Text("TAB A"),
                    Text("TAB B"),
                  ]),
            ),
          ];
        },
        body: TabBarView(
          controller: _tabController,
          children: [
            TabA(),
            const Center(
              child: Text('Display Tab 2',
                  style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _tabController.dispose();
    super.dispose();
  }
}

class TabA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scrollbar(
      child: ListView.separated(
        separatorBuilder: (context, child) => Divider(
          height: 1,
        ),
        padding: EdgeInsets.all(0.0),
        itemCount: 30,
        itemBuilder: (context, i) {
          return Container(
            height: 100,
            width: double.infinity,
            color: Colors.primaries[Random().nextInt(Colors.primaries.length)],
          );
        },
      ),
    );
  }
}

Link to sample code

答案 1 :(得分:1)

我在使用刷新指示器的CustomScrollView和SliverAppbar上遇到了类似的问题,最终创建了自己的自定义应用栏。

    import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';

    class HomeView extends StatefulWidget {
      @override
      HomeState createState() => HomeState();
    }

    class HomeState extends State<HomeView> with SingleTickerProviderStateMixin {
      bool _isAppbar = true;
      ScrollController _scrollController = new ScrollController();

      @override
      void initState() {
        super.initState();
        _scrollController.addListener(() {
          if (_scrollController.position.userScrollDirection ==
              ScrollDirection.reverse) {
            appBarStatus(false);
          }
          if (_scrollController.position.userScrollDirection ==
              ScrollDirection.forward) {
            appBarStatus(true);
          }
        });
      }

      void appBarStatus(bool status) {
        setState(() {
          _isAppbar = status;
        });
      }

      @override
      Widget build(BuildContext context) {
        return SafeArea(
          child: Scaffold(
            appBar: PreferredSize(
              preferredSize: Size.fromHeight(kToolbarHeight),
              child: AnimatedContainer(
                height: _isAppbar ? 55.0 : 0.0,
                duration: Duration(milliseconds: 200),
                child: CustomAppBar(),
              ),
            ),
            body: ListView.builder(
              controller: _scrollController,
              itemCount: 20,
              itemBuilder: (BuildContext context, int index) {
                return container();
              },
            ),
          ),
        );
      }
    }

    Widget container() {
      return Container(
        height: 80.0,
        color: Colors.pink,
        margin: EdgeInsets.all(8.0),
        width: 100,
        child: Center(
            child: Text(
          'Container',
          style: TextStyle(
            fontSize: 18.0,
          ),
        )),
      );
    }

    class CustomAppBar extends StatefulWidget {
      @override
      AppBarView createState() => new AppBarView();
    }

    class AppBarView extends State<CustomAppBar> {
      @override
      Widget build(BuildContext context) {
        return AppBar(
          backgroundColor: Colors.white,
          leading: InkWell(
            onTap: () => {},
            child: new Padding(
              padding: const EdgeInsets.all(8.0),
              child: CircleAvatar(
                backgroundColor: Colors.white,
                child: ClipOval(
                  child: Image.network(
                      'https://images.squarespace-cdn.com/content/5aee389b3c3a531e6245ae76/1530965251082-9L40PL9QH6PATNQ93LUK/linkedinPortraits_DwayneBrown08.jpg?format=1000w&content-type=image%2Fjpeg'),
                ),
              ),
            ),
          ),
          actions: <Widget>[
            IconButton(
              alignment: Alignment.centerLeft,
              icon: Icon(
                Icons.search,
                color: Colors.black,
              ),
              onPressed: () {},
            ),
          ],
          title: Container(
            alignment: Alignment.centerLeft,
            child: Text("Custom Appbar", style: TextStyle(color: Colors.black),)
          ),
        );
      }
    }

答案 2 :(得分:0)

要使此功能正常工作,您将需要使用CustomScrollView小部件而不是NestedScrollViewGoogle Documentation

这是一个有效的示例:

class MyHomeState extends State<MyHome> {
@override
Widget build(BuildContext context) {
  return Scaffold(
    body: CustomScrollView(
      slivers: <Widget>[
        const SliverAppBar(
          pinned: false,
          snap: false,
          floating: true,
          flexibleSpace: FlexibleSpaceBar(
            title: Text('Demo'),
          ),
        ),
        SliverGrid(
          gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
            maxCrossAxisExtent: 200.0,
            mainAxisSpacing: 10.0,
            crossAxisSpacing: 10.0,
            childAspectRatio: 4.0,
          ),
          delegate: SliverChildBuilderDelegate(
            (BuildContext context, int index) {
              return Container(
                alignment: Alignment.center,
                color: Colors.teal[100 * (index % 9)],
                child: Text('grid item $index'),
              );
            },
            childCount: 50,
          ),
        ),
      ],
    )
  );
}
}

Example of this running here