x_router 0.1.0+1 x_router: ^0.1.0+1 copied to clipboard
A simple and powerful routing frameworks for flutter. Uses Navigator 2.0 under the hood.
import 'package:example/layout/home_layout.dart';
import 'package:example/pages/dashboard_page.dart';
import 'package:example/pages/favorites_page.dart';
import 'package:example/pages/loading_page.dart';
import 'package:example/pages/preferences_page.dart';
import 'package:example/pages/product_details_page.dart';
import 'package:example/pages/products_page.dart';
import 'package:example/pages/sign_in_page.dart';
import 'package:example/services/auth_service.dart';
import 'package:example/services/products_service.dart';
import 'package:flutter/material.dart';
import 'package:x_router/x_router.dart';
import 'pages/sign_in_page.dart';
/// This example features a complex
/// app navigation system with child routing
/// tab navigation
///
/// Most apps don't need all these features
/// but despite the complexity the example
/// is quite understandable
/// all the locations in the app
class RouteLocations {
static const home = '/app';
static const dashboard = '$home/dashboard';
static const products = '$home/products';
static const favorites = '$home/favorites';
static const preferences = '$home/preferences';
static const productDetail = '$home/products/:id';
static const productInfo = '$productDetail/info';
static const productComments = '$productDetail/comments';
static const signIn = '/sign-in';
}
/// the routes for each location
final _routes = [
XRoute(
path: RouteLocations.signIn,
builder: (ctx, route) => SignInPage(),
titleBuilder: (ctx, route) => 'sign in ! (Browser tab title)',
),
XRoute(
path: RouteLocations.preferences,
builder: (ctx, route) => const PreferencesPage(),
titleBuilder: (ctx, route) => translate(ctx, 'preferences'),
),
XRoute(
path: RouteLocations.dashboard,
builder: (ctx, route) => const HomeLayout(
child: DashboardPage(),
),
titleBuilder: (_, __) => 'dashboard',
),
XRoute(
path: RouteLocations.products,
builder: (ctx, route) => const HomeLayout(
child: ProductsPage(),
),
titleBuilder: (_, __) => 'products',
),
XRoute(
path: RouteLocations.favorites,
builder: (ctx, route) => const HomeLayout(
child: FavoritesPage(),
),
titleBuilder: (_, __) => 'My favorites',
),
XRoute(
path: RouteLocations.productDetail,
builder: (ctx, route) => HomeLayout(
child: ProductDetailsPage(route.pathParams['id']!),
),
// nested Router !
childRouterConfig: XChildRouterConfig(
resolvers: [
XRedirectResolver(
from: RouteLocations.productDetail,
to: RouteLocations.productInfo,
),
],
routes: [
XRoute(
path: RouteLocations.productInfo,
builder: (_, __) =>
const Center(child: Text('info (Displayed via nested router)')),
),
XRoute(
path: RouteLocations.productComments,
builder: (_, __) => const Center(
child: Text('comments (Displayed via nested router)')),
),
],
),
),
// XRoute(
// path: RouteLocations.home,
// builder: (ctx, route) => const HomePage(),
// childRouterConfig: XChildRouterConfig(
// routes: [
// // XRoute(
// // path: RouteLocations.productInfo,
// // builder: (ctx, route) => ProductDetailsPage(route.pathParams['id']!),
// // // here is a nested router
// // // childRouterConfig: XChildRouterConfig(
// // // resolvers: [
// // // XRedirectResolver(
// // // from: RouteLocations.productDetail,
// // // to: RouteLocations.productInfo,
// // // ),
// // // ],
// // // routes: [
// // // XRoute(
// // // path: RouteLocations.productInfo,
// // // builder: (_, __) => const Center(
// // // child: Text('info (Displayed via nested router)')),
// // // ),
// // // XRoute(
// // // path: RouteLocations.productComments,
// // // builder: (_, __) => const Center(
// // // child: Text('comments (displayed via nested router)')),
// // // ),
// // // ],
// // // ),
// // ),
// ],
// ),
// ),
];
// the router instance used throughout the app,
// It is up to you if you want to let it as a global variable, use injection,
// inherited widget to access it.. That's outside the scope.
final router = XRouter(
resolvers: [
AuthResolver(),
XRedirectResolver(
from: RouteLocations.home,
to: RouteLocations.dashboard,
matchChildren: false,
),
XRedirectResolver(
from: RouteLocations.productDetail,
to: '${RouteLocations.productDetail}/info',
matchChildren: false,
),
XNotFoundResolver(redirectTo: RouteLocations.home, routes: _routes),
],
routes: _routes,
);
/// goes to login page if we are not signed in.
class AuthResolver extends ValueNotifier implements XResolver {
AuthResolver() : super(AuthStatus.unknown) {
AuthService.instance.authStatusStream
.listen((authStatus) => value = authStatus);
}
@override
XResolverAction resolve(String target) {
switch (value) {
case AuthStatus.authenticated:
if (target.startsWith(RouteLocations.signIn)) {
return const Redirect(RouteLocations.home);
} else {
return const Next();
}
case AuthStatus.unautenticated:
if (target.startsWith(RouteLocations.signIn)) {
return const Next();
} else {
return const Redirect(RouteLocations.signIn);
}
case AuthStatus.unknown:
default:
return Loading(
(_, __) => const LoadingPage(text: 'Guard: Checking Auth Status'),
);
}
}
}
/// checks if the product exists, else redirect to product details page
class ProductFoundResolver implements XResolver {
final _productDetailsPattern = XRoutePattern(RouteLocations.productDetail);
@override
XResolverAction resolve(String target) {
final parsed = _productDetailsPattern.parse(target, matchChildren: true);
final productId = parsed.pathParameters['id'];
if (parsed.matches) {
try {
ProductsService.products
.firstWhere((product) => product.id == productId);
} catch (e) {
return const Redirect(RouteLocations.productDetail);
}
}
return const Next();
}
}
void main() async {
// router.eventStream.listen((event) => print(event));
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routeInformationParser: router.informationParser,
routerDelegate: router.delegate,
debugShowCheckedModeBanner: false,
title: 'XRouter Demo',
theme: ThemeData.from(
colorScheme: const ColorScheme.light(
primary: Colors.blue, secondary: Colors.blue))
.copyWith(
textSelectionTheme:
const TextSelectionThemeData(selectionColor: Colors.orange),
),
);
}
}
// fake translate
String translate(BuildContext ctx, String text) => text;