Git Product home page Git Product logo

sailor's Introduction

sailor

anchor_image

License: MIT pub_package

A Flutter package for easy navigation management.

Warning: Package is still under development, there might be breaking changes in future.

Index

Setup and Usage

  1. Create an instance of Sailor and add routes.
// Routes class is created by you.
class Routes {
  static final sailor = Sailor();

  static void createRoutes() {
    sailor.addRoute(SailorRoute(
        name: "/secondPage",
        builder: (context, args, params) {
          return SecondPage();
        },
      ));
  }
}
  1. Register the routes in onGenerateRoute using the generate function of Sailor and also Sailor's navigatorKey.
class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sailor Example',
      home: Home(),
      navigatorKey: Routes.sailor.navigatorKey,  // important
      onGenerateRoute: Routes.sailor.generator(),  // important
    );
  }
}
  1. Make sure to create routes before starting the application.
void main() async {
  Routes.createRoutes();
  runApp(App());
}
  1. Use the instance of Sailor to navigate.
Routes.sailor.navigate("/secondPage");
  • TIP: Sailor is a callable class, so you can omit navigate and directly call the method.
Routes.sailor("/secondPage");

Passing Parameters

Sailor allows you to pass parameters to the page that you are navigating to.

  • Before passing the parameter itself, you need to declare it while declaring your route. Let's declare a parameter named id that has a default value of 1234.
sailor.addRoutes([
  SailorRoute(
    name: "/secondPage",
    builder: (context, args, params) => SecondPage(),
    params: [
      SailorParam<int>(
        name: 'id',
        defaultValue: 1234,
      ),
    ],
  ),
);
  • Pass the actual parameter when navigating to the new route.
Routes.sailor.navigate<bool>("/secondPage", params: {
  'id': 4321,
});
  • Parameters can be retrieved from two places, first, the route builder and second, the opened page itself.

Route Builder:

sailor.addRoutes([
  SailorRoute(
    name: "/secondPage",
    builder: (context, args, params) {
      // Getting a param
      final id = params.param<int>('id');
      return SecondPage();
    },
    params: [
      SailorParam(
        name: 'id',
        defaultValue: 1234,
      ),
    ],
  ),
);

Opened page:

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final id = Sailor.param<int>(context, 'id');

    ...

  }
}

Make sure to specify the type of paramter when declaring SailorParam<T>. This type is used to make sure when the route is being opened, it is passed the correct param type. Right now Sailor logs a warning if the type of declared and passed param is not same. In future version this might throw an error.

Passing Arguments

Sailor allows you to pass arguments to the page that you are navigating to.

  • Create a class that extends from BaseArguments.
class SecondPageArgs extends BaseArguments {
  final String text;

  SecondPageArgs(this.text);
}
  • When calling the navigate method pass these arguments.
final response = Routes.sailor.navigate(
  "/secondPage",
  args: SecondPageArgs('Hey there'),
);
  • When in the SecondPage, use Sailor.args to get the passed arguments.
class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final args = Sailor.args<SecondPageArgs>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Compass Example'),
      ),
      body: Center(
        child: Text(args.text),
      ),
    );
  }
}

Route Guards (Experimental)

Routes can be protected from being opened when navigate is called using route guard.

A route guard can be added when declaring a SailorRoute.

