Skip to content

Beamer is confusing fixed routes with dynamic routes when using RoutesLocationBuilder #686

@andersonaavila

Description

@andersonaavila

Describe the bug
When using RoutesLocationBuilder, Beamer is confusing fixed routes with dynamic routes.

'/'
'/books'
'/books/create'
'/books/:bookId'

If we call context.beamToNamed('/books/create') beamer confuses the word 'create' with the parameter :bookId.

Beamer version: v1.7.0, master

To Reproduce
Steps to reproduce the behavior:

  1. Copy and Simulate the code below
  2. Put breakpoints on each return BeamPage
  3. Click on Create Book button
  4. At the last breakpoint, see the error that the word 'create' is confused with the parameter :bookId

Expected behavior
Beamer should differentiate fixed routes from dynamic routes. The method context.beamToNamed('/books/create') should return the correct beamPage without confusion with '/books/:bookId' .

Screenshots

Image

Smartphone:

  • Device: Galaxy S24 FE - SM S721B
  • Device: Redmi Note 6 Pro
  • Simulator: Pixel 3a XL API 30

Additional context
Don't forget we are using RoutesLocationBuilder.

Example

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

// DATA
class Book {
  const Book(this.id, this.title, this.author);

  final int id;
  final String title;
  final String author;
}

const List<Book> books = [
  Book(1, 'Stranger in a Strange Land', 'Robert A. Heinlein'),
  Book(2, 'Foundation', 'Isaac Asimov'),
  Book(3, 'Fahrenheit 451', 'Ray Bradbury'),
];

// SCREENS
class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home Screen'),
      ),
      body: Column(
        children: [
          Center(
            child: ElevatedButton(
              onPressed: () => context.beamToNamed('/books'),
              child: const Text('See books'),
            ),
          ),
          Center(
            child: ElevatedButton(
              onPressed: () => context.beamToNamed('/books/create'),
              child: const Text('create book'),
            ),
          ),
        ],
      ),
    );
  }
}

class BooksScreen extends StatelessWidget {
  const BooksScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Books'),
      ),
      body: ListView(
        children: books
            .map(
              (book) => ListTile(
                title: Text(book.title),
                subtitle: Text(book.author),
                onTap: () => context.beamToNamed('/books/${book.id}'),
              ),
            )
            .toList(),
      ),
    );
  }
}

class BookDetailsScreen extends StatelessWidget {
  const BookDetailsScreen({super.key, required this.book});

  final Book? book;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(book?.title ?? 'Not Found'),
      ),
      body: book != null
          ? Padding(
              padding: const EdgeInsets.all(8.0),
              child: Text('Author: ${book!.author}'),
            )
          : const SizedBox.shrink(),
    );
  }
}

class BookCreateScreen extends StatelessWidget {
  const BookCreateScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('Book Create'),
        ),
        body: Center(
          child: Text('create a book here'),
        ));
  }
}

// APP
class MyApp extends StatelessWidget {
  MyApp({super.key});

  final routerDelegate = BeamerDelegate(
    locationBuilder: RoutesLocationBuilder(
      routes: {
        '/': (_, __, ___) {
          return const BeamPage(
              key: ValueKey(''), title: 'Home', child: HomeScreen());
        },
        '/books': (_, __, ___) {
          return const BeamPage(
              key: ValueKey('books'), title: 'Books', child: BooksScreen());
        },
        '/books/create': (_, __, ___) {
          return BeamPage(
              key: ValueKey('book-create'),
              title: 'Book create',
              child: BookCreateScreen());
        },
        '/books/:bookId': (_, state, ___) {
          final String bookIdParameter = state.pathParameters['bookId']!;
          final bookId = int.parse(bookIdParameter);
          final book = books.firstWhere((book) => book.id == bookId);
          return BeamPage(
              key: ValueKey('book-$bookIdParameter'),
              title: 'Book #$bookIdParameter',
              child: BookDetailsScreen(book: book));
        },
      },
    ).call,
    notFoundRedirectNamed: '/books',
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerDelegate: routerDelegate,
      routeInformationParser: BeamerParser(),
      backButtonDispatcher:
          BeamerBackButtonDispatcher(delegate: routerDelegate),
    );
  }
}

void main() => runApp(MyApp());

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions