lulupointu / vrouter

MIT License
202 stars 39 forks source link

How to pass data back and forth #111

Closed CripyIce closed 3 years ago

CripyIce commented 3 years ago

Hi there, After migrating my whole project to use vrouter I came across a case I'm not sure how to solve properly, would appreciate the help. I have a posts list with filter option which navigates the user to a new screen, then he chooses all the filter he wants and then press "Apply" button and it pop back to the posts list screen with the data he chose to filter and the posts list refreshes according to that data. Code explanation:

  1. User press "filter" button:

    final result = await Navigator.push(
        context,
        MaterialPageRoute(
            builder: (_) => PostsFilter(
                data: {
                    "authors": _authors,
                    "date": _date,
                  },
                )));
    
    print("result is: $result");
    
    // Some filter logic

    Note: I also pass data when pushing to show the filter with the applied filters from before (if exists).

  2. User selects the filters he wants and press "Apply":

    Navigator.pop(context, {
    "authors": _authorsSelectedList
        .where((element) => element.selected)
        .map((e) => e.data)
        .toList(),
    "date": _currentDate,
    });

    3.Some filter logic is being called since user popped back with data.

This process can repeat it self and pass data back and forth.

I'm not sure if queryParameters could help me since the filter will have many parameters in the future and I don't want the url to be very long and weird.

I hope I explained my case clearly. Thanks!

lulupointu commented 3 years ago

Hi,

actually if you want any sort of deep-linking I would go with queryParameters, optional filtering is exactly why they are here for.

The other option is to store all this data using any state management method you want. Of course the issue here is that if you share the url you won't share your selected options.

Depends on what you want :wink:

CripyIce commented 3 years ago

Thanks for the fast response, The app is personally for each user, so I don't see the reason to be able to have the option to share the url - so I guess I can pass queryParameters for now. You suggest using Provider for example? Any way of keep using Navigator with vrouter?

lulupointu commented 3 years ago

Actually if it's just for a filter screen you don't have to change the url so you can use Navigator.push and Navigator.pop with vrouter (just be sure to have a recent version of vrouter because compatibility with Navigator was added recently, I believe you must be >=v1.1.3).

I got a bit carried away and crafted an example for you ```dart import 'package:flutter/material.dart'; import 'package:vrouter/vrouter.dart'; main() { runApp(MyApp()); } class Book { final String title; final String description; Book( this.title, this.description, ); } class MyApp extends StatelessWidget { final List books = [ Book('Stranger in a Strange Land', 'It tells the story of Valentine Michael Smith, a human who comes to Earth in early adulthood after being born on the planet Mars and raised by Martians, and explores his interaction with and eventual transformation of Terran culture. '), Book('Foundation', 'The premise of the stories is that, in the waning days of a future Galactic Empire, the mathematician Hari Seldon spends his life developing a theory of psychohistory, a new and effective mathematical sociology'), Book('Fahrenheit 451', 'The novel has been the subject of interpretations focusing on the historical role of book burning in suppressing dissenting ideas for change'), ]; MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return VRouter( routes: [ VWidget(path: '/', widget: HomeScreen()), ], ); } } class HomeScreen extends StatefulWidget { final List books = [ Book('Stranger in a Strange Land', 'It tells the story of Valentine Michael Smith, a human who comes to Earth in early adulthood after being born on the planet Mars and raised by Martians, and explores his interaction with and eventual transformation of Terran culture. '), Book('Foundation', 'The premise of the stories is that, in the waning days of a future Galactic Empire, the mathematician Hari Seldon spends his life developing a theory of psychohistory, a new and effective mathematical sociology'), Book('Fahrenheit 451', 'The novel has been the subject of interpretations focusing on the historical role of book burning in suppressing dissenting ideas for change'), ]; HomeScreen({Key? key}) : super(key: key); @override _HomeScreenState createState() => _HomeScreenState(); } class _HomeScreenState extends State { late var selectedBooks = List.from(widget.books); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Your filters books')), body: Padding( padding: const EdgeInsets.all(8.0), child: Stack( children: [ Positioned.fill( child: ListView.separated( separatorBuilder: (_, __) => Column( children: [ SizedBox(height: 20), Container(width: double.infinity, height: 1, color: Colors.grey[300],), SizedBox(height: 20), ], ), itemCount: selectedBooks.length, itemBuilder: (context, index) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( selectedBooks[index].title, style: Theme.of(context).textTheme.headline6, ), Text( selectedBooks[index].description, style: Theme.of(context).textTheme.subtitle1, ), ], ); }, ), ), Align( alignment: Alignment.bottomCenter, child: ElevatedButton( child: Text('Filter your books'), onPressed: () { Navigator.push>( context, MaterialPageRoute( builder: (context) => FilterScreen( books: widget.books, initialSelectedBooks: selectedBooks, ), ), ).then( (newSelectedBooks) => setState(() => selectedBooks = newSelectedBooks!), ); }, ), ), ], ), ), ); } } class FilterScreen extends StatefulWidget { final List books; final List initialSelectedBooks; const FilterScreen({ Key? key, required this.books, required this.initialSelectedBooks, }) : super(key: key); @override _FilterScreenState createState() => _FilterScreenState(); } class _FilterScreenState extends State { late var selectedBooks = List.from(widget.initialSelectedBooks); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Choose which books to show'), leading: BackButton( onPressed: () { Navigator.pop(context, selectedBooks); }, ), ), body: Padding( padding: const EdgeInsets.all(8.0), child: ListView( children: [ for (var book in widget.books) Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ListTile( leading: Icon( selectedBooks.contains(book) ? Icons.radio_button_checked : Icons.radio_button_off, ), title: Opacity( opacity: selectedBooks.contains(book) ? 1 : 0.5, child: Text( book.title, style: Theme.of(context).textTheme.headline6, ), ), onTap: () { setState(() { selectedBooks.contains(book) ? selectedBooks.remove(book) : selectedBooks.add(book); }); }, ), ], ), ], ), ), ); } } ```

Please mind that this is a very simple scenario where you just have 2 screens. It can work for certain case though, especially if you are unsure of which state management method to use I think this will be enough. This can easily be expanded if needed.

lulupointu commented 3 years ago

I'm closing this since the example pretty much examples "How to pass data back and forth". Feel free to reopen if it's unclear 😊

ebelevics commented 3 years ago

Isn't it possible to do with RouteSettings class? For example:

context.vRouter.to(
        RegisterCompleteScreen.routeName,
        arguments: MyDataObject(),
      );
VWidget(
  path: LoginScreen.routeName,
  widget: (context, arguments) => LoginScreen(arguments.data as MyDataObject),
),