sailor.addRoutes([
  SailorRoute(
    name: "/secondPage",
    builder: (context, args, params) => SecondPage(),
    routeGuards: [
      SailorRouteGuard.simple((context, args, params) async {
        // Can open logic goes here.
        if (sharedPreferences.getToken() != null) {
          return true;
        }
        return false;
      }),
    ],
  ),
);

routeGuards takes an array of SailorRouteGuard.

There are two ways to create a route guard:

  • Using SailorRouteGuard.simple, as shown above.
SailorRouteGuard.simple((context, args, params) async {
  // Can open logic goes here.
  if (sharedPreferences.getToken() != null) {
    return true;
  }
  return false;
});
  • Extending SailorRouteGuard class.
class CustomRouteGuard extends SailorRouteGuard {
  @override
  Future<bool> canOpen(
    BuildContext context,
    BaseArguments args,
    ParamMap paramMap,
  ) async {
    return false;
  }
}

The result from each route guard is Future<bool>. If the value returned by each route is true the route is accepted and opened, anything else will result in route being rejected and not being opened.

Transitions

Sailor has inbuilt support for page transitions. A transition is specified using SailorTransition.

Transition can be specified at 3 levels (ordered in priority from highest to lowest):

  • When Navigating (using Sailor.navigate).
  • While adding routes (SailorRoute).
  • Global transitions (SailorOptions).

When navigating

Specify which transitions to use when calling the navigate method.

Routes.sailor.navigate(
  "/secondPage",
  transitions: [SailorTransition.fade_in],
);

More than one transition can be provided when navigating a single route. These transitions are composed on top of each other, so in some cases changing the order will change the animation.

Routes.sailor.navigate(
  "/secondPage",
  transitions: [
    SailorTransition.fade_in,
    SailorTransition.slide_from_right,
  ],
  transitionDuration: Duration(milliseconds: 500),
  transitionCurve: Curves.bounceOut,
);

Duration and Curve can be provided using transitionDuration and transitionCurve respectively.

Routes.sailor.navigate(
  "/secondPage",
  transitions: [
    SailorTransition.fade_in,
    SailorTransition.slide_from_right,
  ],
  transitionDuration: Duration(milliseconds: 500),
  transitionCurve: Curves.bounceOut,
);

In the above example the page will slide in from right with a fade in animation. You can specify as many transitions as you want.

When adding routes

You can specify the default transition for a route, so you don't have to specify it again and again when navigating.

sailor.addRoute(SailorRoute(
  name: "/secondPage",
  defaultTransitions: [
    SailorTransition.slide_from_bottom,
    SailorTransition.zoom_in,
  ],
  defaultTransitionCurve: Curves.decelerate,
  defaultTransitionDuration: Duration(milliseconds: 500),
  builder: (context, args) => SecondPage(),
));

Priority: Transitions provided in Sailor.navigate while navigating to this route, will override these transitions.

Global transitions

You can specify default transition to be used for all routes in Sailor.

SailorOptions(
  defaultTransitions: [
    SailorTransition.slide_from_bottom,
    SailorTransition.zoom_in,
  ],
  defaultTransitionCurve: Curves.decelerate,
  defaultTransitionDuration: Duration(milliseconds: 500),
)

Priority: Transitions provided while adding a route or when navigating using navigate, will override these transitions.

Custom Transitions

Although sailor provides you with a number of out of the box transitions, you can still provide your own custom transitions.

  • To create a custom transition, extend the class CustomSailorTransition and implement buildTransition method.
class MyCustomTransition extends CustomSailorTransition {
  @override
  Widget buildTransition(
    BuildContext context,
    Animation<double> animation,
    Animation<double> secondaryAnimation,
    Widget child,
  ) {
    return FadeTransition(
      opacity: animation,
      child: child,
    );
  }
}

This transition can now be provided at 3 places:

  • While calling navigate.
Routes.sailor.navigate<bool>(
  "/secondPage",
  customTransition: MyCustomTransition(),
);
  • When declaring a SailorRoute.
SailorRoute(
  name: "/secondPage",
  builder: (context, args, params) => SecondPage(),
  customTransition: MyCustomTransition(),
),
  • In SailorOptions:
static final sailor = Sailor(
  options: SailorOptions(
    customTransition: MyCustomTransition(),
  ),
);

Custom Transition Priority

NOTE: Custom transitions have the highest priority, if you provide a custom transition, they will be used over Sailor's inbuilt transitions.

The same priority rules apply to custom transitions as inbuilt sailor transitions, with the added rule that at any step if both transitions are provided (i.e. Sailor's inbuilt transitions and a CustomSailorTransition), the custom transition will be used over inbuilt one.

For example, in the below code, MyCustomTransition will be used instead of SailorTransition.slide_from_top.

Routes.sailor.navigate<bool>(
  "/secondPage",
  transitions: [
    SailorTransition.slide_from_top,
  ],
  customTransition: MyCustomTransition(),
);

Pushing Multiple Routes

Sailor allows you to push multiple pages at the same time and get collected response from all.

final responses = await Routes.sailor.navigateMultiple(context, [
  RouteArgsPair("/secondPage", SecondPageArgs("Multi Page!")),
  RouteArgsPair("/thirdPage", ThirdPageArgs(10)),
]);

print("Second Page Response ${responses[0]}");
print("Third Page Response ${responses[1]}");

Log Navigation

Use SailorLoggingObserver to log the push/pop navigation inside the application. Add the SailorLoggingObserver to the navigatorObservers list inside your MaterialApp.

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Compass Example',
      home: Home(),
      onGenerateRoute: Routes.sailor.generator(),
      navigatorObservers: [
        SailorLoggingObserver(),
      ],
    );
  }
}

