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

Overview

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);
  },
);
Comments
  • Error: 'dependOnInheritedWidgetOfExactType' was called on null

    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?

    opened by smkhalsa 25
  • Added ability to define transition type when defining route

    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

    enhancement testing 
    opened by jonsamwell 14
  • No registered route was found to handle '/videos/1234'.

    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?

    opened by stemuk 11
  • Why pass arguments via routes? Why use Fluro?

    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.)

    question stack overflow 
    opened by jtlapp 10
  • Automatic navigation to / route, when opening a keyboard

    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.

    investigating information needed 
    opened by tobire 10
  • New direction + maintainer

    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

    Notice 
    opened by lukef 9
  • ambiguous class name

    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]
    opened by falkorichter 9
  • Flutter 1.15 introduces a breaking change

    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.

    investigating 
    opened by piratejason 9
  • the reverseTransitionDuration params is not found for PageRouteBuilder

    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

    opened by YufengHu123 8
  • Route Guard and Rerouting

    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?

    question stack overflow 
    opened by Benoss 8
  • Push route without BuildContext

    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?

    question stack overflow 
    opened by Cretezy 8
  • Flutter web: Refreshing dynamic route opens blank page

    Flutter web: Refreshing dynamic route opens blank page

    I encountered an issue while developing a Flutter Web App using the fluro routing system. In the app it is possible to access an overview of plugins as well as a specific plugin detail page. Therefore a fluro router is set up when starting the app:

    void _setupRouter() {
      final router = FluroRouter();
    
      router.notFoundHandler = Handler(handlerFunc: (c, p) => NotFoundPage());
      router.define('/', handler: Handler(handlerFunc: (c, p) => LandingPage()));
      router.define('/plugins', handler: Handler(handlerFunc: (c, p) => const PluginStorageView()));
      var pluginHandler = Handler(handlerFunc: (c, p) => PluginDetailScreen(storageItemId: p['id']?.first ?? 'unknown'));
      router.define('/plugins/:id', handler: pluginHandler);
    
      App.router = router;
    }
    

    When clicking on an plugin in the overview screen, the detail page is opened using this statement:

    App.router.navigateTo(context, '/plugins/${plugin.id}')
    

    All of this works pretty fine opening a webpage looking like this: image

    However, when refreshing the page or opening the app at a specified dynamic route such as mywebsite.com/plugins/1234, the website is just white and not a single element of my Material app is loaded: image

    Is there any way I can access dynamic routes directly using an URL?

    opened by Schloool 0
  • Expected a value of type 'Handler', but got one of type 'Null'

    Expected a value of type 'Handler', but got one of type 'Null'

    I have url : www.foobar.com/template/1234&token=1234 where the first 1234 is the template id. here is my code:

    class CustomRouter {
      static FluroRouter router = FluroRouter();
      static final Handler _splashPage = Handler(
        handlerFunc: ((context, parameters) {
          return const SplashPage();
        }),
      );
      static final Handler _templatePageHandler = Handler(
        handlerFunc: ((context, parameters) {
          String templateId = parameters['tID']!.first;
          return const TemplatePage(templateId: templateId);
        }),
      );
     
      static void defineRoutes() {
        router.define("/", handler: _splashPage);
        router.define(
          "/template/:templateId",
          handler: _templatePageHandler,
          transitionType: TransitionType.fadeIn,
        );
      }
    }
    

    and the app.dart is as:

    class App extends StatefulWidget {
      const App({Key? key}) : super(key: key);
    
      @override
      State<App> createState() => _AppState();
    }
    
    class _AppState extends State<App> {
      @override
      void initState() {
        super.initState();
        CustomRouter.defineRoutes();
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Name",
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
          ),
          onGenerateRoute: CustomRouter.router.generator,
        );
      }
    }
    

    Throws an Expected a value of type 'Handler', but got one of type 'Null'. When I tried running the same code but without the :templateId it works fine.

    opened by fahimhoque 1
  • remove hash from the url

    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.

    opened by ajeetkumarsah 0
  • notFoundHanler == null

    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

    opened by LatCui 0
  • Reloaded web page 404 error

    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

    opened by vishalvishvakarma1111 0
Releases(2.0.3)
Owner
Luke Pighetti
Mobile engineer
Luke Pighetti
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

Eduardo Migueis 2 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.

Google 905 Jan 2, 2023
Flexible retry library for Dio package

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

Rodion Mostovoy 43 Dec 12, 2022
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

Trương Việt Hoàng 4 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

Onuoha Obinna 3 Oct 17, 2021
Okan YILDIRIM 37 Jul 10, 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.

null 2 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

Azka Full Snack Developer:) 73 Jan 7, 2023
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

Tim Created It. 73 Dec 16, 2022
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

Natesh Bhat 190 Jan 7, 2023
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

Dart 17 Dec 26, 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

LINAGORA 18 Dec 19, 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.

Kawaljeet Singh 6 Nov 4, 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

Ómar Óskarsson 9 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

null 2 Mar 15, 2022
A Flutter plugin for XUpdate -- Android Update Library

flutter_xupdate A Flutter plugin for XUpdate -- Android Update Library。See the use Chinese Document for details。 About me WeChat public number juejin

薛翔 233 Dec 25, 2022
Flutter library that handles BLE operations for multiple devices

Flutter reactive BLE library Flutter library that handles BLE operations for multiple devices. Usage The reactive BLE lib supports the following: BLE

null 1 Apr 12, 2022