skip to Main Content

My app has a bottom navigation bar that shows or hides according to the direction of the scrolling:

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


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

  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  final ScrollController _scrollController = ScrollController();
  // The item list where the data is stored.
  List<String> items = []; 
  // Flags used to check the loading status.
  bool loading = false, allLoaded = false;

  // Function that loads data in the item list.
  mockFetch() async {
    // No more data to load.
    if (allLoaded) {
      return;
    }   

    // Set the loading flag to true to prevent this function to be called several times.
    setState(() {
      loading = true;
    }); 

    // Simulates the delay that might occurs during an API call.
    await Future.delayed(Duration(milliseconds: 500));

    // If the item list is higher or equal to 60, an empty array is returned to simulate
    // the end of the data stream. If not, 20 more items are generated. 
    List<String> newData = items.length >= 60 ? [] : List.generate(20, (index) => "List Item ${index + items.length}");

    // Add the new data to the item list.
    if (newData.isNotEmpty) {
      items.addAll(newData);
    }

    // Reset the flags.
    setState(() {
      loading = false;
      // Returns false if the array is empty.
      allLoaded = newData.isEmpty;
    });
  }

  @override
  void initState() {
    super.initState();
    // Call the mockFetch() function when the state of MyHomePage is initialized for the first time.
    mockFetch();
    // Set a listener.
    _scrollController.addListener(() {

      if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent && !loading) {
        mockFetch();
      }
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.deepOrange,
        title: Text("Home Page"),
      ),
      body: LayoutBuilder(builder: (context, constraints) {
        if (items.isNotEmpty) {
          return Stack(
            // Dispay the data.
            children: [
              ListView.separated(
                // Assign the scroll controller to the ListView.
                controller: _scrollController,
                itemBuilder: (context, index) {
                  // Display the newly loaded items.
                  if (index < items.length) {
                    return ListTile(
                      title: Text(items[index]),
                    );
                  }
                  // An extra item has been added to the index of the list meaning that
                  // all the items have been loaded.
                  else {
                    // Inform the user that there is no more data to load.
                    return Container(
                      width: constraints.maxWidth,
                      height: 50,
                      child: Center(
                        child: Text("Nothing more to load"),
                      ),
                    );
                  }
              },
              // Add a separator between each item.
              separatorBuilder: (context, index) {
                return Divider(height: 1);
              },
              // Add an extra item to the length of the list when all of the items are loaded.
              itemCount: items.length + (allLoaded ? 1 : 0)),

              if (loading)...[
                // Display a progress indicator at the bottom of the list whenever some data is loading.
                Positioned(
                  left: 0,
                  bottom: 0,
                  child: Container(
                    width: constraints.maxWidth,
                    height: 80,
                    child: Center(
                      child: CircularProgressIndicator(),
                    ),
                  )
                )
              ]
            ],
          );
        }
        else {
          return Container(
            child: Center(
              child: CircularProgressIndicator(),
            ),
          );
        }

      }
    ),

      bottomNavigationBar: AnimatedBuilder(
        animation: _scrollController,
        builder: (context, child) {
          return AnimatedContainer(
            duration: Duration(milliseconds: 300),
            height: _scrollController.hasClients && _scrollController.position.userScrollDirection == ScrollDirection.reverse ? 0: 100,
            child: child,
          );  
        },  
        child: BottomNavigationBar(
          backgroundColor: Colors.amber[200],
          items: [
            BottomNavigationBarItem(
              icon: Icon(Icons.home),
              label: 'Home',
            ),  
            BottomNavigationBarItem(
              icon: Icon(Icons.child_friendly),
              label: 'Child',
            ),  
          ],  
        ),  
      ),  
    ); // Scaffold
  }

But when the bottom navigation bar shows or hides I get the following error

A RenderFlex overflowed by 14 pixels on the bottom.

I tried to use Expanded, Flexible, SingleChildScrollView with the bottom navigation bar but I still gets this error.
Can someone show me how to fix this ?

4

