简体   繁体   English

Flutter BottomNavigationBar 与 AnimatedContainer - 是什么导致 RenderFlex 溢出错误?

[英]Flutter BottomNavigationBar with AnimatedContainer - what is causing a RenderFlex overflow error?

My Flutter app uses a BottomNavigationBar wrapped in an AnimatedContainer.我的 Flutter 应用程序使用包装在 AnimatedContainer 中的 BottomNavigationBar。 When the animation takes place (activated by scrolling the list) a RenderFlex overflow error occurs.当动画发生(通过滚动列表激活)时,会发生 RenderFlex 溢出错误。 I can't work out what is causing this to happen.我无法弄清楚导致这种情况发生的原因。

I've stripped down the project to bare bones code in the hope that someone could try it out and identify the issue.我已经将项目简化为基本代码,希望有人可以尝试并找出问题所在。

The main class:主要类:

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

  @override
  State<TestMain> createState() => _TestMain();
}

class BottomNavBarItemData {
  String label;Icon icon;Widget screen;
  BottomNavBarItemData({required this.label,required this.icon,required this.screen});
}

late ScrollController mainScrollController;

class _TestMain extends State<TestMain> {
  int _selectedIndex = 0;
  bool _isVisible = true;

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

  final List<BottomNavBarItemData> screens = [
    BottomNavBarItemData(
      icon: const Icon(Icons.home, size: 25.0, color: Colors.red),
      label: 'Page1',
      screen: const Screen1(),
    ),
    BottomNavBarItemData(
      icon: const Icon(Icons.home, size: 25.0, color: Colors.red),
      label: 'Page2',
      screen: const Screen2(),
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      extendBody: true,
      body: SafeArea(
        child: IndexedStack(
          index: _selectedIndex,
          children: [
            ...screens.map((e) => e.screen).toList(),
          ],
        ),
      ),
      bottomNavigationBar: AnimatedContainer(
        duration: const Duration(milliseconds: 400),
        height: _isVisible ? 70 : 0.0,
        child: SizedBox(
          child: BottomNavigationBar(
            type: BottomNavigationBarType.fixed,
            backgroundColor: Colors.orange,
            currentIndex: _selectedIndex,
            selectedIconTheme: IconThemeData(color: Colors.white),
            selectedItemColor: Colors.white,
            selectedFontSize: 14,
            unselectedFontSize: 14,
            unselectedIconTheme: const IconThemeData(
              color: Colors.lightBlueAccent,
            ),
            unselectedItemColor: Colors.lightBlueAccent,
            onTap: _onItemTapped,
            items: screens.map((e) => BottomNavigationBarItem(
                    label: e.label,
                    icon: e.icon,
                  ),
                ).toList(),
          ),
        ),
      ),
    );
  }

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }
}

And the two screens called by the main class:主类调用的两个屏幕:

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

  @override
  State<Screen1> createState() => _Screen1();
}

class _Screen1 extends State<Screen1> {

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      child: SingleChildScrollView(
        controller: mainScrollController,
        physics: const AlwaysScrollableScrollPhysics(),
        child: Column(children: [
          Container(height: 150, color: Colors.blue),
          Container(height: 150, color: Colors.white),
          Container(height: 150, color: Colors.blue),
          Container(height: 150, color: Colors.white),
          Container(height: 150, color: Colors.blue),
          Container(height: 150, color: Colors.white),
        ],),
      ),
    );
  }
}

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

  @override
  State<Screen2> createState() => _Screen2();
}

class _Screen2 extends State<Screen2> {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
    );
  }
}

Container gets overflow because the inner item space is greater to the animation size, like on 35 it will show the overflow.容器溢出是因为内部项目空间大于动画大小,就像在 35 上它会显示溢出。 You can use different animation, but it will be little difference.您可以使用不同的动画,但差别不大。

You can use SizeTransition for this case您可以在这种情况下使用SizeTransition

class _TestMain extends State<TestMain> with SingleTickerProviderStateMixin {
  int _selectedIndex = 0;
  bool _isVisible = true;

  late final AnimationController _controller = AnimationController(
    duration: const Duration(milliseconds: 400),
    vsync: this,
  )..forward();
  late final Animation<double> _animation = CurvedAnimation(
    parent: _controller,
    curve: Curves.fastOutSlowIn,
  );

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

  @override
  void initState() {
    _isVisible = true;
    mainScrollController = ScrollController();
    mainScrollController.addListener(() {
      if (mainScrollController.position.userScrollDirection ==
          ScrollDirection.reverse) {
        _controller.reverse();
        setState(() {
          _isVisible = false;
        });
      }
      if (mainScrollController.position.userScrollDirection ==
          ScrollDirection.forward) {
        _controller.forward();
        setState(() {
          _isVisible = true;
        });
      }
    });
    super.initState();
  }

  final List<BottomNavBarItemData> screens = [
    BottomNavBarItemData(
      icon: const Icon(Icons.home, size: 25.0, color: Colors.red),
      label: 'Page1',
      screen: const Screen1(),
    ),
    BottomNavBarItemData(
      icon: const Icon(Icons.home, size: 25.0, color: Colors.red),
      label: 'Page2',
      screen: const Screen2(),
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      extendBody: true,
      body: SafeArea(
        child: IndexedStack(
          index: _selectedIndex,
          children: [
            ...screens.map((e) => e.screen).toList(),
          ],
        ),
      ),
      bottomNavigationBar: SizeTransition(
        sizeFactor: _animation,
        child: SizedBox(
          child: BottomNavigationBar(
            type: BottomNavigationBarType.fixed,
            backgroundColor: Colors.orange,
            currentIndex: _selectedIndex,
            selectedIconTheme: IconThemeData(color: Colors.white),
            selectedItemColor: Colors.white,
            selectedFontSize: 14,
            unselectedFontSize: 14,
            unselectedIconTheme: const IconThemeData(
              color: Colors.lightBlueAccent,
            ),
            unselectedItemColor: Colors.lightBlueAccent,
            onTap: _onItemTapped,
            items: screens
                .map(
                  (e) => BottomNavigationBarItem(
                    label: e.label,
                    icon: e.icon,
                  ),
                )
                .toList(),
          ),
        ),
      ),
    );
  }

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }
}

or或者

 bottomNavigationBar: AnimatedScale(
        duration: const Duration(milliseconds: 400),
        scale: _isVisible ? 1 : 0.0,
        alignment: Alignment.bottomCenter,

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

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