更改路线时,Flutter Provider 会多次更新
Flutter Provider updates multiple time when changing route
我使用了一个模型的 ChangeNotifierProvider,它有一个“数字”属性 和一个为其设置随机值的方法。有2条路线。在第一个中,有一个变量使用 context.select
或 context.watch
(无关紧要)侦听此 属性,然后在 Text
小部件中使用。还有一个调用模型方法的按钮,该方法设置随机值。最初,如果没有屏幕发生变化,按下此按钮时,将按预期重建小部件。推送到另一条路线然后返回到第一条路线时会出现问题。如果再次按下按钮,小部件将重建两次,因为提供者出于某种原因也被更改了两次。在屏幕之间切换的次数越多,当您按下设置随机值的按钮时,小部件重建的次数就越多。下面您可以看到来自 Dart DevTools 的带有日志的屏幕截图。在这里我举了一个小例子,只是为了说明我的问题,但是在另一个项目中,不是简单的文本小部件而是带有数据的 table ,每次屏幕更改后性能的下降变得更加明显,因为对导致模型更改的用户操作的响应变得更长。
class MyModel extends ChangeNotifier {
int _number = 0;
int get number => _number;
void setRandomNumber() {
print('Set random number');
_number = Random().nextInt(1000);
notifyListeners();
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<MyModel>(
create: (context) => MyModel(),
child: MaterialApp(
routes: {
'/home': (context) => MyHomePage(),
'/second': (context) => SecondPage(),
},
initialRoute: '/home',
),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final number = context.select<MyModel, num>((model) => model.number);
print('REBUILD');
return Scaffold(
body: SafeArea(
child: Column(
children: [
Text(number.toString()),
TextButton(
child: Text('Go to Page 2'),
onPressed: () {
print('Go to Page 2');
Navigator.pushNamed(context, '/second');
},
),
TextButton(
child: Text('Set Random Number'),
onPressed: () => context.read<MyModel>().setRandomNumber(),
),
],
),
),
);
}
}
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: TextButton(
child: Text('Go to Home Page'),
onPressed: () {
print('Go to Home Page');
Navigator.pushNamed(context, '/home');
},
),
);
}
}
控制台:
flutter: Set random number
flutter: REBUILT
flutter: Go to Page 2
flutter: Go to Home Page
flutter: Set random number
flutter: REBUILT
flutter: REBUILT
DevTools 中的日志
这是因为你推的时候没有弹出。你有一堆屏幕 1、屏幕 2、屏幕 1。你按下屏幕 1 上的按钮,它会执行设置的随机数函数,并通知屏幕 1 的两个实例上的听众。
在任何时候都不要在导航堆栈中出现重复屏幕通常是一个好习惯。像 auto_route 这样的包在某种程度上加强了这一点,尽管你也可以在不使用包的情况下管理它。当 popping 可以将您带到同一屏幕时,请勤奋并警惕推送。
我使用了一个模型的 ChangeNotifierProvider,它有一个“数字”属性 和一个为其设置随机值的方法。有2条路线。在第一个中,有一个变量使用 context.select
或 context.watch
(无关紧要)侦听此 属性,然后在 Text
小部件中使用。还有一个调用模型方法的按钮,该方法设置随机值。最初,如果没有屏幕发生变化,按下此按钮时,将按预期重建小部件。推送到另一条路线然后返回到第一条路线时会出现问题。如果再次按下按钮,小部件将重建两次,因为提供者出于某种原因也被更改了两次。在屏幕之间切换的次数越多,当您按下设置随机值的按钮时,小部件重建的次数就越多。下面您可以看到来自 Dart DevTools 的带有日志的屏幕截图。在这里我举了一个小例子,只是为了说明我的问题,但是在另一个项目中,不是简单的文本小部件而是带有数据的 table ,每次屏幕更改后性能的下降变得更加明显,因为对导致模型更改的用户操作的响应变得更长。
class MyModel extends ChangeNotifier {
int _number = 0;
int get number => _number;
void setRandomNumber() {
print('Set random number');
_number = Random().nextInt(1000);
notifyListeners();
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<MyModel>(
create: (context) => MyModel(),
child: MaterialApp(
routes: {
'/home': (context) => MyHomePage(),
'/second': (context) => SecondPage(),
},
initialRoute: '/home',
),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final number = context.select<MyModel, num>((model) => model.number);
print('REBUILD');
return Scaffold(
body: SafeArea(
child: Column(
children: [
Text(number.toString()),
TextButton(
child: Text('Go to Page 2'),
onPressed: () {
print('Go to Page 2');
Navigator.pushNamed(context, '/second');
},
),
TextButton(
child: Text('Set Random Number'),
onPressed: () => context.read<MyModel>().setRandomNumber(),
),
],
),
),
);
}
}
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: TextButton(
child: Text('Go to Home Page'),
onPressed: () {
print('Go to Home Page');
Navigator.pushNamed(context, '/home');
},
),
);
}
}
控制台:
flutter: Set random number
flutter: REBUILT
flutter: Go to Page 2
flutter: Go to Home Page
flutter: Set random number
flutter: REBUILT
flutter: REBUILT
DevTools 中的日志
这是因为你推的时候没有弹出。你有一堆屏幕 1、屏幕 2、屏幕 1。你按下屏幕 1 上的按钮,它会执行设置的随机数函数,并通知屏幕 1 的两个实例上的听众。
在任何时候都不要在导航堆栈中出现重复屏幕通常是一个好习惯。像 auto_route 这样的包在某种程度上加强了这一点,尽管你也可以在不使用包的情况下管理它。当 popping 可以将您带到同一屏幕时,请勤奋并警惕推送。