go_router ShellRoute, how to use the go method to jump routes and process the carried parameters

115 Views Asked by At

go_router ShellRoute, how to use the go method to jump routes and process the carried parameters

code:

// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

final GlobalKey<NavigatorState> _rootNavigatorKey =
    GlobalKey<NavigatorState>(debugLabel: 'root');
final GlobalKey<NavigatorState> _shellNavigatorKey =
    GlobalKey<NavigatorState>(debugLabel: 'shell');

void main() {
  runApp(ShellRouteExampleApp());
}

class ShellRouteExampleApp extends StatelessWidget {
  ShellRouteExampleApp({super.key});

  final GoRouter _router = GoRouter(
    navigatorKey: _rootNavigatorKey,
    initialLocation: '/a',
    debugLogDiagnostics: true,
    routes: <RouteBase>[
      StatefulShellRoute.indexedStack(
          builder: (context, state, navigationShell) {
            return ScaffoldWithNavBar(
              child: navigationShell,
            );
          },
          branches: [
            StatefulShellBranch(routes: [
              GoRoute(
                path: '/a',
                name: 'a',
                builder: (BuildContext context, GoRouterState state) {
                  return ScreenA(
                    login: bool.parse(
                        state.uri.queryParameters['login'] ?? 'false'),
                  );
                },
              ),
            ]),
            StatefulShellBranch(routes: [
              GoRoute(
                path: '/b',
                builder: (BuildContext context, GoRouterState state) {
                  return const ScreenB();
                },
              ),
            ]),
          ]),
      GoRoute(
        path: '/login',
        name: 'login',
        parentNavigatorKey: _rootNavigatorKey,
        builder: (BuildContext context, GoRouterState state) {
          return const LoginScreen();
        },
      ),
    ],
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      routerConfig: _router,
    );
  }
}

class ScaffoldWithNavBar extends StatelessWidget {
  const ScaffoldWithNavBar({
    required this.child,
    super.key,
  });

  /// The widget to display in the body of the Scaffold.
  /// In this sample, it is a Navigator.
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: child,
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'A Screen',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.business),
            label: 'B Screen',
          ),
        ],
        currentIndex: _calculateSelectedIndex(context),
        onTap: (int idx) => _onItemTapped(idx, context),
      ),
    );
  }

  static int _calculateSelectedIndex(BuildContext context) {
    final String location = GoRouterState.of(context).uri.toString();
    if (location.startsWith('/a')) {
      return 0;
    }
    if (location.startsWith('/b')) {
      return 1;
    }
    if (location.startsWith('/c')) {
      return 2;
    }
    return 0;
  }

  void _onItemTapped(int index, BuildContext context) {
    switch (index) {
      case 0:
        GoRouter.of(context).go('/a');
        break;
      case 1:
        GoRouter.of(context).go('/b');
        break;
      case 2:
        GoRouter.of(context).go('/c');
        break;
    }
  }
}

class ScreenA extends StatefulWidget {
  final bool login;

  const ScreenA({super.key, required this.login});

  @override
  State<ScreenA> createState() => _ScreenAState();
}

class _ScreenAState extends State<ScreenA> {
  @override
  void initState() {
    super.initState();
    afterLogin();
  }

  void afterLogin() {
    print('login success :${widget.login}');
    //todo someting then setState
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Text('Screen A login:${widget.login}'),
            widget.login == true
                ? const Text('login success')
                : const Text('login fail'),
            TextButton(
              onPressed: () {
                context.push('/login');
              },
              child: const Text('push login'),
            ),
          ],
        ),
      ),
    );
  }
}

/// The second screen in the bottom navigation bar.
class ScreenB extends StatelessWidget {
  /// Constructs a [ScreenB] widget.
  const ScreenB({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            const Text('Screen B'),
            TextButton(
              onPressed: () {
                GoRouter.of(context).go('/b/details');
              },
              child: const Text('View B details'),
            ),
          ],
        ),
      ),
    );
  }
}

/// The third screen in the bottom navigation bar.
class LoginScreen extends StatelessWidget {
  /// Constructs a [LoginScreen] widget.
  const LoginScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            const Text('login'),
            TextButton(
              onPressed: () {
                GoRouter.of(context)
                    .goNamed('a', queryParameters: {'login': 'true'});
              },
              child: const Text('login'),
            ),
          ],
        ),
      ),
    );
  }
}

I want to click "push login" in "A Screen" to jump to the login page, then click "login" to jump back to "A Screen" on the home page, and call the "afterLogin" method in it, but I can't trigger it using go. In any middle stage of life, push can’t be used to jump. What should I do?

1

There are 1 best solutions below

6
On

The problem is the method is not called anywhere. Call it by afterLogin(); under the build method in ScreenA to see the print.

It should look like this

class ScreenA extends StatelessWidget {
  final bool login;

  const ScreenA({super.key, required this.login});

  void afterLogin() {
    //todo
    print('login success :$login');
  }

  @override
  Widget build(BuildContext context) {
    afterLogin(); // calling the method
  ...

Edit

If ScreenA is a StatefulWidget, initState() is called once when the State object is instantiated. Pushing to LoginScreen then using the goNamed method, looks like it's goes to the previous instance of ScreenA.

Try one of these methods:

Method 1 instead of pushing from ScreenA, do go instead. However, this does not provide a previous page to go back to by a back button.

Method 2 pushing to the next screen and returning a value by popping.

// ScreenA
context.push('/login').then((value) {
  print('login success :$value');
  //todo someting then setState
  // setState(() {});
});

// LoginScreen
GoRouter.of(context).pop(true);