Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deep Location problem #29

Closed
csinesz opened this issue Jan 24, 2021 · 2 comments
Closed

Deep Location problem #29

csinesz opened this issue Jan 24, 2021 · 2 comments
Assignees
Labels
bug Something isn't working
Milestone

Comments

@csinesz
Copy link

csinesz commented Jan 24, 2021

If I going deeper in routing, and would like to go back with APP BAR left arrow - the URL doen’t change.
However - I press the back button in browser - URL is changing.

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

// SCREENS
class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home Screen'),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () => context.beamTo(BooksLocation()),
          child: Text('Go to books location'),
        ),
      ),
    );
  }
}

const List<Map<String, String>> books = [
  {
    'id': '1',
    'title': 'Stranger in a Strange Land',
    'author': 'Robert A. Heinlein',
  },
  {
    'id': '2',
    'title': 'Foundation',
    'author': 'Isaac Asimov',
  },
  {
    'id': '3',
    'title': 'Fahrenheit 451',
    'author': 'Ray Bradbury',
  },
];

class BooksScreen extends StatelessWidget {
  BooksScreen({this.titleQuery = ''});

  final String titleQuery;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Books'),
      ),
      body: ListView(
        children: books
            .where((book) =>
                book['title'].toLowerCase().contains(titleQuery.toLowerCase()))
            .map((book) => ListTile(
                  title: Text(book['title']),
                  subtitle: Text(book['author']),
                  onTap: () => Beamer.of(context).beamTo(
                    BooksLocation.withParameters(
                      path: {'id': book['id']},
                    ),
                  ),
                ))
            .toList(),
      ),
    );
  }
}

class BookDetailsScreen extends StatelessWidget {
  BookDetailsScreen({
    this.book,
  });

  final Map<String, String> book;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(book['title']),
      ),
      body: Column(
        children: [
          Text('Author: ${book['author']}'),
          SizedBox(
            height: 50.0,
          ),
          ElevatedButton(
            child: Text('GO DEEPER -> '),
            onPressed: () {
              Beamer.of(context).beamTo(
                BooksThirdLocation.withParameters(
                  path: {'id': book['id']},
                ),
              );
            },
          ),
        ],
      ),
    );
  }
}

class BookThirdScreen extends StatelessWidget {
  BookThirdScreen({
    this.book,
  });

  final Map<String, String> book;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(book['title']),
      ),
      body: Text('Third screen of book nr. ${book['author']}'),
    );
  }
}

// LOCATIONS
class HomeLocation extends BeamLocation {
  @override
  List<Page> get pages => [
        BeamPage(
          key: ValueKey('home'),
          page: HomeScreen(),
        ),
      ];

  @override
  String get pathBlueprint => '/';
}

class BooksLocation extends BeamLocation {
  BooksLocation() : super();

  BooksLocation.withParameters({
    Map<String, String> path,
    Map<String, String> query,
  }) : super.withParameters(path: path, query: query);

  @override
  List<Page> get pages => [
        ...HomeLocation().pages,
        BeamPage(
          key: ValueKey('books-${queryParameters['title'] ?? ''}'),
          page: BooksScreen(
            titleQuery: queryParameters['title'] ?? '',
          ),
        ),
        if (pathParameters.containsKey('id'))
          BeamPage(
            key: ValueKey('book-${pathParameters['id']}'),
            page: BookDetailsScreen(
              book: books
                  .firstWhere((book) => book['id'] == pathParameters['id']),
            ),
          ),
      ];

  @override
  String get pathBlueprint => '/books/:id';
}

class BooksThirdLocation extends BeamLocation {
  BooksThirdLocation() : super();

  BooksThirdLocation.withParameters({
    Map<String, String> path,
    Map<String, String> query,
  }) : super.withParameters(path: path, query: query);

  @override
  List<Page> get pages => [
        ...HomeLocation().pages,
        BeamPage(
          key: ValueKey('books-${queryParameters['title'] ?? ''}'),
          page: BooksScreen(
            titleQuery: queryParameters['title'] ?? '',
          ),
        ),
        if (pathParameters.containsKey('id'))
          BeamPage(
            key: ValueKey('book-${pathParameters['id']}'),
            page: BookDetailsScreen(
              book: books
                  .firstWhere((book) => book['id'] == pathParameters['id']),
            ),
          ),
        if (pathParameters.containsKey('id'))
          BeamPage(
            key: ValueKey('book-deeper-${pathParameters['id']}'),
            page: BookThirdScreen(
              book: books
                  .firstWhere((book) => book['id'] == pathParameters['id']),
            ),
          ),
      ];

  @override
  String get pathBlueprint => '/books/:id/deeper';
}

// APP
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Beamer(
      initialLocation: HomeLocation(),
      beamLocations: [
        HomeLocation(),
        BooksLocation(),
      ],
      notFoundPage: Scaffold(body: Center(child: Text('Not found'))),
      app: MaterialApp(),
    );
  }
}

void main() {
  runApp(MyApp());
}
@slovnicki
Copy link
Owner

@csinesz thanks for reporting this. From the top of my head, I think it's a problem related to something I was trying to solve yesterday (and thought I solved it, but was aware that solution is not ideal). I will test your example and try to fix this today.