Once added, start navigating in your app and check the logs. You will see something like this.

flutter: [Sailor] Route Pushed: (Pushed Route='/', Previous Route='null', New Route Args=null, Previous Route Args=null)
flutter: [Sailor] Route Pushed: (Pushed Route='/secondPage', Previous Route='/', New Route Args=Instance of 'SecondPageArgs', Previous Route Args=null)
flutter: [Sailor] Route Popped: (New Route='/', Popped Route='/secondPage', New Route Args=null, Previous Route Args=Instance of 'SecondPageArgs')

Support

If you face any issue or want a new feature to be added to the package, please create an issue. I will be more than happy to resolve your queries.

sailor's People

Contributors

gurleensethi avatar pawnyy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

sailor's Issues

Wrong context in SailorRoute builder to get BlocProvider

I'm trying to use Sailor with flutter_bloc. When adding a SailorRoute like this:

    SailorRoute(
        name: '/details',
        builder: (context, args, params) {
          print('context... $context');
          return BlocProvider.value(
            value: BlocProvider.of<WeatherBloc>(context),
            child: WeatherDetailPage(
              masterWeather: params.param('masterWeather'),
            ),
          );
        },
        params: [
          SailorParam<Weather>(
            name: 'masterWeather',
          ),
        ],
      ),

Seeing following exception:

════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following assertion was thrown building Builder(dirty):
        BlocProvider.of() called with a context that does not contain a Bloc of type WeatherBloc.

        No ancestor could be found starting from the context that was passed to BlocProvider.of<WeatherBloc>().

        This can happen if:
        1. The context you used comes from a widget above the BlocProvider.
        2. You used MultiBlocProvider and didn't explicity provide the BlocProvider types.

        Good: BlocProvider<WeatherBloc>(builder: (context) => WeatherBloc())
        Bad: BlocProvider(builder: (context) => WeatherBloc()).

        The context used was: Builder(dirty)
        
The relevant error-causing widget was: 
  MaterialApp file:///home/mc/development/projects/flutter_tmp/flutter-bloc-library-v1-tutorial/lib/main.dart:19:12
When the exception was thrown, this was the stack: 
#0      BlocProvider.of (package:flutter_bloc/src/bloc_provider.dart:80:7)
#1      Routes.createRoutes.<anonymous closure> (package:flutter_bloc_v1_tutorial/main.dart:49:33)
#2      Sailor.generator.<anonymous closure>.<anonymous closure> (package:sailor/src/sailor.dart:521:37)
#3      new BaseTransitionPageRoute.<anonymous closure> (package:sailor/src/transitions/base_transition_page_route.dart:23:60)
#4      PageRouteBuilder.buildPage (package:flutter/src/widgets/pages.dart:109:12)
...
════════════════════════════════════════════════════════════════════════════════════════════════════

For details see https://github.com/zash80/flutter-bloc-library-v1-tutorial forked from https://github.com/ResoCoder/flutter-bloc-library-v1-tutorial replacing flutter navigation with Sailor navigation.

Get transition used for current route

Sometimes routes are pushed using different transitions, and for this, we might want to display a slightly different UI based on the transition used.

Ex. for certain route we might want to use a Close (X) icon to pop the route instead of the Arrow icon.

Is there any way this could be implemented?

Is there any way to access arguments from URL in Flutter Web?

I am currently building an application which contains a list of items.

I noticed that Sailor does add the route name to the URL as you would expect from a normal web app.

I also do know we can programmatically pass arguments and params to the pages when navigating to the route.

My question is, is there any way to get the arguments passed in the URL with Sailor?

That is, if we have http://localhost:62656/#/book?id=1

Can I get the id from the URL as an argument for the route?

Replacing a screen

Hi there.
I started using this package like a week ago, as I got it as a suggestion. And all I can say is that I love it. I enjoy using it given the simplicity of the package.

However when making my current app, I've gotten into a scenario where I need to replace my current screen (moving from a login screen to another screen) and disable returning back unless the user chooses to (i.e.: log out).

Will there be any possibility of making a replace method in the future? Are there any hacky ways of doing this now?

