slovnicki / beamer

A routing package built on top of Router and Navigator's pages API, supporting arbitrary nested navigation, guards and more.
MIT License
591 stars 129 forks source link

Multiple BeamLocations with same subclass breaks page rendering. #658

Open jwalton opened 9 months ago

jwalton commented 9 months ago

Describe the bug

If you have two ore more BeamLocations that are of the same subclass, page rendering is broken. Navigating to links works, but the content on screen doesn't change.

Beamer version: 1.6.0

To Reproduce

This creates two instances of SimpleLocation, which is just a BeamLocation where you pass in a path pattern, a key, and a child. The two SimpleLocations ought to allow routing to /a and /b, but it doesn't work. If you try to click the button to go to "/b", the URL changes if you're on Web, but the content of the page doesn't change.

If you copy-paste the SimpleLocation class to a SimpleLocation2 class, and change the "BScreen" route to use SimpleLocation2, the app will start working as expected. Somehow having two BeamLocations that are instances of the same class breaks page rendering.

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

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

class SimpleLocation extends BeamLocation<BeamState> {
  final List<String> _pathPatterns;
  final ValueKey _key;
  final Widget child;

  SimpleLocation(String path, String key, this.child)
      : _pathPatterns = [path],
        _key = ValueKey(key);

  @override
  List<String> get pathPatterns => _pathPatterns;

  @override
  List<BeamPage> buildPages(BuildContext context, BeamState state) {
    return [
      BeamPage(
        key: _key,
        child: child,
      ),
    ];
  }
}

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

  final routerDelegate = BeamerDelegate(
    initialPath: '/a',
    // locationBuilder: RoutesLocationBuilder(
    //   routes: {
    //     // Return either Widgets or BeamPages if more customization is needed
    //     '/a': (context, state, data) => AScreen(),
    //     '/b': (context, state, data) => BScreen(),
    //   },
    // ),
    locationBuilder: BeamerLocationBuilder(
      beamLocations: [
        SimpleLocation('/a', 'a', AScreen()),
        SimpleLocation('/b', 'b', BScreen()),
      ],
    ).call,
  );
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      routeInformationParser: BeamerParser(),
      routerDelegate: routerDelegate,
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('A Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Beamer.of(context).beamToNamed('/b');
          },
          child: const Text('Go to B'),
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('B Screen'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Beamer.of(context).beamToNamed('/a');
          },
          child: const Text('Go to A'),
        ),
      ),
    );
  }
}

Expected behavior

BeamLocations should work.

stan-at-work commented 4 months ago

Describe the bug

If you have two ore more BeamLocations that are of the same subclass, page rendering is broken. Navigating to links works, but the content on screen doesn't change.

Beamer version: 1.6.0

To Reproduce

This creates two instances of SimpleLocation, which is just a BeamLocation where you pass in a path pattern, a key, and a child. The two SimpleLocations ought to allow routing to /a and /b, but it doesn't work. If you try to click the button to go to "/b", the URL changes if you're on Web, but the content of the page doesn't change.

If you copy-paste the SimpleLocation class to a SimpleLocation2 class, and change the "BScreen" route to use SimpleLocation2, the app will start working as expected. Somehow having two BeamLocations that are instances of the same class breaks page rendering.


import 'package:beamer/beamer.dart';

import 'package:flutter/material.dart';

void main() {

  runApp(MyApp());

}

class SimpleLocation extends BeamLocation<BeamState> {

  final List<String> _pathPatterns;

  final ValueKey _key;

  final Widget child;

  SimpleLocation(String path, String key, this.child)

      : _pathPatterns = [path],

        _key = ValueKey(key);

  @override

  List<String> get pathPatterns => _pathPatterns;

  @override

  List<BeamPage> buildPages(BuildContext context, BeamState state) {

    return [

      BeamPage(

        key: _key,

        child: child,

      ),

    ];

  }

}

class MyApp extends StatelessWidget {

  MyApp({super.key});

  final routerDelegate = BeamerDelegate(

    initialPath: '/a',

    // locationBuilder: RoutesLocationBuilder(

    //   routes: {

    //     // Return either Widgets or BeamPages if more customization is needed

    //     '/a': (context, state, data) => AScreen(),

    //     '/b': (context, state, data) => BScreen(),

    //   },

    // ),

    locationBuilder: BeamerLocationBuilder(

      beamLocations: [

        SimpleLocation('/a', 'a', AScreen()),

        SimpleLocation('/b', 'b', BScreen()),

      ],

    ).call,

  );

  // This widget is the root of your application.

  @override

  Widget build(BuildContext context) {

    return MaterialApp.router(

      title: 'Flutter Demo',

      theme: ThemeData(

        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),

        useMaterial3: true,

      ),

      routeInformationParser: BeamerParser(),

      routerDelegate: routerDelegate,

    );

  }

}

class AScreen extends StatelessWidget {

  AScreen({super.key});

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: const Text('A Screen'),

      ),

      body: Center(

        child: ElevatedButton(

          onPressed: () {

            Beamer.of(context).beamToNamed('/b');

          },

          child: const Text('Go to B'),

        ),

      ),

    );

  }

}

class BScreen extends StatelessWidget {

  BScreen({super.key});

  @override

  Widget build(BuildContext context) {

    return Scaffold(

      appBar: AppBar(

        title: const Text('B Screen'),

      ),

      body: Center(

        child: ElevatedButton(

          onPressed: () {

            Beamer.of(context).beamToNamed('/a');

          },

          child: const Text('Go to A'),

        ),

      ),

    );

  }

}

Expected behavior

BeamLocations should work.

Yea, this code example should work, its weard that is doesn't.

I will look in to it 👀