Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

release: 0.0.4-dev.8 #2

Merged
merged 9 commits into from
May 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Build
on: [push, pull_request]
jobs:
flutter_test_and_analyze:
name: Run flutter test and analyze
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: subosito/[email protected]
with:
channel: "stable"
- name: Install Dependencies
run: flutter packages get
- name: Format
run: flutter format --set-exit-if-changed lib test example
- name: Analyze
run: flutter analyze lib test example
- name: Run tests
run: flutter test --no-pub --coverage --test-randomize-ordering-seed random
- name: Check Code Coverage
uses: ChicagoFlutter/[email protected]
with:
path: packages/flutter_bloc/coverage/lcov.info
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# 0.0.4-dev.8

- docs: Improve README.md
- docs: Improve API documentation
- docs: Improve `example` comments
- fix: Issue when poping pages restored from web history
- fix: Issue when popping pagem from invalid positions
- test: Improve test coverage
- chore: Set up C.I (test, code analyze)

# 0.0.4-dev.7

- feat: initial release 🎉
136 changes: 83 additions & 53 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,42 @@
# APS Navigator
# APS Navigator - App Pagination System

[![build](https://github.com/guilherme-v/aps_navigator/actions/workflows/ci.yaml/badge.svg)](https://github.com/guilherme-v/aps_navigator/actions)
[![codecov](https://codecov.io/gh/guilherme-v/aps_navigator/branch/develop/graph/badge.svg)](https://codecov.io/gh/guilherme-v/aps_navigator)
[![style: lint](https://img.shields.io/badge/style-lint-4BC0F5.svg)](https://pub.dev/packages/lint)
[![License: MIT](https://img.shields.io/badge/license-MIT-purple.svg)](https://opensource.org/licenses/MIT)
[![pub points](https://badges.bar/aps_navigator/pub%20points)](https://pub.dev/packages/aps_navigator)
[![pub package](https://img.shields.io/pub/v/aps_navigator.svg?color=success)](https://pub.dartlang.org/packages/aps_navigator)

This library is just a wrapper around Navigator 2.0 and Router/Pages API to make their use a little easier.
This library is just a wrapper around Navigator 2.0 and Router/Pages API that tries to make their use easier:

## Basic feature set
## :wrench: Basic feature set

- Web support - back/forward buttons, URL updates, recover app state from web history.
- Gives you control of Route Stack - add/remove Pages at the Middle, add multiples Pages at once, remove a range of pages at once.
- Handles Operational System events.
- Internal Navigators.

## Overview
:rowboat: What we've tried to achieve:

- Simple API
- Easy setup
- Minimal amount of "new classes types" to learn:
- No need to extend(or implement) anything
- Web support (check the images in the following sections):
- Back/Forward buttons
- Dynamic URLs
- Static URLs
- Recover app state from web history
- Control of Route Stack:
- Add/remove Pages at a specific position
- Add multiples Pages at once
- Remove a range of pages at once
- Handles Operational System events
- Internal(Nested) Navigators

:warning: What we didn't try to achieve:

- To use code generation
- Don't get me wrong. Code generation is a fantastic technique that makes code clear and coding faster - we have great libraries that are reference in the community and use it
- The thing is: It doesn't seems natural to me have to use this kind of procedure for something "basic" as navigation
- To use Strongly-typed arguments passing

## :eyes: Overview

### 1 - Create the Navigator and define the routes:

Expand All @@ -25,7 +49,7 @@ final navigator = APSNavigator.from(
);
```

### 2 - Configure MaterialApp to use it:
### 2 - Configure MaterialApp to use it:

```dart
class MyApp extends StatelessWidget {
Expand All @@ -41,7 +65,7 @@ class MyApp extends StatelessWidget {
}
```

### 3 - Create the widget Page:
### 3 - Create the widget Page (route):

```dart
class DynamicURLPage extends StatefulWidget {
Expand All @@ -51,8 +75,7 @@ class DynamicURLPage extends StatefulWidget {
@override
_DynamicURLPageState createState() => _DynamicURLPageState();

// You don't need to use a static function as Builder,
// but it seems to be a good way to organize things
// Builder function
static Page route(RouteData data) {
final tab = data.values['tab'] == 'books' ? 0 : 1;
return MaterialPage(
Expand All @@ -63,7 +86,11 @@ class DynamicURLPage extends StatefulWidget {
}
```

### 4 - Navigate to the page:
- You don't need to use a static function as PageBuilder, but it seems to be a good way to organize things.
- Important: **AVOID** using '**const**' keyword at `MaterialPage` or `DynamicURLPage` levels, or Pop may not work correctly with Web History.
- Important: **Always** include a Key.

### 4 - Navigate to it:

```dart
APSNavigator.of(context).push(
Expand All @@ -73,10 +100,11 @@ class DynamicURLPage extends StatefulWidget {
```

- The browser's address bar will display: `/dynamic_url_example?tab=books`.
- The navigator will create the Page and put it at the top of the Route Stack.
- The following sections describe better the above steps.
- The `Page` will be created and put at the top of the Route Stack.

The following sections describe better the above steps.

## Usage
## :massage: Usage

### 1 - Creating the Navigator and defining the Routes:

Expand All @@ -94,7 +122,8 @@ final navigator = APSNavigator.from(
'/static_url_example': PageBuilder..,

// Defines the location (and queries): '/dynamic_url_example?tab=(tab_value)&other=(other_value)'
'/dynamic_url_example{?tab,other}': PageBuilder.., // Important: Notice that the '?' is only included once
// Important: Notice that the '?' is only
'/dynamic_url_example{?tab,other}': PageBuilder..,

// Defines the location (and path variables): '/posts' and '/posts/(post_id_value)'
'/posts': PageBuilder..,
Expand All @@ -109,19 +138,19 @@ final navigator = APSNavigator.from(
);
```

`Routes` is just a map between `Templates` and `Page Builders`:
`routes` is just a map between `Templates` and `Page Builders`:

- `Templates` are simple strings with predefined markers to Path ({a}) and Query({?a,b,c..}) values.
- `Page Builders` are functions that return a Page and receive a `RouteData`. Check the section 3 bellow.
- :postbox: `Templates` are simple strings with predefined markers to Path (`{a}`) and Query(`{?a,b,c..}`) values.
- :house: `Page Builders` are plain functions that return a `Page` and receive a `RouteData`. Check the section 3 bellow.

Given the configuration above, the app will open at: `/dynamic_url_example?tab=1`.

### 2 - Configure MaterialApp:

After creating a Navigator:
After creating a Navigator, we need to set it up to be used:

- Set it as `MaterialApp.router.routeDelegate`.
- Remember to also add the `MaterialApp.router.routeInformationParser`:
- :one: Set it as `MaterialApp.router.routeDelegate`.
- :two: Remember to also add the `MaterialApp.router.routeInformationParser`:

```dart
class MyApp extends StatelessWidget {
Expand All @@ -137,17 +166,17 @@ class MyApp extends StatelessWidget {
}
```

### 3 - Creating the widget Page:
### 3 - Creating the widget Page(route):

When building a route:
When building a `Page`:

- The library matches address `templates` with the current address. E.g.:
- Template: `/dynamic_url_example/{id}{?tab,other}'`
- Address: `/dynamic_url_example/10?tab=1&other=abc`
- All *paths* and *queries* values are extracted and included in a `RouteData.data` instance. E.g.:
- :one: The library tries to match the address `templates` with the current address. E.g.:
- :postbox: Template: `/dynamic_url_example/{id}{?tab,other}'`
- :house: Address: `/dynamic_url_example/10?tab=1&other=abc`
- :two: All *paths* and *queries* values are extracted and included in a `RouteData.data` instance. E.g.:
- `{'id': '10', 'tab': '1', 'other': 'abc'}`
- This istance is passed as param to the `PageBuilder` function - `static Page route(RouteData data)`...
- A new Page instance is created and included at the Route Stack - you check that easily using the dev tools.
- :three: This istance is passed as param to the `PageBuilder` function - `static Page route(RouteData data)`...
- :four: A new Page instance is created and included at the Route Stack - you check that easily using the dev tools.

```dart
class DynamicURLPage extends StatefulWidget {
Expand All @@ -169,14 +198,14 @@ class DynamicURLPage extends StatefulWidget {
}
```

### 4 - Navigating to the pages:
### 4 - Navigating to Pages:

Example Link: [All Navigating Examples](https://github.com/guilherme-v/aps_navigator/blob/develop/example/lib/pages/home_page.dart)

4.1 - To navigate to a route with queries variables:
4.1 - To navigate to a route with **queries variables**:

- Template: `/dynamic_url_example{?tab,other}`
- Address: `/dynamic_url_example?tab=books&other=abc`
- :postbox: Template: `/dynamic_url_example{?tab,other}`
- :house: Address: `/dynamic_url_example?tab=books&other=abc`

```dart
APSNavigator.of(context).push(
Expand All @@ -185,21 +214,21 @@ Example Link: [All Navigating Examples](https://github.com/guilherme-v/aps_navig
);
```

4.2 - To navigate to a route with path variables:
4.2 - To navigate to a route with **path variables**:

- Template: `/posts/{post_id}`
- Address: `/posts/10`
- :postbox: Template: `/posts/{post_id}`
- :house: Address: `/posts/10`

```dart
APSNavigator.of(context).push(
path: '/post/10', // set path values in [path]
);
```

You can also include params that aren't used as queries variables:
4.3 - You can also include params that **aren't** used as queries variables:

- Template: `/static_url_example`
- Address: `/static_url_example`
- :postbox: Template: `/static_url_example`
- :house: Address: `/static_url_example`

```dart
APSNavigator.of(context).push(
Expand All @@ -210,7 +239,7 @@ You can also include params that aren't used as queries variables:

---

## Details
## :wine_glass: Details

### 1. Dynamic URLs Example

Expand Down Expand Up @@ -244,7 +273,7 @@ When using dynamic URLs, changing the app's state also changes the browser's URL
}
```

What is important to know:
:sleepy: What is important to know:

- Current limitation: Any value used at URL must be saved as `string`.
- Don't forget to include a `Key` on the `Page` created by the `PageBuilder` to everything works properly.
Expand Down Expand Up @@ -280,7 +309,7 @@ When using static URLs, changing the app's state doesn't change the browser's UR
}
```

What is important to know:
:sleepy: What is important to know:

- Don't forget to include a `Key` on the `Page` created by the `PageBuilder` to everything works properly.

Expand All @@ -306,7 +335,7 @@ Pop returning the data:
APSNavigator.of(context).pop('Do!');
```

What is important to know:
:sleepy: What is important to know:

- Data will only be returned once.
- In case of user navigate your app and back again using the browser's history, the result will be returned at `didUpdateWidget` method as `result,` instead of `await` call.
Expand Down Expand Up @@ -345,7 +374,7 @@ Push a list of the Pages at once:

In the example above `ApsPushParam(path: '/multi_push', params: {'number': 4}),` will be the new top.

What is important to know:
:sleepy: What is important to know:

- You don't necessarily have to add at the top; you can use the `position` param to add the routes at the middle of Route Stack.
- Don't forget to include a `Key` on the `Page` created by the `PageBuilder` to everything works properly.
Expand All @@ -364,7 +393,7 @@ Remove all the Pages you want given a range:
APSNavigator.of(context).removeRange(start: 2, end: 5);
```

### 6. Internal Navigator
### 6. Internal (Nested) Navigators

Example Link: [Internal Navigator Example](https://github.com/guilherme-v/aps_navigator/blob/develop/example/lib/pages/examples/internal_navigator/internal_navigator.dart)

Expand Down Expand Up @@ -411,16 +440,17 @@ class _InternalNavigatorState extends State<InternalNavigator> {
}
```

What is important to know:
:sleepy: What is important to know:

- Current limitation: Browser's URL won't update based on internal navigator state

## Warning & Suggestions

- Although this package is already useful, it's still in the Dev stage.
- I'm not sure if creating yet another navigating library is something good - we already have a lot of confusion around it today.
- This lib is not back-compatible with the old official Navigation API - at least for now (Is it worth it?).
- Do you have any ideas or found a bug? Fell free to open an issue :)
- :construction: Although this package is already useful, it's still in the **Dev stage**.
- :stuck_out_tongue: I'm not sure if creating yet another navigating library is something good - we already have a lot of confusion around it today.
- :hankey: This lib is not back-compatible with the old official Navigation API - at least for now (Is it worth it?).
- :bug: Do you have any ideas or found a bug? Fell free to open an issue! :)
- :information_desk_person: Do you want to know the current development stage? Check the [Project's Roadmap](https://github.com/guilherme-v/aps_navigator/projects/1).

## Maintainers

Expand Down
11 changes: 9 additions & 2 deletions example/lib/pages/examples/dynamic_url_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,19 @@ class _DynamicURLPageState extends State<DynamicURLPage> {
}

@override
void didUpdateWidget(DynamicURLPage oldWidget) {
super.didUpdateWidget(oldWidget);
void didChangeDependencies() {
super.didChangeDependencies();
final previous = APSNavigator.of(context).currentConfig.values;
tabIndex = (previous['tab'] == 'books') ? 0 : 1;
}

@override
void didUpdateWidget(DynamicURLPage oldWidget) {
super.didUpdateWidget(oldWidget);
// final previous = APSNavigator.of(context).currentConfig.values;
// tabIndex = (previous['tab'] == 'books') ? 0 : 1;
}

@override
Widget build(BuildContext context) {
return Scaffold(
Expand Down
13 changes: 9 additions & 4 deletions example/lib/pages/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();

static Page route(RouteData _) {
return const MaterialPage(
key: ValueKey('Home'), // Important to include a key
static Page route(RouteData a) {
// * Important: AVOID using 'const' keyword at this level, or Pop may not work properly with Web History
// ignore: prefer_const_constructors
return MaterialPage(
// * Important: include a key, you can use 'const' here
key: const ValueKey('Home'),
// ignore: prefer_const_constructors
child: HomePage(),
);
}
Expand All @@ -23,7 +27,7 @@ class _HomePageState extends State<HomePage> {
void didUpdateWidget(HomePage oldWidget) {
super.didUpdateWidget(oldWidget);
final params = APSNavigator.of(context).currentConfig.values;
result = params['result'] as String;
result = params['result'] as String?;
if (result != null) _showSnackBar(result!);
}

Expand Down Expand Up @@ -170,6 +174,7 @@ class _HomePageState extends State<HomePage> {
}

void _showSnackBar(String message) {
// print('HERE!: $message');
WidgetsBinding.instance!.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
Expand Down
Loading