6
votes

Masquer la barre de navigation inférieure lors du défilement vers le bas et vice versa

 screenshot

J'ai une liste dans le corps et la barre de navigation inférieure. Je souhaite masquer la barre de navigation inférieure avec une animation de glissement vers le bas lorsque la liste des articles est défilée vers le bas et visible avec une animation de glissement vers le haut lorsqu'elle fait défiler vers le haut. Comment faire?


0 commentaires

3 Réponses :


7
votes

Voici le code.

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)),
      ],
    );
  }
}

Remarque : j'ai essayé bottomNavigationBar mais les choses ne fonctionnaient pas comme prévu, j'ai donc créé une sorte de barre de navigation inférieure et vous pouvez modifier davantage le code pour votre usage.

Capture d'écran

 entrez la description de l'image ici


0 commentaires

5
votes

Code de travail avec 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),
                    ),
                  )
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Modèle de travail


1 commentaires

Cela devrait être la réponse acceptée pour le moment! Au moins jusqu'à ce que l'équipe Flutter fasse une mise à niveau



6
votes

Bien que la solution de Naveen fonctionne parfaitement, je n'ai pas aimé l'idée d'utiliser setState pour gérer la visibilité de la barre de navigation. Chaque fois que l'utilisateur change de direction de défilement, la page d'accueil entière, y compris la barre d'applications et le corps, se reconstruit, ce qui peut être une opération coûteuse. J'ai créé une classe distincte pour gérer la visibilité qui utilise un ValueNotifier pour suivre l'état masqué actuel.

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'),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),

Il ne vous reste plus qu'à créer une instance finale de HideNavbar dans votre widget HomePage.

body: CustomScrollView(
          controller: hiding.controller,
          ...

Passez maintenant le ScrollController de l'instance à ListView ou CustomScrollView corps de votre Scaffold.

final HideNavbar hiding = HideNavbar();

Puis entourez votre bottomNavigationBar d'un ValueListenableBuilder qui prend le ValueNotifier de l'instance HideNavbar et définit la propriété height de bottomNavigationBar sur 0 ou sur toute autre valeur en fonction de l'état du 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;
          }
        }
      },
    );
  }
}

Cette approche garde la page d'accueil comme un StatelessWidget, évite d'innombrables reconstructions et ne nécessite aucune bibliothèque externe. Vous pouvez également l'implémenter en tant qu'approche basée sur le flux, mais cela nécessiterait une autre bibliothèque telle que dart: async et ne changerait rien.


0 commentaires