Edit: I am stupid. Should have researched more before posting here. Haven't seen 'navigationType' parameter in the navigate method.

Mocking for Sailor.args

I started using Sailor to implement routing, but I encountered a problem in tests. If Sailor can still be instantiated in Dependenct Injection and using Mockito for check the result of widgets including navigation, but methods for getting arguments are impossible mock. In order not to violate the area of responsibility, I can not move the receipt of arguments.

The simplest solution: in the instance Sailor add the same method args that will reference to the static method.

Sailor push not work as I expect with CupertinoTabScaffold innter TabViews.

How I push with sailor and without sailor.

Routes.sailor.navigate(
                          "Inner1",
                          navigationType: NavigationType.push,
            );

Navigator.of(context).push(
                        MaterialPageRoute(
                        builder: (context) => Inner1(),
                     ),
           );

My Flutter layout inspector.

Observe (with sailor)
3 bottom tabs
[Inner Tab Screen stack]
(https://github.com/kishansinhparmarxongo/resources_link/blob/master/Inner%20screen.png)

Expect (Without sailor)
3 bottom tabs
Inner Tab Screen

You can follow this link UX expect

Redirect Features for routeGuards

It would be very helpful to provide a possibility to routeGuards for an error page in case a routeGuard evaluates to false.
This could e.g. be an login page or an page to show that the user has insufficient rights.

Feature: Nested Pages

For a scenario where you have a master layout page that then contained child widgets, have a way to nest these to have deeper routes. For example:

Routes:

  • #/admin/dashboard
  • #/admin/users
sailor.addRoutes([
  SailorRoute(
    name: "/admin",
    builder: (context, args, params) => AdminLayout(),
    subroutes: [
      SailorRoute(
        name: "/dashboard",
        builder: (context, args, params) => DashboardPage(),
      ),
      SailorRoute(
        name: "/users",
        builder: (context, args, params) => UsersPage(),
      ),   
    ]   
);

This would build the widgets such as:

AdminLayout(child: DashboardPage());

Production ready?

Hi,

Can I use this package to ship a product? Awesome package btw.

How can I use param data in initState in new page?

Hi,

I need to send from main.dart the "_startDate" and "_endDate" to new page. In new page in initState I need to use these values before building a new page. If I use param the data was empty.

What is the best way to use the values in new page initState? Any example much appreciated.

Package broken on Master

I updated my Flutter SDK to

Flutter 1.15.4-pre.274 • channel master • https://github.com/flutter/flutter.git
Framework • revision cbbb7ec5a7 (14 hours ago) • 2020-03-04 12:40:15 -0800
Engine • revision 90bd6f8148
Tools • Dart 2.8.0 (build 2.8.0-dev.11.0 5701c4fd3b)

Then i got this when trying to run my project

Compiler message:
[   +2 ms] ../../../Development/flutter/.pub-cache/hosted/pub.dartlang.org/sailor-0.6.0/lib/src/sailor.dart:509:9: Error: No named parameter with the name
'isInitialRoute'.
[        ]         isInitialRoute: settings.isInitialRoute,
[        ]         ^^^^^^^^^^^^^^
[        ] ../../../Development/flutter/packages/flutter/lib/src/widgets/navigator.dart:419:9: Context: Found this candidate, but the arguments don't match.
[        ]   const RouteSettings({
[        ]         ^^^^^^^^^^^^^
[+2956 ms] 
                    Compiler message:
[        ] ../../../Development/flutter/.pub-cache/hosted/pub.dartlang.org/sailor-0.6.0/lib/src/sailor.dart:509:9: Error: No named parameter with the name
'isInitialRoute'.
[        ]         isInitialRoute: settings.isInitialRoute,
[        ]         ^^^^^^^^^^^^^^
[        ] ../../../Development/flutter/packages/flutter/lib/src/widgets/navigator.dart:419:9: Context: Found this candidate, but the arguments don't match.
[        ]   const RouteSettings({
[        ]         ^^^^^^^^^^^^^
[+4452 ms] Persisting file store
[   +9 ms] Done persisting file store
[   +1 ms] Target kernel_snapshot failed: Exception: Errors during snapshot creation: null
[        ] Failed to build bundle.
[        ] Error launching application on iPhone 11 Pro Max.
[  +14 ms] "flutter run" took 14,128ms.

#0      throwToolExit (package:flutter_tools/src/base/common.dart:14:3)
#1      RunCommand.runCommand (package:flutter_tools/src/commands/run.dart:568:7)
<asynchronous suspension>
#2      FlutterCommand.verifyThenRunCommand (package:flutter_tools/src/runner/flutter_command.dart:711:18)
#3      _rootRunUnary (dart:async/zone.dart:1134:38)
#4      _CustomZone.runUnary (dart:async/zone.dart:1031:19)
#5      _FutureListener.handleValue (dart:async/future_impl.dart:140:18)
#6      Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:682:45)
#7      Future._propagateToListeners (dart:async/future_impl.dart:711:32)
#8      Future._completeWithValue (dart:async/future_impl.dart:526:5)
#9      _AsyncAwaitCompleter.complete (dart:async-patch/async_patch.dart:34:15)
#10     _completeOnAsyncReturn (dart:async-patch/async_patch.dart:293:13)
#11     RunCommand.usageValues (package:flutter_tools/src/commands/run.dart)
#12     _rootRunUnary (dart:async/zone.dart:1134:38)
#13     _CustomZone.runUnary (dart:async/zone.dart:1031:19)
#14     _FutureListener.handleValue (dart:async/future_impl.dart:140:18)
#15     Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:682:45)
#16     Future._propagateToListeners (dart:async/future_impl.dart:711:32)
#17     Future._completeWithValue (dart:async/future_impl.dart:526:5)
#18     Future._asyncComplete.<anonymous closure> (dart:async/future_impl.dart:556:7)
#19     _rootRun (dart:async/zone.dart:1126:13)
#20     _CustomZone.run (dart:async/zone.dart:1023:19)
#21     _CustomZone.runGuarded (dart:async/zone.dart:925:7)
#22     _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:965:23)
#23     _microtaskLoop (dart:async/schedule_microtask.dart:43:21)
#24     _startMicrotaskLoop (dart:async/schedule_microtask.dart:52:5)
#25     _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:118:13)
#26     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:169:5)

How to use guards with flutter_bloc states

I'm trying to implement the following if a user is logged in can't navigate to specific pages like login, register ...etc and if login the same can't navigate to profile ...etc

I'm using a flutter bloc Package and I have done the Authentication bloc and works great now my issue that I have ( I guess ) to access the Authentication bloc to check if the user authenticated or not and return a boolean depends on that!

So be something like that

BlocListener<AuthenticationBloc, AuthenticationState>(
  ...
);

But the problem I can't do that as far I know in SailorRouteGaurds!! or I don't know how to implement this

class Routes {
  static final Sailor sailor = Sailor();

  static void createRoutes() {
    sailor.addRoutes([
      SailorRoute(
        name: '/login', 
        builder: (context, args, params) {
          return Directionality(
            textDirection: TextDirection.rtl,
            child: LoginPage(),
          );
        },
        routeGuards: [
          SailorRouteGuard.simple(
            (context, args, params) async {
              return true;
            }
          )
        ]
      )
    ]);
  }
}

Error in 0.7.0

Compiler message:
../../dev-tools/flutter/.pub-cache/hosted/pub.dartlang.org/sailor-0.7.0/lib/src/sailor.dart:429:43: Error: Can't return a value from a void function.
return this.navigatorKey.currentState.pop(result);
^

Build fails.

Custom navigatorObserver

Hi.
This is a great peace of code, thanks for that.

And my question is:
we can use SailorLoggingObserver() for logging purposes, obviously. But I need to use a navigatorObserver to monitor user's movements from screen to screen in order to check if user is currently on chat's screen. So I can mark chat as 'unread' otherwise.

I managed to get this info this way:
var screenargs = route.settings.arguments as ArgumentsWrapper;

But in order to do that I need to import:
import 'package:sailor/src/models/arguments_wrapper.dart';

And dart analyzer complains:

'Don't import implementation files from another package.

It would be nice to have an observer not only for logging but actually for getting routes' params.

New Feature

Hello, thanks for this great package. I've been constantly using it for my three flutter projects and its very useful and wonderful but I like to request some new feature that i think will help this package become more powerful.

Requesting an alternative way to get the instance of the ARGUMENT from this

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Suggestion For Uniformity
    // Uniformity = Readability = Understandability = Familiarity = Maintainability

    // Preferred
    // final argument = Sailor.of<HomeArgument>(context);
    final argument = Sailor.args<HomeArgument>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Sailor Example'),
      ),
      body: Center(
        child: Text(argument.text),
      ),
    );
  }
}

to provider consumer pattern like this

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SailorConsumer<HomeArgument>(
      builder: (BuildContext context, HomeArgument argument) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Sailor Example'),
          ),
          body: Center(
            child: Text(argument.text),
          ),
        );
      }
    );
  }
}

