0

I am coding an app with Flutter. I am using go_router: ^4.2.7 to navigate between pages. It's a learning app. In the section "my_course_materials_page.dart" there is a widget that opens a page passing a String that contains a URL for opening a pdf into a Container() (package is: syncfusion_flutter_pdfviewer: ^20.2.48-beta). I can open the pdf in the widget, but when I try to navigate to the page that should show the material (my_course_material_page.dart) the app crashes with the following error:

Exception has occurred.
_AssertionError ('package:go_router/src/configuration.dart': Failed assertion: line 74 pos 16: 'paramNames.contains(key)': unknown param "materialURL" for /login/myCourses/myCourseMaterials/myCourseMaterial)

Here are the various files:

  1. The button that opens the page is into "material_element_card.dart:
ElevatedButton(
  onPressed: () {
    context.goNamed(AppRoute.myCourseMaterial.name,
        params: {
          'materialURL': materialURL,
        });
  },
  style: ElevatedButton.styleFrom(
    minimumSize: const Size.fromHeight(60),
    shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16)),
  ),
  child: const Text('View PDF'),
),
  1. The page that will show the pdf: "my_course_material_page.dart"
import 'package:flutter/material.dart';
...
import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart';

class MyCourseMaterialPage extends StatelessWidget {
  MyCourseMaterialPage({required this.materialURL, super.key});
  final String materialURL;
  final PdfViewerController _pdfViewerController = PdfViewerController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(slivers: [
        const MyCourseSliverAppBar(
          courseId: 001,
          headerTitle: 'Material',
          courseTitle: 'MO20222023',
          courseLocation: '',
          courseStartDate: '2022-09-01',
          courseEndDate: '2023-06-01',
          courseHeaderImageBackground:
              'assets/images/course_placeholder.png',
        ),
        SliverToBoxAdapter(
          child: Column(
            children: [
              Container(
                decoration: BoxDecoration(
                  color: const Color.fromARGB(250, 30, 41, 62),
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Row(
                  children: [
                    Expanded(
                      child: IconButton(
                          onPressed: () {
                            _pdfViewerController.zoomLevel++;
                          },
                          icon: const Icon(Icons.zoom_in)),
                    ),
                    Expanded(
                      child: IconButton(
                          onPressed: () {
                            _pdfViewerController.zoomLevel--;
                          },
                          icon: const Icon(Icons.zoom_out)),
                    ),
                    Expanded(
                      child: IconButton(
                          onPressed: () {
                            _pdfViewerController.previousPage();
                          },
                          icon: const Icon(Icons.navigate_before)),
                    ),
                    Expanded(
                      child: IconButton(
                          onPressed: () {
                            _pdfViewerController.nextPage();
                          },
                          icon: const Icon(Icons.navigate_next)),
                    ),
                  ],
                ),
              ),
              kSpacerSmall,
              SizedBox(
                width: double.infinity,
                height: 420,
                child: SfPdfViewer.network(
                  materialURL,
                  pageLayoutMode: PdfPageLayoutMode.single,
                  controller: _pdfViewerController,
                ),
              ),
            ],
          ),
        ),
      ]),
    );
  }
}
  1. the file that holds all the go_router logic: "app_router.dart"
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
... (all pages/screens import)

enum AppRoute {
  start,
  login,
  register,
  coursesCatalog,
  coursesCatalogCategory,
  courseDetails,
  news,
  singleNews,
  school,
  contact,
  userSection,
  myDashboard,
  myProfile,
  myCourses,
  myCourse,
  myCourseDashboard,
  myCourseCalendar,
  myCourseProgram,
  myCourseMaterials,
  myCourseMaterial,
  myCourseHomeworks,
  myCourseHomework,
  myCourseNotifications,
  myCourseNotification,
  myCourseVimeo,
  myCourseZoom,
  mySettings,
  myNotifications,
  myNotification,
  myHelp,
}

