Fluro is a Flutter routing library that adds flexible routing options like wildcards, named parameters and clear route definitions.

Last update: May 19, 2022

English | Português



The brightest, hippest, coolest router for Flutter.

Version Build Status

Features

  • Simple route navigation
  • Function handlers (map to a function instead of a route)
  • Wildcard parameter matching
  • Querystring parameter parsing
  • Common transitions built-in
  • Simple custom transition creation
  • Follows beta Flutter channel
  • Null-safety

Example Project

There is a pretty sweet example project in the example folder. Check it out. Otherwise, keep reading to get up and running.

Getting started

First, you should define a new FluroRouter object by initializing it as such:

final router = FluroRouter();

It may be convenient for you to store the router globally/statically so that you can access the router in other areas in your application.

After instantiating the router, you will need to define your routes and your route handlers:

var usersHandler = Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) {
  return UsersScreen(params["id"][0]);
});

void defineRoutes(FluroRouter router) {
  router.define("/users/:id", handler: usersHandler);

  // it is also possible to define the route transition to use
  // router.define("users/:id", handler: usersHandler, transitionType: TransitionType.inFromLeft);
}

In the above example, the router will intercept a route such as /users/1234 and route the application to the UsersScreen passing the value 1234 as a parameter to that screen.

Navigating

You can use FluroRouter with the MaterialApp.onGenerateRoute parameter via FluroRouter.generator. To do so, pass the function reference to the onGenerate parameter like: onGenerateRoute: router.generator.

You can then use Navigator.push and the flutter routing mechanism will match the routes for you.

You can also manually push to a route yourself. To do so:

router.navigateTo(context, "/users/1234", transition: TransitionType.fadeIn);

Class arguments

Don't want to use strings for params? No worries.

After pushing a route with a custom RouteSettings you can use the BuildContext.settings extension to extract the settings. Typically this would be done in Handler.handlerFunc so you can pass RouteSettings.arguments to your screen widgets.

/// Push a route with custom RouteSettings if you don't want to use path params
FluroRouter.appRouter.navigateTo(
  context,
  'home',
  routeSettings: RouteSettings(
    arguments: MyArgumentsDataClass('foo!'),
  ),
);

/// Extract the arguments using [BuildContext.settings.arguments] or [BuildContext.arguments] for short
var homeHandler = Handler(
  handlerFunc: (context, params) {
    final args = context.settings.arguments as MyArgumentsDataClass;

    return HomeComponent(args);
  },
);

GitHub

