Last active
July 11, 2022 01:55
-
-
Save dukex/38be63ebd8a6f9f0a209a4f6949080b4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
final books = [ | |
const Book(id: '3160', name: 'The Odyssey', author: 'Homer'), | |
const Book(id: '8795', name: 'The Divine Comedy', author: 'Dante Alighieri'), | |
]; | |
void main() { | |
final betterRoutes = BetterRouter(routes: { | |
'/': (_) => const HomeScreen(), | |
'/books': (_) => const BooksScreen(), | |
r"\/books\/(?<id>.+)": (_) => const BookScreen(), | |
'-matchAll': (_) => const Text('not found page'), | |
}); | |
runApp(MaterialApp(onGenerateRoute: betterRoutes)); | |
} | |
class HomeScreen extends StatelessWidget { | |
const HomeScreen({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return AppShell( | |
title: const Text("Welcome"), | |
body: TextButton( | |
onPressed: () => Navigator.pushNamed(context, "/books"), | |
child: const Text("Go to books"))); | |
} | |
} | |
class Book { | |
final String id; | |
final String name; | |
final String author; | |
const Book({required this.id, required this.name, required this.author}); | |
} | |
class BooksScreen extends StatelessWidget { | |
const BooksScreen({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return AppShell( | |
title: const Text("Books"), | |
body: Column(children: [ | |
for (var i = 0; i < books.length; i++) | |
TextButton( | |
onPressed: () => | |
Navigator.pushNamed(context, "/books/${books[i].id}"), | |
child: Text(books[i].name)) | |
])); | |
} | |
} | |
class BookScreen extends StatelessWidget { | |
const BookScreen({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
final params = | |
ModalRoute.of(context)!.settings.arguments as Map<String, String?>; | |
final book = books.firstWhere((b) => b.id == params["id"]!); | |
return AppShell( | |
title: Text(book.name), | |
body: Column( | |
children: [Text("By ${book.author}"), Text("ID: ${params['id']}")], | |
)); | |
} | |
} | |
class AppShell extends StatelessWidget { | |
final Widget body; | |
final Widget title; | |
const AppShell({super.key, required this.body, required this.title}); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold(appBar: AppBar(title: title), body: body); | |
} | |
} | |
class RouteInfo { | |
final String path; | |
final Map<String, String?> data; | |
RouteInfo({required this.path, required this.data}); | |
} | |
class Matcher { | |
final String path; | |
final RegExp matcher; | |
const Matcher(this.path, this.matcher); | |
RouteInfo? matchWith(expPath) { | |
final match = matcher.firstMatch(expPath); | |
if (match == null) { | |
return null; | |
} | |
final values = | |
List.generate(match.groupCount, (index) => match.group(index + 1)); | |
final data = Map.fromIterables(match.groupNames, values); | |
return RouteInfo(path: path, data: data); | |
} | |
} | |
class BetterRouter { | |
final Map<String, WidgetBuilder> routes; | |
BetterRouter({required this.routes}) : assert(routes['-matchAll'] != null); | |
pageRouteBuilder<T>(RouteSettings settings, WidgetBuilder builder) { | |
return MaterialPageRoute<T>(settings: settings, builder: builder); | |
} | |
Route<dynamic>? call(RouteSettings settings) { | |
final String path = settings.name!; | |
RouteInfo routeInfo = _routeInfo(path); | |
final WidgetBuilder pageContentBuilder = routes[routeInfo.path]!; | |
final Route<dynamic> route = pageRouteBuilder<dynamic>( | |
settings.copyWith(arguments: routeInfo.data), | |
pageContentBuilder, | |
); | |
return route; | |
} | |
Iterable<Matcher> get _mapper => | |
routes.keys.map((path) => Matcher(path, RegExp("^$path\$"))); | |
final _mapped = <String, RouteInfo>{}; | |
RouteInfo _routeInfo(path) => _mapped.putIfAbsent( | |
path, | |
() => _mapper.map((m) => m.matchWith(path)).lastWhere( | |
(element) => element != null, | |
orElse: () => RouteInfo(path: "-matchAll", data: {}), | |
)!); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment