user1209216
user1209216

Reputation: 7954

Create bloc instance for named route + pass arguments

My app uses FlutterBloc for state management. My use case:

My code to show details page on list item tap:

return GestureDetector(
      onTap: () {
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (_) => BlocProvider(
              create: (_) => DetailsBloc(
                someRepository: context.read<SomeRepository>(),
                otherRepository context.read<OtherRepository>(),
              )..add(GetItemDetailsEvent(id: item.id.toString())),
              child: const EventDetailsPage(),
            ),
          ),
        );
      },
      child: Card(....)

It works fine. On each list item tap, it shows new page and provides new DetailsBloc instance to its context. After page is closed, bloc will be destroyed as well (this is how I want it to work). DetailsBloc includes event to load details data by item id, so details window is able to refresh itself to show details.

But I prefer to use named routes and use pushNamed instead. I am able to provide new DetailsBloc instance this way when route is created and optionally, send event to it:

'/item_details': (context) => BlocProvider(
            create: (context) => DataBloc(
                  someRepository: SomeRepository(),
                  otherRepository: OherRepository(),
                )..add(GetItemDetailsEvent(id: <id>),
                ,
               child: const DetailsPage()),

This way, I can show new page by Navigator.of(context).pushNamed('/item_details'); But in this case, I see no way to pass id to event to load data, because it's not known when route initialises itself. So I can't add event in route definition. I could try to add event later, on list item tap:

onTap: () {
 context.read<DetailsBloc>().add((GetItemDetailsEvent(id: item.id.toString());

This obviously won't work, because DetailsBloc is not provided here and it even does not exist anywhere before route is created. I can also add event to DetailsBloc inside my DetailsPage but it must be done in inside init state, so:

Do you have any ideas how to use named route here?

Upvotes: 1

Views: 416

Answers (1)

Vladyslav Ulianytskyi
Vladyslav Ulianytskyi

Reputation: 2541

You could pass the id in router arguments:

await Navigator.pushNamed(
                context,
                DetailsPage.routeName,
                arguments: id,
              );

then in the route_builder:

...
case DetailsPage.routeName:
        final arguments = settings.arguments as String;
        return MaterialPageRoute(
          builder: (context) => DetailsPage(itemId: arguments),
          settings: settings,
        );        
...

and finaly in the DetailsPage:

class DetailsPage extends StatelessWidget {
  const DetailsPage({
    required this.itemId,
    super.key,
  });

  static const routeName = '/details_page';
  final int storyId;

  @override
  Widget build(BuildContext context) => BlocProvider<DetailsBloc>(
        create: (context) => DetailsBloc(
            someRepository: SomeRepository(),//or get it from DI or ServiceLocator
            otherRepository: OtherRepository(),//or get it from DI or ServiceLocator
          )..add(GetItemDetailsEvent(id: item.id.toString())),
        child: const DetailsPageView(),
      );
}

Upvotes: 1

Related Questions