Flutter BaseScreen 不断重建/不需要重建

时间:2021-03-16 14:32:38

标签: flutter

我有 a simple app,有 3 个底部导航项目。
但是,当我单击下面的底部导航项之一时,整个页面都在移动,iOS 从右到左移动,android 从底部移动。
我希望我的底部导航应用保持在同一个位置,如 traditionnal Bottom Nav Bar

enter image description here

我知道使用 StatefulWidgetint _selectedIndex = 0;static const List<Widget> _widgetOptions 的底部导航栏的 I can find 工作实现,

但我的应用程序已经很大,并且在下面定义的 3 个步骤中使用了不同的机制:(您可以在我的 github repo 上找到):

1. MaterialApp 定义了 _onGenerateRoute :

main.dart

MaterialApp(
      title: 'Keep bottom navigation',
      themeMode: ThemeMode.dark,
      onGenerateRoute: _generateRoute,
    );

main.dart

  Route<dynamic> _generateRoute(RouteSettings settings) {
    switch (settings.name) {
      case AppRoutes.computer:
        return MaterialPageRoute(builder: (context) => ComputerScreen());
      case AppRoutes.phone:
        return MaterialPageRoute(builder: (context) => PhoneScreen());
      case AppRoutes.person:
      default:
        return MaterialPageRoute(builder: (context) => PersonScreen());
    }
  }

route_config.dart

class AppRoutes {
  static const String computer = '/computer';
  static const String phone = '/phone';
  static const String person = '/person';
}

2.每个屏幕都将 BaseScreen 扩展为一个状态:

screens/computer_screen.dart


class ComputerScreen extends StatefulWidget {
  @override
  _ComputerScreenState createState() => _ComputerScreenState();
}

class _ComputerScreenState extends BaseScreenState<ComputerScreen> {
  @override
  String get currentRoute => AppRoutes.computer;

  @override
  Widget buildScreen(BuildContext context) {
    return Center(
      child:
          Text("Computer screen", style: Theme.of(context).textTheme.headline3),
    );
  }
}

3. Base Scene 定义了屏幕的基础:

base_screen.dart

import 'package:flutter/material.dart';
import 'package:keep_bottomnav/bottom_nav_bar.dart';

abstract class BaseScreenState<T extends StatefulWidget> extends State<T> {
  String get currentRoute;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: this.buildAppBar(context),
      body: this.buildScreen(context),
      bottomNavigationBar: this.buildBottomNavigationBar(context),
      floatingActionButton: this.buildFloatingActionButton(context),
    );
  }

  Widget buildScreen(BuildContext context);

  PreferredSizeWidget buildAppBar(BuildContext context) {
    return null;
  }

  Widget buildBottomNavigationBar(BuildContext context) {
    return BottomNavBar(
      selectedRoute: currentRoute,
      onSelectRoute: _onItemTapped,
    );
  }

  Widget buildFloatingActionButton(BuildContext context) {
    return null;
  }

  bool get centerFloatingActionButton => false;

  void _onItemTapped(String route) {
    Navigator.pushNamedAndRemoveUntil(context, route, (route) => false);
  }
}

4.底部导航栏的界面

import 'package:flutter/material.dart';
import 'package:keep_bottomnav/bottom_nav_bar_item.dart';
import 'package:keep_bottomnav/route_config.dart';

class BottomNavBar extends StatelessWidget {
  final String selectedRoute;
  final ValueChanged<String> onSelectRoute;

  BottomNavBar({this.selectedRoute, this.onSelectRoute});

  @override
  Widget build(BuildContext context) {
    double screenWidth = MediaQuery.of(context).size.width;
    double itemWidth = (screenWidth / 3);
    return Material(
      color: Colors.white,
      child: SafeArea(
        child: Container(
          padding: const EdgeInsets.only(bottom: 10),
          color: Colors.white,
          height: 65,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              _buildNavigationItem(
                AppRoutes.computer,
                icon: Icon(Icons.computer),
                title: "Computer 1",
                itemWidth: itemWidth,
              ),
              _buildNavigationItem(
                AppRoutes.phone,
                icon: Icon(Icons.phone),
                title: "Phone 2",
                itemWidth: itemWidth,
              ),
              _buildNavigationItem(
                AppRoutes.person,
                icon: Icon(Icons.person),
                title: "Person 3",
                itemWidth: itemWidth,
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildNavigationItem(String route,
      {Widget icon, String title, itemWidth}) {
    return BottomNavBarItem(
      isSelected: selectedRoute == route,
      icon: icon,
      title: title,
      minWidth: itemWidth,
      onTap: () {
        if (onSelectRoute != null) {
          onSelectRoute(route);
        }
      },
    );
  }
}
import 'package:flutter/material.dart';

class BottomNavBarItem extends StatelessWidget {
  final bool isSelected;
  final VoidCallback onTap;
  final Widget icon;
  final String title;
  final double minWidth;

  BottomNavBarItem(
      {Key key,
      @required this.icon,
      @required this.title,
      this.isSelected = false,
      this.onTap,
      this.minWidth = 50})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: this.onTap,
      child: ConstrainedBox(
        constraints: BoxConstraints(minWidth: 0),
        child: Container(
          decoration: BoxDecoration(
              border: Border(
                  top: BorderSide(
                      color: isSelected ? Colors.yellow : Colors.transparent,
                      width: 3.0))),
          padding: const EdgeInsets.only(top: 10, left: 10, right: 10),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              icon,
              Text(
                title.toUpperCase(),
                style: TextStyle(color: Colors.white, fontSize: 9.0),
              )
            ],
          ),
        ),
      ),
    );
  }
}

非常感谢您的帮助

在BottomNavItems 上使用Hero 有问题

1 个答案:

答案 0 :(得分:1)

动画来自MaterialPageRoute。往返于 Widget 时,您需要提供 PageRoutePageRoute 负责告诉框架如何从一个屏幕转换到另一个屏幕。通过使用 MaterialPageRoute,您可以使用它的预定义值添加您在上面看到的动画。要删除该 animation,您可以创建自己的 PageRoute,没有动画:

Route _createRoute() {
  return PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => MyPage(),
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      return child;
    },
  );
}

在导航器中使用 _createRoute() 而不是 MaterialPageRoute

Navigator.of(context).push(_createRoute());

问题是如何解决的:

  Route<dynamic> _generateRoute(RouteSettings settings) {
    switch (settings.name) {
      case AppRoutes.computer:
        return _createRoute(ComputerScreen());
      case AppRoutes.phone:
        return _createRoute(PhoneScreen());
      case AppRoutes.person:
      default:
        return _createRoute(PersonScreen());
    }
  }
}

Route _createRoute(Widget screen) {
  return PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => screen,
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      return child;
    },
  );
}