Because everybody likes widget and everything is a widget :)

Add support for modal navigation transitions

Coming from Fluro, I am missing modal transitions. I tried using a custom generator function as workaround but ArgumentsWrapper is not public so I can't access the pages params.

It would be nice if the the modal transitions would use platform specific page routes.

Get current route (entire route object or just name)

Hi,
First let me say that this is a great package.

I am trying to get the current route in order to perform a specific action.
Is there a way to get the current route - something like Routes.sailor.currentRouteName or Routes.sailor.currentRoute.

I came up with 2 possible options, if there is another way please let me know:

  1. Maybe use the 'SailorLoggingObserver` (like its suggested in issue #32 ) but I am not sure this is the correct approach?
  2. The option I am using now is ModalRoute.of(context).isCurrent but I am guessing navigation package like Sailor should have something build in, no?

Thanks

Error: This expression has type 'void' and can't be used.

   Compiler message:
    ../../../flutter/.pub-cache/hosted/pub.dartlang.org/sailor-0.6.0/lib/src/sailor.dart:429:43: Error: This expression has type 'void' and can't be used.
        return this.navigatorKey.currentState.pop(result);
                                              ^

Flutter doctor

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel master, v1.14.7-pre.52, on Mac OS X 10.14.6 18G2022, locale en-AU)
 
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
[✓] Xcode - develop for iOS and macOS (Xcode 11.2.1)
[✓] Android Studio (version 3.5)
[✓] VS Code (version 1.41.1)
[✓] Connected device (2 available)

