Repository navigation
Pages on Navigator stack rebuild when a new page is pushed #11655
Copy link
Copy link
Closed
Labels
c: performanceRelates to speed or footprint issues (see "perf:" labels)Relates to speed or footprint issues (see "perf:" labels)customer: crowdAffects or could affect many people, though not necessarily a specific customer.Affects or could affect many people, though not necessarily a specific customer.f: routesNavigator, Router, and related APIs.Navigator, Router, and related APIs.frameworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.
Description
Steps to Reproduce
Pages on Navigator stack rebuild when a new page is pushed onto the stack.
Suppose the pages were numbered numerically, in the order they are pushed onto the stack i.e. page 1 -> page 2 -> page 3. When we push Page 3, previous pages from page 1 to 2 rebuild as evident from the print statements. Is this a bug?
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(id: 1),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.id}) : super(key: key);
final int id;
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
print('Page ${widget.id} built!');
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.id.toString()),
),
body: new Center(
child: new RaisedButton(
child: new Text('Open next page'),
onPressed: () {
Navigator.of(context).push(
new MaterialPageRoute(
builder: (_) => new MyHomePage(id: widget.id + 1),
maintainState: true
)
);
}
)
),
);
}
}Logs
When page 2 is pushed, the log outputs
I/flutter (21176): Page 2 built!
I/flutter (21176): Page 2 built!
I/flutter (21176): Page 1 built!
Expected:
I/flutter (21176): Page 2 built! // only
When page 3 is pushed, the log outputs
I/flutter (21176): Page 2 built!
I/flutter (21176): Page 3 built!
I/flutter (21176): Page 3 built!
I/flutter (21176): Page 2 built!
I/flutter (21176): Page 1 built!
Expected:
I/flutter (21176): Page 3 built! // only
Flutter Doctor
[√] Flutter (on Microsoft Windows [Version 10.0.15063], locale en-US, channel master)
• Flutter at C:\Users\tzm\Downloads\flutter_sdk
• Framework revision b156a0f054 (5 days ago), 2017-08-11 22:01:40 -0700
• Engine revision fef7d827d6
• Tools Dart version 1.25.0-dev.9.0
[√] Android toolchain - develop for Android devices (Android SDK 25.0.3)
• Android SDK at C:\Users\tzm\AppData\Local\Android\sdk
• Platform android-26, build-tools 25.0.3
• Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
• Java version OpenJDK Runtime Environment (build 1.8.0_112-release-b06)
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
c: performanceRelates to speed or footprint issues (see "perf:" labels)Relates to speed or footprint issues (see "perf:" labels)customer: crowdAffects or could affect many people, though not necessarily a specific customer.Affects or could affect many people, though not necessarily a specific customer.f: routesNavigator, Router, and related APIs.Navigator, Router, and related APIs.frameworkflutter/packages/flutter repository. See also f: labels.flutter/packages/flutter repository. See also f: labels.
Activity
kerego commentedon Nov 30, 2017
I have noticed this behavior but couldn't figure out what is the problem, can someone from the team comment on this?
Hixie commentedon Nov 30, 2017
This is working as intended. In general, you should assume that all widgets can rebuild at any time, that they don't is mostly just an optimisation.
In particular, routes will rebuild because their navigator state has changed so they might need to update how they draw back buttons and the like.
lidroider commentedon Jul 6, 2018
@Hixie
I have same issue with @cornelius-tan
I understand that when navigator state has changed, routes will rebuild
But I follow the document of MaterialPageRoute, I found out maintainState = true to keep state of previous route.
So what is the "actual" meaning of maintainState and what is the state in maintainState?
Please give me your opinion
p/s: sorry for my English
Hixie commentedon Jul 13, 2018
maintainState controls whether the route's widgets and render objects are kept around or not. If you set it to false on a route, then when another route is pushed on top, the entire subtree is thrown away until that route is popped.
lidroider commentedon Aug 8, 2018
@Hixie
Is there any way to prevent rebuild a widget when another route is pushed on top?
Ex: I have route
/a-> Widget A,/b-> Widget B. When I push route/bon/a, Widget A is rebuilt and Widget B is built; when i pop/b, Widget A is rebuilt again.I expect Widget A not to be rebuilt when i push and pop
/bPlease help me
Thks
Hixie commentedon Aug 13, 2018
You should generally assume that every widget will be rebuild every frame, and design your build methods to be idempotent (i.e. running them multiple times should do nothing different than running them once), and fast (so that there's no problem with running them a lot). In practice we run build methods less often than that but you shouldn't rely on that.
shivamlko commentedon Sep 21, 2018
@Hixie I have FutureBuilder and List to populate in Page 1, when I open Page 2, FutureBuilder runs again and duplicates data, and if I pop Page 2 , then again FutureBuilder runs, hence 3 times duplicate data. i am stuck in this
How to maintain and best way to do not execute any FutureBuilder or StreamBuilder when I call setState()
for now I have to make bool var to stop each widget duplicate data ,I this code of Pagination works but when I call but duplicate when pop from Page 2 or setState() is called
zoechi commentedon Sep 24, 2018
@shivamlko you'll need to restructure your code so that the data is not fetched again. For example you can cache it and only fetch from the server if the cached value is
nulland1990 commentedon Jan 13, 2019
wow. The way widget builds is too complicated for the programmer.
rrousselGit commentedon Mar 4, 2019
I'll reference it here because this is a very common question:
This is intended. If this causes any problem for you, then you're likely using widgets incorrectly.
You may want to read https://stackoverflow.com/questions/52249578/how-to-deal-with-unwanted-widget-build, which showcase one of the common pitfall and how to solve it.
cosinus84 commentedon Mar 27, 2019
Because of this behaviour when you want to show the status bar when you push A->B: the status bar appears and dissapears again because widget A is rebuild again after widget B build. Widget A hides the status bar, and B has the status bar.
Afsar-Pasha commentedon Apr 13, 2019
Any progress?
103 remaining items
rrousselGit commentedon Nov 29, 2019
On a related note @Hixie, do you think it'd be feasible to have a "true" fix to this rebuild by changing Overlay's
_Theatrecall from:to:
where the previous Stack would be implemented manually by TheatreRender instead of using composition?
From my understanding, it is the reparenting from onstage to offstage that is the original problem this issue was about.
If that's a reasonable fix, I might give it a try and implement it myself.
Hixie commentedon Nov 29, 2019
Please file a new bug to discuss specific proposals.
TreeLiked commentedon Dec 8, 2019
How do I solve this problem? There are many images on my homepage. When I navigate to a new page or return, the images are reloaded, causing the page to flicker.
cuong1112035 commentedon Dec 8, 2019
You can use
constto to create your images. They should not reload when their parent reloadTreeLiked commentedon Dec 8, 2019
@cuong1112035 Oh, however, the const constructor requires constants during compilation. The picture here is a network picture, which is a link requested from the server. I cannot use the const constructor.
cuong1112035 commentedon Dec 8, 2019
you could try
AsyncMemoizerto load your remote images just one time. Or maybe just wrap your remote image within aconstwidget. Hope this helpTreeLiked commentedon Dec 8, 2019
@cuong1112035 Sorry, can you give me an example about using the image const constructor? I have been delaying this issue for a long time, thank you very much~ I am using CachedNetWorkImage
tony123S commentedon Dec 8, 2019
@TreeLiked Are you using bloc?
TreeLiked commentedon Dec 8, 2019
cuong1112035 commentedon Dec 8, 2019
"const constructor requires constants during compilation" you are right. I just check it and there are no ways to use
constto prevent your image widget to rebuild.Also, CachedNetWorkImage caches your images already so that you don't need to use AsyncMemoizer for caching.
Sorry for my stupid suggestion, hope this issue will be resolved in next Flutter versions
TreeLiked commentedon Dec 9, 2019
oh, yes, thank you still~
SBDavid commentedon Dec 9, 2019
I fix the "rebuild" problem by adding a key to Overlay build function.
First I add a GlobalKey to
OverlayEntryclass. For example_key2. This key will be used inTickerMode, the purpose is to give a Key to overlay offstage elements.And the next step is add
_key2to build function.And then Flutter diff algorithm will know how to update offstage element properly, other than just inflateWidget. The inflateWidget method will reload images which were used in offstage elements, thus cause serious performance issue.
Hixie commentedon Dec 10, 2019
Please file a new bug to discuss specific proposals.