final goRouter = GoRouter(
  initialLocation: '/',
  debugLogDiagnostics: true,
  routes: [
    GoRoute(
      path: '/',
      name: AppRoute.start.name,
      builder: (context, state) => const StartPage(),
      routes: [
        // COURSES CATALOG SECTION
        GoRoute(
          path: 'coursesCatalog',
          name: AppRoute.coursesCatalog.name,
          pageBuilder: (context, state) => CustomTransitionPage<void>(
            key: state.pageKey,
            child: const CoursesCatalog(),
            transitionsBuilder:
                (context, animation, secondaryAnimation, child) =>
                    FadeTransition(opacity: animation, child: child),
          ),
...
        // LOGIN SECTION
        GoRoute(
          path: 'login',
          name: AppRoute.login.name,
          pageBuilder: (context, state) => CustomTransitionPage<void>(
            key: state.pageKey,
            child: const LoginPage(),
            transitionsBuilder:
                (context, animation, secondaryAnimation, child) =>
                    FadeTransition(opacity: animation, child: child),
          ),
          routes: [
            // USER SECTION
            GoRoute(
              path: 'myDashboard',
              name: AppRoute.myDashboard.name,
              pageBuilder: (context, state) => CustomTransitionPage<void>(
                key: state.pageKey,
                child: const MyDashboardPage(),
                transitionsBuilder:
                    (context, animation, secondaryAnimation, child) =>
                        FadeTransition(opacity: animation, child: child),
              ),
              routes: [
                // MY PROFILE SECTION
              ],
            ),
            GoRoute(
              path: 'myProfile',
              name: AppRoute.myProfile.name,
              // builder: (context, state) => const MyProfilePage(),
              // path: '/fade',
              pageBuilder: (context, state) => CustomTransitionPage<void>(
                key: state.pageKey,
                child: const MyProfilePage(),
                transitionsBuilder:
                    (context, animation, secondaryAnimation, child) =>
                        FadeTransition(opacity: animation, child: child),
              ),
            ),
            // MY COURSES SECTION
            GoRoute(
              path: 'myCourses',
              name: AppRoute.myCourses.name,
              pageBuilder: (context, state) => CustomTransitionPage<void>(
                key: state.pageKey,
                child: const MyCoursesPage(),
                transitionsBuilder:
                    (context, animation, secondaryAnimation, child) =>
                        FadeTransition(opacity: animation, child: child),
              ),
              routes: [
                GoRoute(
                  path: 'myCourse',
                  name: AppRoute.myCourse.name,
                  pageBuilder: (context, state) => MaterialPage(
                    key: state.pageKey,
                    fullscreenDialog: true,
                    child: const MyCourseGeneralInfoPage(),
                  ),
                ),
                GoRoute(
                  path: 'myCourseCalendar',
                  name: AppRoute.myCourseCalendar.name,
                  pageBuilder: (context, state) => MaterialPage(
                    key: state.pageKey,
                    fullscreenDialog: true,
                    child: const MyCourseCalendarPage(),
                  ),
                ),
                GoRoute(
                  path: 'myCourseMaterials',
                  name: AppRoute.myCourseMaterials.name,
                  pageBuilder: (context, state) => MaterialPage(
                    key: state.pageKey,
                    fullscreenDialog: false,
                    child: const MyCourseMaterialsPage(),
                  ),
                  routes: [
                    GoRoute(
                      path: 'myCourseMaterial',
                      name: AppRoute.myCourseMaterial.name,
                      pageBuilder: (context, state) {
                        final materialSingleURL = state.params['materialURL']!;
                        return MaterialPage(
                          // arguments: materialURL,
                          key: state.pageKey,
                          fullscreenDialog: false,
                          child: MyCourseMaterialPage(
                            materialURL: materialSingleURL,
                          ),
                        );
                      },
                    ),
                  ],
                ),
               ...
            ),
          ],
        ),
      ],
      ...
    ),
  ],
  errorBuilder: (context, state) => const NotFoundScreen(),
);

The specific route that is called by the ElevatedButton (1.), is:

GoRoute(
  path: 'myCourseMaterial',
  name: AppRoute.myCourseMaterial.name,
  pageBuilder: (context, state) {
    final materialSingleURL = state.params['materialURL']!;
    return MaterialPage(
      key: state.pageKey,
      fullscreenDialog: false,
      child: MyCourseMaterialPage(
        materialURL: materialSingleURL,
      ),
    );
  },
),

All other routes work perfectly and the app opens every page. This exception is thrown only when I tap on the EB that has the param materialURL that should be passed to the my_course_material_page.dart, in order to show the pdf.

Any hint/idea?

sachax
  • 75
  • 1
  • 8

1 Answers1

3

state.params is only used to specify path parameters:

https://gorouter.dev/parameters

In your case you may want to use a query parameter instead:

context.goNamed('${AppRoute.myCourseMaterial.name}/materialURL=$materialURL');

I suspect that you'll need to URL-encode/decode the materialURL since it will be added to the target URL.

Alternatively you could use the extra parameter.

bizz84
  • 1,964
  • 21
  • 34
  • Yes. Using the extra parameter makes all working perfectly: i can pass a string in the extra object and retrieve it in the following route. Thanks. – sachax Sep 13 '22 at 16:36