隐藏向下滚动的底部导航栏,反之亦然

时间:2019-01-29 05:17:33

标签: dart flutter bottomnavigationview

screenshot

我在正文和底部导航栏中有一个列表。我想在帖子列表向下滚动时用向下滑动的动画隐藏底部导航栏,在向上滚动时可以向上滑动的动画可见。怎么做?

3 个答案:

答案 0 :(得分:5)

尽管Naveen的解决方案运行完美,但我不喜欢使用setState处理导航栏可见性的想法。每次用户更改滚动方向时,包括应用程序栏和主体在内的整个首页都会重新生成,这可能是一项昂贵的操作。我创建了一个单独的类来处理使用ValueNotifier来跟踪当前隐藏状态的可见性。

class HideNavbar {
  final ScrollController controller = ScrollController();
  ValueNotifier<bool> visible = ValueNotifier<bool>(true);

  HideNavbar() {
    visible.value = true;
    controller.addListener(
      () {
        if (controller.position.userScrollDirection ==
            ScrollDirection.reverse) {
          if (visible.value) {
            visible.value = false;
          }
        }

        if (controller.position.userScrollDirection ==
            ScrollDirection.forward) {
          if (!visible.value) {
            visible.value = true;
          }
        }
      },
    );
  }
}

现在,您要做的就是在Homepage小部件中创建HideNavbar的最终实例。

final HideNavbar hiding = HideNavbar();

现在将实例的ScrollController传递到ListView的{​​{1}}或CustomScrollView正文中。

Scaffold

然后用body: CustomScrollView( controller: hiding.controller, ... 包围bottomNavigationBar,该ValueListenableBuilderValueNotifier实例获取HideNavbar并将bottomNavigationBar的height属性设置为0或任何其他值值取决于ValueNotifier的状态。

bottomNavigationBar: ValueListenableBuilder(
        valueListenable: hiding.visible,
        builder: (context, bool value, child) => AnimatedContainer(
          duration: Duration(milliseconds: 500),
          height: value ? 56.0 : 0.0,
          child: Wrap(
            children: <Widget>[
              BottomNavigationBar(
                type: BottomNavigationBarType.fixed,
                backgroundColor: Colors.blue,
                fixedColor: Colors.white,
                unselectedItemColor: Colors.white,
                items: [
                  BottomNavigationBarItem(
                    icon: Icon(Icons.home),
                    title: Text('Home'),
                  ),
                  BottomNavigationBarItem(
                    icon: Icon(Icons.card_giftcard),
                    title: Text('Offers'),
                  ),
                  BottomNavigationBarItem(
                    icon: Icon(Icons.account_box),
                    title: Text('Account'),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),

这种方法将主页保持为StatelessWidget,避免了无数次重建,并且不需要任何外部库。您还可以将其实现为基于流的方法,但是这将需要诸如dart:async之类的另一个库,并且不会进行任何更改。

答案 1 :(得分:2)

这是代码。

void main() => runApp(MaterialApp(home: Scaffold(body: MyApp())));

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  ScrollController _scrollController;
  double _containerMaxHeight = 56, _offset, _delta = 0, _oldOffset = 0;

  @override
  void initState() {
    super.initState();
    _offset = 0;
    _scrollController = ScrollController()
      ..addListener(() {
        setState(() {
          double offset = _scrollController.offset;
          _delta += (offset - _oldOffset);
          if (_delta > _containerMaxHeight)
            _delta = _containerMaxHeight;
          else if (_delta < 0) _delta = 0;
          _oldOffset = offset;
          _offset = -_delta;
        });
      });
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        return Stack(
          alignment: Alignment.bottomCenter,
          children: <Widget>[
            ListView.builder(
              physics: ClampingScrollPhysics(),
              controller: _scrollController,
              itemCount: 20,
              itemBuilder: (context, index) => ListTile(title: Text(index.toString())),
            ),
            Positioned(
              bottom: _offset,
              width: constraints.maxWidth,
              child: Container(
                width: double.infinity,
                height: _containerMaxHeight,
                color: Colors.grey[300],
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: <Widget>[
                    _buildItem(Icons.home, "Home"),
                    _buildItem(Icons.blur_circular, "Collection"),
                    _buildItem(Icons.supervised_user_circle, "Community"),
                    _buildItem(Icons.notifications, "Notifications"),
                  ],
                ),
              ),
            ),
          ],
        );
      },
    );
  }

  Widget _buildItem(IconData icon, String title) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Icon(icon, size: 28),
        Text(title, style: TextStyle(fontSize: 10)),
      ],
    );
  }
}

注意:我尝试了bottomNavigationBar,但是操作没有按预期进行,因此我创建了自己的底部导航栏,您可以进一步修改代码以供使用。

屏幕截图

enter image description here

答案 2 :(得分:0)

使用BottomNavigationBar的代码。

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

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

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

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  ScrollController _hideBottomNavController;

  bool _isVisible;

  @override
  initState() {
    super.initState();
    _isVisible = true;
    _hideBottomNavController = ScrollController();
    _hideBottomNavController.addListener(
      () {
        if (_hideBottomNavController.position.userScrollDirection ==
            ScrollDirection.reverse) {
          if (_isVisible)
            setState(() {
              _isVisible = false;
            });
        }
        if (_hideBottomNavController.position.userScrollDirection ==
            ScrollDirection.forward) {
          if (!_isVisible)
            setState(() {
              _isVisible = true;
            });
        }
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: CustomScrollView(
          controller: _hideBottomNavController,
          shrinkWrap: true,
          slivers: <Widget>[
            SliverPadding(
              padding: const EdgeInsets.all(10.0),
              sliver: SliverList(
                delegate: SliverChildBuilderDelegate(
                  (context, index) => _getItem(context),
                  childCount: 20,
                ),
              ),
            ),
          ],
        ),
      ),
      bottomNavigationBar: AnimatedContainer(
        duration: Duration(milliseconds: 500),
        height: _isVisible ? 56.0 : 0.0,
        child: Wrap(
          children: <Widget>[
            BottomNavigationBar(
              type: BottomNavigationBarType.fixed,
              backgroundColor: Colors.blue,
              fixedColor: Colors.white,
              unselectedItemColor: Colors.white,
              items: [
                BottomNavigationBarItem(
                  icon: Icon(Icons.home),
                  title: Text('Home'),
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.card_giftcard),
                  title: Text('Offers'),
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.account_box),
                  title: Text('Account'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  _getItem(BuildContext context) {
    return Card(
      elevation: 3,
      margin: EdgeInsets.all(8),
      child: Row(
        children: <Widget>[
          Expanded(
            child: Container(
              padding: EdgeInsets.all(8),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Text(
                      'Item',
                      style:
                          TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                    ),
                  )
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Working Model