Repository: flutter
Issue: Best practice to handle route loading
Labels: framework,d: api docs,f: routes,platform-web,P3,team-web,triaged-web

I know that the official stance for the onGenerateRoute function is that it can get called numerous times. There are multiple issues (one I also have raised in the past) that suggest the Navigator getting called multiple times inside the generateRoute function is a bug but it is a given that a builder function will get called any number of times. What I am looking for here is a best practice to handle the above case. My problem is as follows:
    I have a flutter web app that shows something on the screen based on the url (with an id query parameter) it is provided, let's say the calculations needed to do when this widget loads are heavy. These calculations right now run on the initState of that widget. From the print statements it is evident that these are run even if the widget is not on top of the Navigation Stack, which is also a problem because when I first load the app from the browser I expect to only have one page and not a number of pages stacked which is equal to the number of times the onGenerateRoute function was called.


main.dart
```
import 'package:flutter/material.dart';
import 'package:url_strategy/url_strategy.dart';
import 'router.dart' as router;

void main() {
  setPathUrlStrategy();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      onGenerateRoute: router.generateRoute,
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.id});

  final String? id;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    print('this is a serious calculation with id ${widget.id}');
    super.initState();
    for (int i = 0; i < 3; i++) {
      Future.delayed(const Duration(seconds: 5), () {
        print(
            'api calls made for ${widget.id} and now we will run further calculations');
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.id.toString()),
        ),
        body: const Center());
  }
}
```
router.dart
```
import 'package:flutter/material.dart';

import 'main.dart';

Route<dynamic> generateRoute(RouteSettings settings) {
  RoutingData? routingData = settings.name?.getRoutingData;
  print('routing data is ${routingData!.route}');
  switch (routingData.route) {
    case '/books/':
      print('with slash');
      return filterIdAndBuildHomePage(routingData, settings);
    case '/books':
      print('without slash');

      return filterIdAndBuildHomePage(routingData, settings);
    default:
      return MaterialPageRoute(
          builder: (context) {
            return const MyHomePage(
              id: null,
            );
          },
          settings: settings);
  }
}

MaterialPageRoute<dynamic> filterIdAndBuildHomePage(
    RoutingData routingData, RouteSettings settings) {
  String? id = routingData['id'];
  return MaterialPageRoute(
      builder: (context) {
        return MyHomePage(
          id: id,
        );
      },
      settings: settings);
}

class RoutingData {
  final String route;
  final Map<String, String> _queryParameters;

  RoutingData({
    required this.route,
    required Map<String, String> queryParameters,
  }) : _queryParameters = queryParameters;

  operator [](String key) => _queryParameters[key];
}

extension StringExtension on String {
  RoutingData get getRoutingData {
    Uri uriData = Uri.parse(this);
    return RoutingData(
      queryParameters: uriData.queryParameters,
      route: uriData.path,
    );
  }
}
```
My app also uses the url_strategy package which just removes the '#' from the url path, it should not have any effect on this issue, so my pubspec.yaml is this:
```
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
  flutter:
    sdk: flutter
  url_strategy: ^0.2.0  


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2

dev_dependencies:
  flutter_test:
    sdk: flutter
```

Steps to reproduce:

1. Copy/Paste above code in main.dart and create a new file called router.dart in the same directory, also copy/paste the router.dart code provided above to the new file. Next add the url_strategy: ^0.2.0 dependency on pubspec.yaml and you are good to run the project in web simulation.

2. Add this url `/books/?id=Frankenstein/sth/couldGoOn/` next to your localhost, it should look something like this
![image](https://user-images.githubusercontent.com/68654336/213742168-7832d25e-3a0f-436d-9e77-786dfd2857c1.png)
and hit Enter or reload the browser.

Terminal output
```
routing data is /
routing data is /books
without slash
routing data is /books/
with slash
routing data is /books/
with slash
routing data is /books/
with slash
routing data is /books/
with slash
2 this is a serious calculation with id null
this is a serious calculation with id Frankenstein
this is a serious calculation with id Frankenstein/sth
this is a serious calculation with id Frankenstein/sth/couldGoOn
this is a serious calculation with id Frankenstein/sth/couldGoOn/
//These got called after the app loads and all but the last are not present on screen
6 api calls made for null and now we will run further calculations
3 api calls made for Frankenstein and now we will run further calculations
3 api calls made for Frankenstein/sth and now we will run further calculations
3 api calls made for Frankenstein/sth/couldGoOn and now we will run further calculations
3 api calls made for Frankenstein/sth/couldGoOn/ and now we will run further calculations
```
It is also evident that the number of builds of the route function is equal to the number of slashes + 1 for default route (in the example above I can press the back button 6 times). This always happens. Since it can get built any number of times, shouldn't I see different build counts across app runs? It seems closely related to the number of slashes. Navigation Stack is also the number of builds, and CPU consumes resources to run everything beneath the top page as well. 

My question is how can I implement a solution to run this heavy function, once this whole routing behavior is done? Where do I run it if not the init state of the widget and how do I start the app without 5 Nagivation stack pages beneath the top one (number which is depending on the url) ?

I apologize for any formating errors beforehand. Thank you.

**Target Platform: Flutter Web
**Target OS version/browser: Google Chrome, Mozilla , etc
**Devices:**

Flutter doctor
```
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 3.3.10, on Microsoft Windows [Version 10.0.19044.2251], locale en-US)
[!] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    X Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/windows#android-setup for more details.
[√] Chrome - develop for the web
[X] Visual Studio - develop for Windows
    X Visual Studio not installed; this is necessary for Windows development.
      Download at https://visualstudio.microsoft.com/downloads/.
      Please install the "Desktop development with C++" workload, including all of its default components
[√] Android Studio (version 2021.3)
[√] VS Code (version 1.74.3)
[√] Connected device (3 available)
[√] HTTP Host Availability
```

</details>