1

Consider the following StatelessWidget:

class SwitchScreen extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
        final testService = Provider.of<TestService>(context);  // Line 1
        Future( () { 
            Navigator.of(context).push(   // Segment 2
                 MaterialPageRoute(builder: (context) => TestScreen())  // Segment 2
            );    // Segment 2
        });

        return Scaffold(body: Center( child: Text("lol") ) );
    }
}

The widget is directly below the root in the widget tree and wrapped by a ChangeNotifierProvider:

void main() => runApp(new Main());

class Main extends StatefulWidget {
    _MainState createState() => _MainState();
}

class _MainState extends State<Main> {
    @override
    Widget build(BuildContext context) {
        return MaterialApp(
            title: 'SampleProgram',
            home: ChangeNotifierProvider<TestService>(
                builder: (_) { return TestService(); } ,
                child: SwitchScreen(),
            ),
        );
    }
}

The service associated with the provider, TestService, is currently empty. TestScreen is simply another StatelessWidget which includes AppBar wrapped inside a Scaffold.

I would expect the program to finish rendering the SwitchScreen, navigate to TestScreen to fulfill the future, and finally render the AppBar inside TestScreen. However, every time it enters the TestScreen view, something appears to trigger a rebuild of SwitchScreen. The app then bounces back to SwitchScreen, moves to TestScreen to fulfill the future, and repeats this process. By using debug Print statements I'm sure that the build method of SwitchScreen is called immediately after TestScreen finishes rendering.

The interesting thing is that if I comment out Line 1, the build method won't be re-triggered. Similarly if I replace the entirety of Segment 2 with anything else, say a print statement, the build method won't keep firing either. I suspected that Navigator is resulting in some value change in TestService, forcing SwitchScreen to rebuild, so I overrode the notifyListeners method in TestService since this method is the only way SwitchScreen can be affected by TestService.

class TestService with ChangeNotifier {
    @override
    void notifyListeners() {
        print("Triggering SwitchScreen's build method");
    }
}

But no string is printed out. Right now I'm very curious about what's causing the rebuilding and what roles do the Provider and the Navigator play in this. Any help would be really appreciated.

Yi Hong
  • 11
  • 1
  • 1
    You can't have such thing in your build method. See https://stackoverflow.com/questions/52249578/how-to-deal-with-unwanted-widget-build – Rémi Rousselet Jun 20 '19 at 21:24

1 Answers1

0

Instead of calling

final testService = Provider.of<TestService>(context);

Use

final testService = Provider.of<TestService>(context, listen: false);

Using the above line in a build method won’t cause this widget to rebuild when notifyListeners is called.

Mr Random
  • 1,992
  • 7
  • 20