GgRouter - Easy Routing for Flutter

GgRouter is a simple and powerful routing package for Flutter. Just define your nested routes. Add query parameters. Define the route transitions. GgRouter will do the rest for you:

  • GgRouter selects the right widgets for rendering
  • GgRouter restores the previous route state.
  • GgRouter performs only necessary animations.
  • GgRouter parses the URI and applies it to your application state.
  • GgRouter synchronizes route tree changes to the browser URI.

Additionally, GgRouter allows you to create index routes, default routes, wildcard routes. And finally, it can backup and restore the complete route tree as JSON.

Demo

Klick here to watch a YouTube demo of GgRouter.

Features

Content

Initialize GgRouter

To initialize GgRouter, create a MaterialApp.router(...) instance and provide it with an instance of GgRouterDelegate and GgRouterInformationParser.

class MyApp extends StatelessWidget {
  MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerDelegate: GgRouterDelegate(
        child: Scaffold(
          body: GgRouterExample(),
        ),
      ),
      routeInformationParser: GgRouteInformationParser(),
    );
  }
}

Define routes

Page routes

Use the GgRouter widget to add routes to your application structure:

@override
Widget build(BuildContext context){
  return GgRouter(
    {
      'sports':         _sports,
      'transportation': _transportation,
      'places':         _places
    }
  );
}

Each of these routes will replace its siblings when being selected.

Popover routes

To show a route in front of existing content, create a popover route:

GgPopoverRoute(
  // ...
  name: 'popover',          // The route which will open the popover
  base: _myWidget,          // The regular content
  popover: _myDialog,       // The popover
  inAnimation: _rotateIn,   // The appearing animation
  outAnimation: _rotateOut, // The disappearing animation
),

Nested routes

You can arbitrarily nest these routes. Just place another GgRouter widget within one of the routes. Child GgRouter widgets do not need to be direct children.

Handling fallbacks

Index route

To define a default route which is shown when none of the routes is selected, add a route with name '_INDEX_':

GgRouter(
  {
    '_INDEX_': _index,
    'sports': _sports,
    // ...
  }
);

Default route

Chose a default route when no _INDEX route is defined by using the defaultRoute parameter.

GgRouter(
  {
    'sports': _sports,
    // ...
  },
  defaultRoute: 'sports'
);

Wildcard routes

If you want to handle arbitrary route names, e.g., parsing an ID from the URI, you can setup a wild card route using * as route name:

return GgRouter(
  {
    // ...
    '*': _wildCardPage,
  },
  /// ...
);

To get the name of the actual route, use GgRouter.of(context).routeName:

Widget _wildCardPage(BuildContext context) {
  final routeName = GgRouter.of(context).routeName;
  // ... do something with the routeName
}

Use GgRouter.of(context).navigateTo('/sports/football') to absolutely navigate to the football page, no matter where you currently are in your application.

  • Use GgRouter.of(context).navigateTo('./dialog/') to navigate to the direct child.
  • Use GgRouter.of(context).navigateTo('..') to navigate to the parent.
  • Use GgRouter.of(context).navigateTo('../../') to navigate to the grand parent.
  • Use GgRouter.of(context).navigateTo('../transportation/') to navigate to a sibling.

When you switch to a route, you might want to open the child route that was opened when you left the route the last time. Use the _LAST_ keyword to activate this route:

GgRouter.of(context).navigateTo('/sports/_LAST_');

Navigation buttons and GgRouter widgets can be used side by side. Navigation elements can use GgRouter.of(context) to perform various routing operations:

  • Use GgRouter.of(context).navigateTo('...') to navigate to a route.
  • Use GgRouter.of(context).routeNameOfActiveChild to find out which child route is currently visible.
  • Use GgRouter.of(context).indexOfActiveChild to find out which of the items in a BottomNavigationBar need to be styled as visible elements.
  • Use GgRouter.of(context).onActiveChildChange to rebuild the navigation bar, when the visible child changes.

URI query params

Define query params

Use GgRouteParams to define a list of query params that are shown in the URI.

GgRouteParams(
  params: {
    'a': GgRouteParam<bool>(seed: false),
    'b': GgRouteParam<int>(seed: 5),
    'c': GgRouteParam<String>(seed: 'hello'),
  },
  child: // ...
}

The param names a, b, and c must only be used one time in a route path. Different route paths can define the same parameter names. When switching a route, also the route parameters will change.

Access query params

To use the value of a query param in a widget, use these method:

  • Use GgRouter.of(context).param('a')?.value to get or set the value of the query param a.
  • Use GgRouter.of(context).param('a')?.stream to observe value changes of query param a.

Animations

Animate route transitions

GgRouter offers a simple way to animate route transitions. Use inAnimation and outAnimation to define animations that are applied to the appearing and the disappearing route:

builder: (context) {
  return GgRouter(
    // ...
    inAnimation: (context, animation, child)
      => Transform.scale(scale: animation.value, child: child),
    outAnimation: (context, animation, child)
      => Transform.scale(scale: 1.0 - animation.value, child: child),
  );
},

With the possibility to define separate in and out animations, you can create advanced transitions. E.g., move an appearing widget in from the left side and out from the right side.

Route specific animations

To find out which route is currently fading in or fading out, use the following methods within your animation callback:

  • GgRouter.of(context).indexOfChildAnimatingOut
  • GgRouter.of(context).nameOfChildAnimatingOut
  • GgRouter.of(context).indexOfChildAnimatingIn
  • GgRouter.of(context).nameOfChildAnimatingIn
Widget _moveOut(
  BuildContext context,
  Animation animation,
  Widget child,
) {
  final w = MediaQuery.of(context).size.width;
  final h = MediaQuery.of(context).size.height;
  final index = GgRouter.of(context).indexOfChildAnimatingOut;

  final toRight = Offset(w * (animation.value), 0);
  final toBottom = Offset(0, h * (animation.value));
  final toLeft = Offset(w * (-animation.value), 0);

  Offset offset = index == 0
      ? toLeft
      : index == 1
          ? toBottom
          : toRight;

  return Transform.translate(
    offset: offset,
    child: child,
  );
}

Save and restore route state

GgRouter constructor offers a saveState and restorState callback:

  • saveState will be called with a JSON string when the route state changes.
  • restoreState will be called at the very first beginning and allows you to restore a previously defined state.

Error handling

If you open a URI in the browser that is not defined using GgRouter(...), an error is thrown. To handle that error, assign an error handler to GgRouter.of(context).errorHandler.

Example

An example demonstrating all of the features above can be found in example/main.dart.

Features and bugs

Please file feature requests and bugs at GitHub.

Libraries

gg_router