Answers


  1. return AnimatedContainer(
                duration: const Duration(milliseconds: 300),
                height: _scrollController.hasClients && _scrollController.position.userScrollDirection == ScrollDirection.reverse ? 0 : kBottomNavigationBarHeight,
                child: SingleChildScrollView(
                  physics: const NeverScrollableScrollPhysics(),
                  child: BottomNavigationBar(
                    backgroundColor: Colors.amber[200],
                    items: const [
                      BottomNavigationBarItem(
                        icon: Icon(Icons.home),
                        label: 'Home',
                      ),
                      BottomNavigationBarItem(
                        icon: Icon(Icons.child_friendly),
                        label: 'Child',
                      ),
                    ],
                  ),
                ),
              );
    
    Login or Signup to reply.
  2. Try below code and wrap your BottomNavigationBar inside SizedBox and give it to height on your need, Wrap SizedBox inside SingleChildScrollView as well.

    SingleChildScrollView(
          child: SizedBox(
            height: MediaQuery.of(context).size.height * .15,
            child: BottomNavigationBar(
              backgroundColor: Colors.amber[200],
              items: const [
                BottomNavigationBarItem(
                  icon: Icon(Icons.home),
                  label: 'Home',
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.child_friendly),
                  label: 'Child',
                ),
              ],
            ),
          ),
        ),
    
    Login or Signup to reply.
  3. You can achieve using of hidable 1.0.3 package for achieve the functionality.

    Full Code:

    import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';
    import 'package:hidable/hidable.dart';
    
    class Home extends StatefulWidget {
      Home({Key? key}) : super(key: key);
    
      @override
      State<Home> createState() => _HomeState();
    }
    
    class _HomeState extends State<Home> {
      final ScrollController _scrollController = ScrollController();
      // The item list where the data is stored.
      List<String> items = [];
      // Flags used to check the loading status.
      bool loading = false, allLoaded = false;
    
      // Function that loads data in the item list.
      mockFetch() async {
        // No more data to load.
        if (allLoaded) {
          return;
        }
    
        // Set the loading flag to true to prevent this function to be called several times.
        setState(() {
          loading = true;
        });
    
        // Simulates the delay that might occurs during an API call.
        await Future.delayed(Duration(milliseconds: 500));
    
        // If the item list is higher or equal to 60, an empty array is returned to simulate
        // the end of the data stream. If not, 20 more items are generated.
        List<String> newData = items.length >= 60 ? [] : List.generate(20, (index) => "List Item ${index + items.length}");
    
        // Add the new data to the item list.
        if (newData.isNotEmpty) {
          items.addAll(newData);
        }
    
        // Reset the flags.
        setState(() {
          loading = false;
          // Returns false if the array is empty.
          allLoaded = newData.isEmpty;
        });
      }
    
      @override
      void initState() {
        super.initState();
        // Call the mockFetch() function when the state of MyHomePage is initialized for the first time.
        mockFetch();
        // Set a listener.
        _scrollController.addListener(() {
          if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent && !loading) {
            mockFetch();
          }
        });
      }
    
      @override
      void dispose() {
        super.dispose();
        _scrollController.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            backgroundColor: Colors.deepOrange,
            title: Text("Home Page"),
          ),
          body: LayoutBuilder(builder: (context, constraints) {
            if (items.isNotEmpty) {
              return Stack(
                // Dispay the data.
                children: [
                  ListView.separated(
                      physics: ClampingScrollPhysics(),
                      // Assign the scroll controller to the ListView.
                      controller: _scrollController,
                      itemBuilder: (context, index) {
                        // Display the newly loaded items.
                        if (index < items.length) {
                          return ListTile(
                            title: Text(items[index]),
                          );
                        }
                        // An extra item has been added to the index of the list meaning that
                        // all the items have been loaded.
                        else {
                          // Inform the user that there is no more data to load.
                          return Container(
                            width: constraints.maxWidth,
                            height: 50,
                            child: Center(
                              child: Text("Nothing more to load"),
                            ),
                          );
                        }
                      },
                      // Add a separator between each item.
                      separatorBuilder: (context, index) {
                        return Divider(height: 1);
                      },
                      // Add an extra item to the length of the list when all of the items are loaded.
                      itemCount: items.length + (allLoaded ? 1 : 0)),
                  if (loading) ...[
                    // Display a progress indicator at the bottom of the list whenever some data is loading.
                    Positioned(
                        left: 0,
                        bottom: 0,
                        child: Container(
                          width: constraints.maxWidth,
                          height: 80,
                          child: Center(
                            child: CircularProgressIndicator(),
                          ),
                        ))
                  ]
                ],
              );
            } else {
              return Container(
                child: Center(
                  child: CircularProgressIndicator(),
                ),
              );
            }
          }),
          bottomNavigationBar: Hidable(
            controller: _scrollController,
            wOpacity: false,
            child: SizedBox(
              height: 200,
              width: MediaQuery.of(context).size.width,
              child: BottomNavigationBar(
                backgroundColor: Colors.amber[200],
                items: const [
                  BottomNavigationBarItem(
                    icon: Icon(Icons.home),
                    label: 'Home',
                  ),
                  BottomNavigationBarItem(
                    icon: Icon(Icons.child_friendly),
                    label: 'Child',
                  ),
                ],
              ),
            ),
          ),
        ); // Scaffold
      }
    }
    
    Login or Signup to reply.
  4. I’m Trying to Solve your Problem:

    To fix this issue, you can try wrapping the ListView with an Expanded widget:

    Stack(
      children: [
        Expanded(
          child: ListView.separated(
            controller: _scrollController,
            itemBuilder: (context, index) {
              // ...
            },
            separatorBuilder: (context, index) {
              return Divider(height: 1);
            },
            itemCount: items.length + (allLoaded ? 1 : 0),
          ),
        ),
        if (loading) ...[
          // ...
        ],
      ],
    ),
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search