Flutter BaseScreen 不断重建/不想要的重建
Posted
技术标签:
【中文标题】Flutter BaseScreen 不断重建/不想要的重建【英文标题】:Flutter BaseScreen keeps rebuilding / Not wanted rebuilds 【发布时间】:2021-06-13 20:36:52 【问题描述】:我有 a simple app,底部有 3 个导航项。 但是,当我单击下面的底部导航项之一时,整个页面都在移动,ios 从右到左,android 从底部移动。 我希望我的底部导航应用程序保持在同一个位置,如traditionnal Bottom Nav Bar
我知道 I can find 使用StatefulWidget
、int _selectedIndex = 0;
和static const List<Widget> _widgetOptions
的底部导航栏的工作实现,
但我的应用程序已经很大,并且在 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 Sceen 定义了屏幕的基础:
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。 BottomNavBar 界面
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 有问题
【问题讨论】:
它来自左/下的原因是因为您使用的是导航 @quoci 有没有办法用 Navigation 伪造底部导航栏的行为? 如果你不想要动画,你可以看到这个post 非常感谢! 【参考方案1】:动画来自MaterialPageRoute
。导航到Widget
和从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;
,
);
【讨论】:
非常感谢! 不客气。我很高兴它帮助了@DimitriLeurs以上是关于Flutter BaseScreen 不断重建/不想要的重建的主要内容,如果未能解决你的问题,请参考以下文章
Flutter 令人费解的流行为:setState 调用但不重建 widget 树
Flutter:StreamProvider 的奇怪行为,使用不完整数据重建的小部件
Flutter:检测任何在屏幕上不可见但在小部件树中的小部件的重建