https://github.com/lukepighetti/fluro
Comments
  • 1. Error: 'dependOnInheritedWidgetOfExactType' was called on null

    I'm getting the following error when calling the Router.generator method:

    The following NoSuchMethodError was thrown building IconTheme(color: Color(0xdd000000)):
    The method 'dependOnInheritedWidgetOfExactType' was called on null.
    Receiver: null
    Tried calling: dependOnInheritedWidgetOfExactType<_InheritedTheme>()
    
    The relevant error-causing widget was
        MaterialApp 
    lib/app.dart:30
    When the exception was thrown, this was the stack
    #0      Object.noSuchMethod  (dart:core-patch/object_patch.dart:53:5)
    #1      Theme.of 
    package:flutter/…/material/theme.dart:128
    #2      Router.matchRoute.<anonymous closure> 
    package:fluro/src/router.dart:130
    #3      Router.matchRoute 
    package:fluro/src/router.dart:183
    #4      Router.generator
    

    It looks like this change may be the source of the bug.

    Is there a reason that null is passed for BuildContext here?

    Reviewed by smkhalsa at 2020-03-25 20:23
  • 2. Added ability to define transition type when defining route

    Also updated the test dependency in pubspec.yaml.

    This is just a first pass to get your thoughts...

    It should cover the enhancements needed for https://github.com/theyakka/fluro/issues/27

    Reviewed by jonsamwell at 2018-09-09 05:43
  • 3. No registered route was found to handle '/videos/1234'.

    I tried to implement routing in my application with fluro, however I had several problems following the 'getting started' guide on Github.

    My implementation is rather simple, since the new GestureDetector( onTap: () { _handleTap(context); }, .... ) is supposed to manually push the route with void _handleTap(BuildContext context) { router.navigateTo(context, "/videos/1234", transition: TransitionType.native); }.

    The router is initialized according to the getting started example, but once I test the app the error

    No registered route was found to handle '/videos/1234'.

    What else is needed in order to push the route, or am I doing something wrong?

    Reviewed by stemuk at 2017-11-23 16:22
  • 4. Why pass arguments via routes? Why use Fluro?

    The fluro docs are missing the reason why one would use fluro. It seems to me that the main reason to use fluro is to pass arguments within the route string. But doing so comes with a number of disadvantages relative to passing arguments via objects:

    • You lose compile-time type enforcement for all arguments that aren't ultimately strings. You have to typecast somewhere.
    • You have to reference objects by ID and look them up by ID instead of just getting the object directly. You have to manage unique IDs for objects that wouldn't otherwise have them, and you may need to also cache remotely loaded objects so they're not reloaded on page change.

    I can think of the following possible advantages of passing all arguments via the route:

    • If Flutter eventually supports Android's ability to kill and restart an app in the background, the app could restart on the same page merely by knowing the route. The Flutter team may decide to use the route for this purpose.
    • If you plan to share code between mobile and web versions of your app, by making each mobile page stateless, you're better positioned for doing so.

    Neither of these advantages is realized today. The Flutter team has not decided how to restart Android apps (to my knowledge), and Flutter Web is still too buggy and slow for production.

    So why have you decided to use fluro for argument passing? Is it just for potential future benefit, or are you realizing benefit now? (I'm trying to plan how I should structure my own apps.)

    Reviewed by jtlapp at 2019-10-23 13:50
  • 5. Automatic navigation to / route, when opening a keyboard

    When I open a keyboard to type into a TextFormField, I get navigated to the target of the / route.

    main.dart

    class ImmoMatchApp extends StatelessWidget {
      final ImmoNavigator _navigator = new ImmoNavigator();
    
      @override
      Widget build(BuildContext context) {
        _navigator.initRoutes();
        return new MaterialApp(
          title: "ImmoMatch",
          onGenerateRoute: _navigator.router.generator,
        );
      }
    }
    

    ImmoNavigator:

    class ImmoNavigator {
      static final ImmoNavigator _navigator = new ImmoNavigator._internal();
      factory ImmoNavigator() {
        return _navigator;
      }
      ImmoNavigator._internal();
    
      final Logger _logger = new Logger('ImmoNavigator');
      final Router router = new Router();
    
      initRoutes() {
        if (router.match('/') == null) {
          print('initializing routes');
          router.define(
            "/",
            handler: new Handler(
              handlerFunc: (BuildContext context, Map<String, dynamic> params) {
                print("Navigating to Boot");
                return new BootView();
              },
            ),
          );
          router.define(
            "/login",
            handler: new Handler(
              handlerFunc: (BuildContext context, Map<String, dynamic> params) {
                print("Navigating to Login");
                return new LoginView();
              },
            ),
          );
          router.define(
            "/signin",
            handler: new Handler(
              handlerFunc: (BuildContext context, Map<String, dynamic> params) {
                return new SignInView();
              },
            ),
          );
        }
      }
    }
    

    The TextFormField is in the LoginView and when I click on it, "Navigating to Boot" gets printed out.

    Reviewed by tobire at 2018-08-05 15:50
  • 6. New direction + maintainer

    Good news. Fluro is moving to a new home.

    Say hello to @lukepighetti! Luke is one of the most active, enthusiastic and talented Flutter developers there is. I'm glad it's going to a friend.

    I won't get too deep into it but some of the policies adopted by the Flutter team have made it pretty much untenable for Yakka to personally maintain or develop this library. Constant breaking changes and, what we consider to be, generally less than ideal community consultation / collaboration have taken all the "fun" out of providing Fluro to you all.

    I know many of you have already moved on or become frustrated with lack of communication and progress. I've, personally, obsessed over what to do with the project over many months. Many times I thought of shutting it down. However, I think there is still plenty life left in Fluro! There are still lots of cool ideas and everyone is excited for it to move way beyond what we envision.

    Thanks for all your support!

    Luke

    Reviewed by lukef at 2020-10-14 20:45
  • 7. ambiguous class name "Router" in Flutter 1.20 (and maybe earlier)

    Describe the bug I upgraded to Flutter 1.20 and there is a name collision:

    error: The name 'Router' is defined in the libraries 'package:fluro/src/router.dart (via package:fluro/fluro.dart)' and 'package:flutter/src/widgets/router.dart (via package:flutter/widgets.dart)'. (ambiguous_import at [flutter_door_buzzer] lib/src/routes.dart:6)
    error: The name 'Router' is defined in the libraries 'package:fluro/src/router.dart (via package:fluro/fluro.dart)' and 'package:flutter/src/widgets/router.dart (via package:flutter/widgets.dart)'. (ambiguous_import at [flutter_door_buzzer] lib/src/routes.dart:6)
    

    To Reproduce

    1. Go to github.com/falkorichter/open-taffeta-mobile-app/tree/macos
    2. run ./flutterw build apk or any other build command

    Expected behavior It should build

    Screenshots image

    Desktop (please complete the following information):

    • OS: [e.g. iOS]
    • Browser [e.g. chrome, safari]
    • Version [e.g. 22]
    Reviewed by falkorichter at 2020-08-19 08:26
  • 8. Flutter 1.15 introduces a breaking change

    Describe the bug A clear and concise description of what the bug is.

    To Reproduce Steps to reproduce the behavior:

    1. Go to '...'
    2. Click on '....'
    3. Scroll down to '....'
    4. See error

    Expected behavior A clear and concise description of what you expected to happen.

    Screenshots If applicable, add screenshots to help explain your problem.

    Desktop (please complete the following information):

    • OS: [e.g. iOS]
    • Browser [e.g. chrome, safari]
    • Version [e.g. 22]

    Smartphone (please complete the following information):

    • Device: [e.g. iPhone6]
    • OS: [e.g. iOS8.1]
    • Browser [e.g. stock browser, safari]
    • Version [e.g. 22]

    Additional context Add any other context about the problem here.

    Reviewed by piratejason at 2020-02-04 14:48
  • 9. the reverseTransitionDuration params is not found for PageRouteBuilder

    flutter version

    • Flutter version 1.20.2 at /Library/flutter
    • Framework revision bbfbf1770c (10 weeks ago), 2020-08-13 08:33:09 -0700
    • Engine revision 9d5b21729f
    • Dart version 2.9.1
    

    Xcode's output:

    /Library/flutter/.pub-cache/hosted/pub.flutter-io.cn/fluro-1.7.4/lib/src/fluro_router.dart:223:11: Error: No named parameter with the name 'reverseTransitionDuration'.
              reverseTransitionDuration: transition == TransitionType.none
              ^^^^^^^^^^^^^^^^^^^^^^^^^
    /Library/flutter/packages/flutter/lib/src/widgets/pages.dart:60:3: Context: Found this candidate, but the arguments don't match.
      PageRouteBuilder({
      ^^^^^^^^^^^^^^^^
    

    Command PhaseScriptExecution failed with a nonzero exit code

    If I remove these lines of code

    Reviewed by YufengHu123 at 2020-10-22 01:48
  • 10. Route Guard and Rerouting

    Is there a way to implement a simple route guard? For example if the user is not logged in (I can get the info from an inherited widget out of the context) I want to go to the /login page and in the other hand if I am on /login but the user is logged in I want to go to the root of the app. I can't reroute during a Handler or I will get the error, which make sense:

    I/flutter ( 3387): The following assertion was thrown building Builder(dirty):
    I/flutter ( 3387): setState() or markNeedsBuild() called during build.
    I/flutter ( 3387): This Overlay widget cannot be marked as needing to build because the framework is already in the
    I/flutter ( 3387): process of building widgets. A widget can be marked as needing to be built during the build phase
    I/flutter ( 3387): only if one of its ancestors is currently building. This exception is allowed because the framework
    I/flutter ( 3387): builds parent widgets before children, which means a dirty descendant will always be built.
    I/flutter ( 3387): Otherwise, the framework might not visit this widget during this build phase.
    I/flutter ( 3387): The widget on which setState() or markNeedsBuild() was called was:
    I/flutter ( 3387):   Overlay-[LabeledGlobalKey<OverlayState>#46a1c](state: OverlayState#29c46(entries:
    

    I could return the Login page instead of any other page but this looks weird as my Login page will be on the route / or any other route. I can clear the stack after logging in, but still a bit stange no?

    How would you manage redirection?

    Reviewed by Benoss at 2018-11-12 10:01
  • 11. Push route without BuildContext

    Hey, I've been trying to dig into this for a bit now. What happens if I want to push a route programmatically, say from a listener setup in my main widget constructor.

    class Global {
      static Router router;
    }
    
    void main() {
      runApp(new App());
    }
    
    class App extends StatelessWidget {
      Router router;
    
      App() {
        Global.router = new Router()
          ..define("home",
              handler: new Handler(handlerFunc: (BuildContext context, params) {
            return new HomePage();
          }))
          ..define("auth",
              handler: new Handler(handlerFunc: (BuildContext context, params) {
            return new AuthPage();
          }));
    
        someListener.listen((user) {
          if (user == null) {
            // Do logout, navigate to "auth"
          }
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return new MaterialApp(
          onGenerateRoute: Global.router.generator,
        );
      }
    }
    

    I'm trying to find a way to access the root navigator from my listener, and push directly from there.

    This is probably also related to Flutter's built-in navigation.

    What would be a solution?

    Reviewed by Cretezy at 2018-03-11 05:43
  • 12. remove hash from the url

    when I tried to remove the hash from the URL (using setUrlStrategy(PathUrlStrategy()) and https://pub.dev/packages/url_strategy) then the URL path navigation is not working.

    Reviewed by ajeetkumarsah at 2022-05-23 07:00
  • 13. notFoundHanler == null

    `AppRouteMatch? match = _routeTree.matchRoute(path!); AppRoute? route = match?.route; if (transitionDuration == null && route?.transitionDuration != null) { transitionDuration = route?.transitionDuration; }

    Handler handler = (route != null ? route.handler : notFoundHandler);
    TransitionType? transition = transitionType;
    if (transitionType == null) {
      transition = route != null ? route.transitionType : TransitionType.native;
    }
    if (route == null && notFoundHandler == null) {
      return RouteMatch(
          matchType: RouteMatchType.noMatch,
          errorMessage: "No matching route was found");
    }
    Map<String, List<String>> parameters =
        match?.parameters ?? <String, List<String>>{};
    if (handler.type == HandlerType.function) {
      handler.handlerFunc(buildContext, parameters);
      return RouteMatch(matchType: RouteMatchType.nonVisual);
    }`
    

    if notFoundHandler == null && route == null, Handler handler = (route != null ? route.handler : notFoundHandler); shoud use Handler? handler ,or if (route == null && notFoundHandler == null) can’t execute

    Reviewed by LatCui at 2022-05-17 09:53
  • 14. Reloaded web page 404 error

    I have created web app getting 404 error on reloading web page using browser button so i tried your example code uploaded to my server getting same error 404 when reload the page using browser button

    Reviewed by vishalvishvakarma1111 at 2022-04-22 05:27
  • 15. All previous view loaded when direct access via url

    Hello there,

    I have a small issue, I don't really know if it's a normal behavior or not. I have different handler define like this: -"/" -"/category" -"/category/id"

    Users can access to "/category/id" directly with a given url, but at the first launch of the app, end each time tab is refreshed, it seems that the view from "/category" is also build (and the call to the different web service are made too, unfortunately...)

    Thanks

    Reviewed by Velliane at 2022-04-11 13:48
  • 16. Arguments is null when using HandlerType.function

    Add type: HandlerType.function to the example code in README.

    FluroRouter.appRouter.navigateTo(
      context,
      'home',
      routeSettings: RouteSettings(
        arguments: MyArgumentsDataClass('foo!'),
      ),
    );
    
    var homeHandler = Handler(
      type: HandlerType.function,   // set function as the type.
      handlerFunc: (context, params) {
        final args = context.settings.arguments as MyArgumentsDataClass;  // args will be null.
        return HomeComponent(args);
      },
    );
    

    expected: args is MyArgumentsDataClass('foo!')

    Reviewed by 623637646 at 2022-04-08 12:29

Related

Flexible retry library for Dio package

Flexible retry library for Dio package. This is a next generation of an abandoned dio_retry package.

May 23, 2022
Easy to use session wrapper that adds support to session storage and management in flutter.

flutter_session_manager Adds an easy to use wrapper to session management in flutter. Allows for easy session storage and management. The session pers

Feb 15, 2022
Quiver is a set of utility libraries for Dart that makes using many Dart libraries easier and more convenient, or adds additional functionality.

Quiver is a set of utility libraries for Dart that makes using many Dart libraries easier and more convenient, or adds additional functionality.

May 19, 2022
Dependency Injection is a great design pattern that allows us to eliminate rigid dependencies between elements and it makes the application more flexible
Dependency Injection is a great design pattern that allows us to eliminate rigid dependencies between elements and it makes the application more flexible

GetX lib DI pattern Dependency Injection is a great design pattern that allows us to eliminate rigid dependencies between elements and it makes the ap

Feb 1, 2022
A simple flexible API wrapper for coinbase commerce API. Totally unofficial.

coinbase_commerce A dart library that connects to interact with the Coinbase Commerce API. Enables projects to connect seamlessly to coinbase and rece

Oct 17, 2021
Feb 2, 2022
The Dart Time Machine is a date and time library for Flutter, Web, and Server with support for timezones, calendars, cultures, formatting and parsing.
The Dart Time Machine is a date and time library for Flutter, Web, and Server with support for timezones, calendars, cultures, formatting and parsing.

The Dart Time Machine is a date and time library for Flutter, Web, and Server with support for timezones, calendars, cultures, formatting and parsing.

Oct 8, 2021
Library for help you make userbot or bot telegram and support tdlib telegram database and only support nodejs dart and google-apps-script

To-Do telegram client dart ✅️ support multi token ( bot / userbot ) ✅️ support bot and userbot ✅️ support telegram-bot-api local server ✅️ support tel

Apr 30, 2022
Scribble is a lightweight library for freehand drawing in Flutter supporting pressure, variable line width and more!
Scribble is a lightweight library for freehand drawing in Flutter supporting pressure, variable line width and more!

Scribble Scribble is a lightweight library for freehand drawing in Flutter supporting pressure, variable line width and more! A

May 26, 2022
Flutter library to add gestures and animations to each Shape you draw on your canvas in your CustomPainter
Flutter library to add gestures and animations to each Shape you draw on your canvas in your CustomPainter

Flutter library to bring your CustomPainter ?? to Life ✨ ⚡️ touchable library gives you the ability to add various gestures and animations to each Sha

May 17, 2022
A library for YAML manipulation with comment and whitespace preservation.

Yaml Editor A library for YAML manipulation while preserving comments. Usage A simple usage example: import 'package:yaml_edit/yaml_edit.dart'; void

Apr 11, 2022
A JMAP client library in Dart to make JMAP method calls and process the responses

JMAP Dart client A JMAP client library to make JMAP method calls and process the responses. We most notably use it to write the TMail Flutter applicat

May 26, 2022
A library to process noun, verb and adjectives transformations/conjugation.

A library to process noun (plural to singular and singular to plural), verb (gerund, present & past) and adjectives (comparative & superlative) transformations/conjugation.

May 15, 2022
Redux Compact is a library that aims to reduce the massive amount of boilerplate that follows maintaining a Flutter Redux application.

Redux Compact Redux Compact is a library that aims to reduce the massive amount of boilerplate that follows maintaining a Flutter Redux application. T

Apr 8, 2022
A Flutter library to make Rest API clients more easily. Inspired by Java Feing.

A Flutter library to make Rest API clients more easily. Inspired by Java Feing. Features Facilitated JSON encode and decode using common interfaces. F

Mar 15, 2022
A Dart library to parse Portable Executable (PE) format

pefile A Dart library to parse Portable Executable (PE) format Usage A simple usage example: var pe = pefile.parse('C:\\Windows\\System32\\notepad.exe

Dec 25, 2021
This library contains methods that make it easy to consume Mpesa Api.

This library contains methods that make it easy to consume Mpesa Api. It's multi-platform, and supports CLI, server, mobile, desktop, and the browser.

Dec 15, 2021