@slovnicki slovnicki self-assigned this Jan 24, 2021
@slovnicki slovnicki added the bug Something isn't working label Jan 24, 2021
@slovnicki slovnicki changed the title Deep Location question Deep Location problem Jan 24, 2021
@slovnicki slovnicki added this to the v0.4.0 milestone Jan 24, 2021
@slovnicki
Copy link
Owner

I solved it, but it's a tricky use case and I'm not sure (yet) how to solve it better at the moment.

First, I noticed you forgot to put BooksThirdLocation() in Beamer.beamLocations, but that did not affect the nature of issue.

The problem was that both BooksLocation and BooksThirdLocation were compared to current stack of pages, but without any pathParameters. At this state then, they are the same. But, the solution was not just to compare everytime with pathParameters because then BooksLocation can never go to just /books without the id part.

So, I added an attribute called keepPathParametersOnPop (default false) to the BeamPage and then BooksThirdLocation should set it to true. In fact, any location that has pages beyond/after :id/ should set this to true.

I also refactored your BooksThirdLocation a little bit to be simpler. Notice that collection-if is not necessary on last BeamPage because there can not be a situation where :id doesn't exists when we're on books/:id/redeem. Because, what would that be... books//redeem? :)

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

// SCREENS
class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home Screen'),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () => context.beamTo(BooksLocation()),
          child: Text('Go to books location'),
        ),
      ),
    );
  }
}

const List<Map<String, String>> books = [
  {
    'id': '1',
    'title': 'Stranger in a Strange Land',
    'author': 'Robert A. Heinlein',
  },
  {
    'id': '2',
    'title': 'Foundation',
    'author': 'Isaac Asimov',
  },
  {
    'id': '3',
    'title': 'Fahrenheit 451',
    'author': 'Ray Bradbury',
  },
];

class BooksScreen extends StatelessWidget {
  BooksScreen({this.titleQuery = ''});

  final String titleQuery;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Books'),
      ),
      body: ListView(
        children: books
            .where((book) =>
                book['title'].toLowerCase().contains(titleQuery.toLowerCase()))
            .map((book) => ListTile(
                  title: Text(book['title']),
                  subtitle: Text(book['author']),
                  onTap: () => Beamer.of(context).beamTo(
                    BooksLocation.withParameters(
                      path: {'id': book['id']},
                    ),
                  ),
                ))
            .toList(),
      ),
    );
  }
}

class BookDetailsScreen extends StatelessWidget {
  BookDetailsScreen({
    this.book,
  });

  final Map<String, String> book;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(book['title']),
      ),
      body: Column(
        children: [
          Text('Author: ${book['author']}'),
          SizedBox(
            height: 50.0,
          ),
          ElevatedButton(
            child: Text('GO DEEPER -> '),
            onPressed: () {
              Beamer.of(context).beamTo(
                BooksThirdLocation.withParameters(
                  path: {'id': book['id']},
                ),
              );
            },
          ),
        ],
      ),
    );
  }
}

class BookThirdScreen extends StatelessWidget {
  BookThirdScreen({
    this.book,
  });

  final Map<String, String> book;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(book['title']),
      ),
      body: Text('Third screen of book nr. ${book['author']}'),
    );
  }
}

// LOCATIONS
class HomeLocation extends BeamLocation {
  @override
  List<BeamPage> get pages => [
        BeamPage(
          key: ValueKey('home'),
          page: HomeScreen(),
        ),
      ];

  @override
  String get pathBlueprint => '/';
}

class BooksLocation extends BeamLocation {
  BooksLocation() : super();

  BooksLocation.withParameters({
    Map<String, String> path,
    Map<String, String> query,
  }) : super.withParameters(path: path, query: query);

  @override
  List<BeamPage> get pages => [
        ...HomeLocation().pages,
        BeamPage(
          key: ValueKey('books-${queryParameters['title'] ?? ''}'),
          page: BooksScreen(
            titleQuery: queryParameters['title'] ?? '',
          ),
        ),
        if (pathParameters.containsKey('id'))
          BeamPage(
            key: ValueKey('book-${pathParameters['id']}'),
            page: BookDetailsScreen(
              book: books
                  .firstWhere((book) => book['id'] == pathParameters['id']),
            ),
          ),
      ];

  @override
  String get pathBlueprint => '/books/:id';
}

class BooksThirdLocation extends BeamLocation {
  BooksThirdLocation() : super();

  BooksThirdLocation.withParameters({
    Map<String, String> path,
    Map<String, String> query,
  }) : super.withParameters(path: path, query: query);

  @override
  List<BeamPage> get pages => [
        ...BooksLocation.withParameters(
          path: pathParameters,
          query: queryParameters,
        ).pages,
        BeamPage(
          key: ValueKey('book-deeper-${pathParameters['id']}'),
          keepPathParametersOnPop: true,
          page: BookThirdScreen(
            book:
                books.firstWhere((book) => book['id'] == pathParameters['id']),
          ),
        ),
      ];

  @override
  String get pathBlueprint => '/books/:id/deeper';
}

// APP
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Beamer(
      initialLocation: HomeLocation(),
      beamLocations: [
        HomeLocation(),
        BooksLocation(),
        BooksThirdLocation(),
      ],
      notFoundPage: Scaffold(body: Center(child: Text('Not found'))),
      app: MaterialApp(),
    );
  }
}

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

Let me know of any other issues you may encounter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants