我有 a simple app,有 3 个底部导航项目。
但是,当我单击下面的底部导航项之一时,整个页面都在移动,iOS 从右到左移动,android 从底部移动。
我希望我的底部导航应用保持在同一个位置,如 traditionnal Bottom Nav Bar
我知道使用 StatefulWidget
、int _selectedIndex = 0;
和 static const List<Widget> _widgetOptions
的底部导航栏的 I can find 工作实现,
但我的应用程序已经很大,并且在下面定义的 3 个步骤中使用了不同的机制:(您可以在我的 github repo 上找到):
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';
}
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),
);
}
}
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);
}
}
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 有问题
答案 0 :(得分:1)
动画来自MaterialPageRoute
。往返于 Widget
时,您需要提供 PageRoute
。 PageRoute
负责告诉框架如何从一个屏幕转换到另一个屏幕。通过使用 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;
},
);
}