diff --git a/README.md b/README.md index e03e695..36ceeb9 100644 --- a/README.md +++ b/README.md @@ -6,200 +6,8 @@ with [catalyst_builder](https://pub.dev/packages/catalyst_builder) and the defau The main advantage is having dependency injection in all your routes / widgets without wire them the hard way. -## How does it work? -Check out the "[under the hood](./docs/under-the-hood.md)" doc. - - -## Installation - -- Follow the setup steps for [catalyst_builder](https://pub.dev/packages/catalyst_builder). -- Follow the setup steps for [explorator](https://pub.dev/packages/explorator/install). - -### Update the build.yaml - -Make sure that you set includePackageDependencies to true in the build.yaml: - -```yaml -# build.yaml -targets: - $default: - auto_apply_builders: true - builders: - catalyst_builder|buildServiceProvider: - options: - providerClassName: 'DefaultServiceProvider' - includePackageDependencies: true -``` - -### Register the RouteResolver - -```dart -@GenerateServiceProvider() -// The ServiceMap is required for wiring the RouteBuilder. Replace it with your own if necessary. -@ServiceMap(services: { - MaterialRouteBuilder: Service(exposeAs: RouteBuilder), -}) -void main() { - /// Create an instance of the service provider - var provider = DefaultServiceProvider(); - - /// Register the provider itself - provider.register( - (p) => provider, - const Service( - exposeAs: ServiceProvider, - lifetime: ServiceLifetime.singleton, - ), - ); - - /// boot the provider - provider.boot(); - - /// Run the app - runApp(MyApp(provider)); -} - -class MyApp extends StatelessWidget { - - /// Inject the provider - final ServiceProvider _provider; - - const MyApp(this._provider, {super.key}); - - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - primarySwatch: Colors.blue, - ), - // Set the initial route. - initialRoute: '/home/HelloFlutter', - // Use the RouteResolver for generating routes - onGenerateRoute: _provider - .resolve() - .resolveRoute, - ); - } -} - -``` - -## Usage - -After you set up the routing, you can register routes. - -```dart -/// Create a class that implements the RouteProvider and add the @Service annotation with a -/// #routeProvider tag. -@Service(tags: [#routeProvider]) -class HomeRouteProvider implements RouteProvider { - @override - List get routes => - [ - // Return your routes - RegisteredRoute( - path: r'/home/{name}', - // The builder should return a function that accepts a provider (ServiceProvider from above) - // and returns a WidgetBuilder. Since we have the provider, we can use DI to get the instance - // of our widget 🙌. - builder: (provider) => (ctx) => provider.resolve(), - ) - ]; -} - -/// Decorate your Widget with the Service Annotation. -/// ServiceLifetime.transient is required to create always a fresh instance of this widget. -@Service(lifetime: ServiceLifetime.transient) -class MyHomePage extends StatefulWidget { - /// RouteArguments contains the parameters and other settings of the route. - final RouteArguments arguments; - - const MyHomePage(this.arguments, {super.key}); - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - - /// Access the name variable from the route. For example "HelloFlutter" - title: Text(widget.arguments.pathVariables['name'] ?? 'Missing name'), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme - .of(context) - .textTheme - .headline4, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), - ); - } -} -``` - -To navigate between routes you can use the default `Navigator.of(context).xxxNamed` methods. - -You find a full example in the [example](./example) directory. - -## Route variables - -As you can see in the example above, you can specify variables in the route. The format is `{name}` -or `{name:REGEXP}` where REGEXP is a regular expression string. - -| Route | Valid | Invalid | Path variables (valid example) | -|----------------------|----------------------------------|---------------------------|--------------------------------| -| `/home` | `/home` | `/home/foo` | - | -| `/home/{name}` | `/home/foo`, `/home/bar` | `/home/foo/bar` | name=foo, name=bar | -| `/home/{name:.+}` | `/home/foo`, `/home/foo/bar` | `/other` | name=foo, name=foo/bar | -| `/user/{userId:\d}` | `/user/1`, `/user/2` | `/user/12`, `/user/admin` | userId=1, userId=2 | -| `/user/{userId:\d+}` | `/user/1`, `/user/2`, `/user/12` | `/user/admin` | userId=1, userId=2 | - -Query parameters are supported in all examples above. - -You can access path variables and query parameters when injecting the `RouteArguments` object. - -## Dev / Prod - -While you develop your app, you can use the following command to watch for changes and update the -routing / ServiceProvider automatically - -```bash -flutter pub run build_runner watch --delete-conflicting-outputs -``` - -When building for production you can use this command before running `flutter build`: - -```bash -flutter pub run build_runner build --delete-conflicting-outputs -``` +## Installation / Configuration +[Quick start guide](./doc/quick-start.md) ## FAQ @@ -211,7 +19,6 @@ add more routes without touching existing code. ### Is Flutter web supported? Yes, all platforms are supported. - ## Tests ```bash diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..1a0f9e3 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,5 @@ +# Docs + +Index: +- [Quick start](./quick-start.md) +- [Under the hood](./under-the-hood.md) \ No newline at end of file diff --git a/docs/images/level0.md b/doc/images/level0.md similarity index 100% rename from docs/images/level0.md rename to doc/images/level0.md diff --git a/docs/images/level0.png b/doc/images/level0.png similarity index 100% rename from docs/images/level0.png rename to doc/images/level0.png diff --git a/docs/images/level1.md b/doc/images/level1.md similarity index 100% rename from docs/images/level1.md rename to doc/images/level1.md diff --git a/docs/images/level1_full.png b/doc/images/level1_full.png similarity index 100% rename from docs/images/level1_full.png rename to doc/images/level1_full.png diff --git a/docs/images/level1_partial.png b/doc/images/level1_partial.png similarity index 100% rename from docs/images/level1_partial.png rename to doc/images/level1_partial.png diff --git a/doc/quick-start.md b/doc/quick-start.md new file mode 100644 index 0000000..5661a37 --- /dev/null +++ b/doc/quick-start.md @@ -0,0 +1,193 @@ +## Installation + +- Follow the setup steps for [catalyst_builder](https://pub.dev/packages/catalyst_builder). +- Follow the setup steps for [explorator](https://pub.dev/packages/explorator/install). + +### Update the build.yaml + +Make sure that you set includePackageDependencies to true in the build.yaml: + +```yaml +# build.yaml +targets: + $default: + auto_apply_builders: true + builders: + catalyst_builder|buildServiceProvider: + options: + providerClassName: 'DefaultServiceProvider' + includePackageDependencies: true +``` + +### Register the RouteResolver + +```dart +@GenerateServiceProvider() +// The ServiceMap is required for wiring the RouteBuilder. Replace it with your own if necessary. +@ServiceMap(services: { + MaterialRouteBuilder: Service(exposeAs: RouteBuilder), +}) +void main() { + /// Create an instance of the service provider + var provider = DefaultServiceProvider(); + + /// Register the provider itself + provider.register( + (p) => provider, + const Service( + exposeAs: ServiceProvider, + lifetime: ServiceLifetime.singleton, + ), + ); + + /// boot the provider + provider.boot(); + + /// Run the app + runApp(MyApp(provider)); +} + +class MyApp extends StatelessWidget { + + /// Inject the provider + final ServiceProvider _provider; + + const MyApp(this._provider, {super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + // Set the initial route. + initialRoute: '/home/HelloFlutter', + // Use the RouteResolver for generating routes + onGenerateRoute: _provider + .resolve() + .resolveRoute, + ); + } +} + +``` + +## Usage + +After you set up the routing, you can register routes. + +```dart +/// Create a class that implements the RouteProvider and add the @Service annotation with a +/// #routeProvider tag. +@Service(tags: [#routeProvider]) +class HomeRouteProvider implements RouteProvider { + @override + List get routes => + [ + // Return your routes + RegisteredRoute( + path: r'/home/{name}', + // The builder should return a function that accepts a provider (ServiceProvider from above) + // and returns a WidgetBuilder. Since we have the provider, we can use DI to get the instance + // of our widget 🙌. + builder: (provider) => (ctx) => provider.resolve(), + ) + ]; +} + +/// Decorate your Widget with the Service Annotation. +/// ServiceLifetime.transient is required to create always a fresh instance of this widget. +@Service(lifetime: ServiceLifetime.transient) +class MyHomePage extends StatefulWidget { + /// RouteArguments contains the parameters and other settings of the route. + final RouteArguments arguments; + + const MyHomePage(this.arguments, {super.key}); + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + int _counter = 0; + + void _incrementCounter() { + setState(() { + _counter++; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + + /// Access the name variable from the route. For example "HelloFlutter" + title: Text(widget.arguments.pathVariables['name'] ?? 'Missing name'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text( + 'You have pushed the button this many times:', + ), + Text( + '$_counter', + style: Theme + .of(context) + .textTheme + .headline4, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: const Icon(Icons.add), + ), + ); + } +} +``` + +To navigate between routes you can use the default `Navigator.of(context).xxxNamed` methods. + +You find a full example in the [example](../example) directory. + +## Route variables + +As you can see in the example above, you can specify variables in the route. The format is `{name}` +or `{name:REGEXP}` where REGEXP is a regular expression string. + +| Route | Valid | Invalid | Path variables (valid example) | +|----------------------|----------------------------------|---------------------------|--------------------------------| +| `/home` | `/home` | `/home/foo` | - | +| `/home/{name}` | `/home/foo`, `/home/bar` | `/home/foo/bar` | name=foo, name=bar | +| `/home/{name:.+}` | `/home/foo`, `/home/foo/bar` | `/other` | name=foo, name=foo/bar | +| `/user/{userId:\d}` | `/user/1`, `/user/2` | `/user/12`, `/user/admin` | userId=1, userId=2 | +| `/user/{userId:\d+}` | `/user/1`, `/user/2`, `/user/12` | `/user/admin` | userId=1, userId=2 | + +Query parameters are supported in all examples above. + +You can access path variables and query parameters when injecting the `RouteArguments` object. + +## Dev / Prod + +While you develop your app, you can use the following command to watch for changes and update the +routing / ServiceProvider automatically + +```bash +flutter pub run build_runner watch --delete-conflicting-outputs +``` + +When building for production you can use this command before running `flutter build`: + +```bash +flutter pub run build_runner build --delete-conflicting-outputs +``` + +## How does it work? +Check out the "[under the hood](./under-the-hood.md)" doc. \ No newline at end of file diff --git a/docs/under-the-hood.md b/doc/under-the-hood.md similarity index 100% rename from docs/under-the-hood.md rename to doc/under-the-hood.md