• No issues found!

Convenient way for multiple navigation stacks

Is there any convenient way to have multiple navigation stacks?
This would really come in hanndy for applications using a bottom navigation bar or something similar.
The expected behaviour would be similar to the YouTube app.

Allow setting custom navigation key

It would be great if we could set a custom GlobalKey<NavigatorState> navigatorKey which defaults to the existing one if not provided. This would make it easier to integrate Sailor with other libraries that also rely on the navigator key.

Package Optimization for UNIQUENESS and BRANDING

Hello, thanks for this great package. I've been constantly using it for my three flutter projects and its very useful and wonderful but I like to request some new feature that I think will help this package become more powerful.

The problem is the keyword Arguments or BaseArguments. It can be used by literally any package

class HomeArgument extends BaseArguments {
  final String text;

  HomeArgument(this.text);
}

Suggesting it to be UNIQUE and BRANDABLE for this package
Infact ANCHOR is used when the SAILOR is finished NAVIGATING

class HomeAnchor extends SailorAnchor {
  final String text;

  HomeAnchor(this.text);
}

Requesting an alternative way to get the instance of the ARGUMENT/ANCHOR from this

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Suggestion For Uniformity
    // Uniformity = Readability = Understandability = Familiarity = Maintainability

    // Preferred
    // final anchor = Sailor.of<HomeAnchor>(context);
    final anchor = Sailor.args<HomeAnchor>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Sailor Example'),
      ),
      body: Center(
        child: Text(anchor.text),
      ),
    );
  }
}

to provider consumer pattern like this

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SailorConsumer<HomeAnchor>(
      builder: (BuildContext context, HomeAnchor anchor) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Sailor Example'),
          ),
          body: Center(
            child: Text(anchor.text),
          ),
        );
      }
    );
  }
}

Because everybody likes widget and everything is a widget :)

How to return login page after logout

Hi,

I have login page and after user successfully logged in it redirects to home page. User Interaction is 10 min. Meaning if user not interact with app 10 min. it redirects to login page.

But after redirects the login page the login page textfield and login button is not working. Any idea?

PS: Also I want to remove all other pages after login. I want to remove every page from stack from home page and home page onward. As shown below when app try to redirects from logout anything in stack from Home Page to Payment Page must be remove.

- Main Screen
- Login Page
- Home Page
- Account Page
     - Account Summary
     - Account Edit
     - Account View
- Card Page
     - Card Summary
     - Card Edit
     - Card View
- Payment Page
     - Payment Summary
     - Make Payment
- Logout Page

My Routes Class:

class Routes {
  static final sailor = Sailor(
    options: SailorOptions(
      handleNameNotFoundUI: true,
      isLoggingEnabled: true,
      customTransition: MyCustomTransition(),
      defaultTransitions: [
        SailorTransition.slide_from_bottom,
        SailorTransition.zoom_in,
      ],
      defaultTransitionCurve: Curves.decelerate,
      defaultTransitionDuration: Duration(milliseconds: 500),
    ),
  );

  /// ROUTES
  static void createRoutes() {
    sailor.addRoutes(
      [
        SailorRoute(
          name: "/LOGIN_PAGE",
          builder: (context, args, params) => LoginPage(),
        ),
        SailorRoute(
          name: "/HOME_PAGE",
          builder: (context, args, params) => HomePage(),
        ),

       .....
       .....
       .....
       .....
       .....

User Login Button:

 InkWell(
            splashColor: yellowColor,
            onTap: () {
              Routes.sailor.navigate("/HOME_PAGE"); 
            },

My Logout function:

  Future<void> _navigationToLoginPage() async {
    await Future.delayed(Duration.zero, () async {
      Routes.sailor.pop(true);
      await Routes.sailor.navigate("/LOGIN_PAGE");
    });
  }

[Docs] Difference to Navigator/Router

Maybe it's just me, but after 2 mins of reading the documentation I didn't see the main benefit of this package compared to Navigator or Router already integrated in flutter.
You really should stress this out in the documentation first (what's the problem you are trying to solve and who is this package for).

Good luck.

How can I retrieve the param values in opened page in initState?

In my route:

        SailorRoute(
          name: "/ACCOUNT_DETAILS",
          builder: (context, args, params) => AccountDetails(),
          customTransition: MyCustomTransition(),
          defaultTransitions: [
            SailorTransition.slide_from_bottom,
            SailorTransition.zoom_in,
          ],
          params: [
            SailorParam<String>(
              name: 'userAccountNo',
              defaultValue: "...",
            ),
            SailorParam<String>(
              name: 'userAccountType',
              defaultValue: "...",
            ),
          ],
        ),

Passing the actual parameter when navigating to the new route:

  Future<void> _navigationToAccountsDetailPage() async {
    await Future.delayed(Duration(milliseconds: 400), () async {
      Routes.sailor.pop(true);
      await Routes.sailor.navigate("/ACCOUNT_DETAILS",
          params: {"userAccountNo": _userAccountNo, "userAccountType": _userAccountType});
    });
  }

This also printed empty

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    final _userAccountNo = Sailor.param<String>(context, 'userAccountNo');
    final _userAccountType = Sailor.param<String>(context, 'userAccountType');

    print("_userAccountNo: $_userAccountNo");
    print("_userAccountType: $_userAccountType");
  }

But how can I retrieve the values in opened (AccountsDetailPage) page in initState??

Sailor not working at all

Good day, i am trying out sailor for the first time, and i am not finding it funny. i have just setup a new application and trying out sailor but it keeps giving this error,

Compiler message:
/C:/tools/flutter/.pub-cache/hosted/pub.dartlang.org/sailor-0.7.0/lib/src/sailor.dart:429:43: Error: Can't return a value from a void function.
return this.navigatorKey.currentState.pop(result);
^
Target kernel_snapshot failed: Exception: Errors during snapshot creation: null
build failed.

Please here is the code block for you perusual.

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

void main() {
  Routes.createRoutes();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
      onGenerateRoute: Routes.sailor.generator(),
      navigatorKey: Routes.sailor.navigatorKey,
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

class SecondPage extends StatelessWidget {
  const SecondPage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text("This is the second page"),
    );
  }
}

class ThirdPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text("This is the third page"),
    );
  }
}

class Routes {
  static final sailor = Sailor();

  static void createRoutes() {
    sailor.addRoutes(
      [
        SailorRoute(
          name: "/first-page",
          builder: (context, args, param) => MyHomePage(),
        ),
        SailorRoute(
          name: "/second-page",
          builder: (context, args, param) => SecondPage(),
        ),
        SailorRoute(
          name: "/third-page",
          builder: (context, args, param) => ThirdPage(),
        ),
      ],
    );
  }
}

Thanks. I love the syntax of sailor but i am unable to use it
#26

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.