Skip to content

Commit

Permalink
Merge pull request #4 from FabianVarela/navigator2
Browse files Browse the repository at this point in the history
Implement Navigator 2.0
  • Loading branch information
FabianVarela authored May 29, 2022
2 parents 6cb86e0 + e1726aa commit c0e6de2
Show file tree
Hide file tree
Showing 14 changed files with 1,205 additions and 144 deletions.
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

![Coverage](./coverage_badge.svg?sanitize=true)

Create a To-do List Flutter app managing CRUD with Firebase, using RiverPod as state management and dependency injection
Create a To-do List Flutter app managing CRUD with Firebase, using RiverPod as state management and dependency injection.

### Getting Started (Firebase)
* To execute the app, you have to Firebase Project already created.
Expand Down Expand Up @@ -34,7 +34,25 @@ Create a To-do List Flutter app managing CRUD with Firebase, using RiverPod as s
├── finalDate (Number)
├── isCompleted (Boolean)
├── subject (String)


### Navigator 2.0

Actually the project has been implemented with **Navigator 2.0** or **Route API**.

#### Deep linking

For using deep links with flutter without any packages, review this [link](https://flutter.dev/docs/development/ui/navigation/deep-linking)

Run deep links in **iOS**, use the command below:
```bash
xcrun simctl openurl booted crudtodoapp://crudtodoexample.com/categories/{categoryId}/todo/{todoId}
```

Run deep links in **Android**, use the command below:
```bash
~/Library/Android/sdk/platform-tools/adb shell am start -a android.intent.action.VIEW \ -c android.intent.category.BROWSABLE \ -d crudtodoapp://crudtodoexample.com/categories/{categoryId}/todo/{todoId}
```

### Used packages

------
Expand Down
25 changes: 15 additions & 10 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@

<uses-permission android:name="android.permission.INTERNET"/>

<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:icon="@mipmap/ic_launcher"
android:name="${applicationName}"
Expand All @@ -19,19 +14,29 @@
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize">
<!--<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />-->

<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background" />

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

<!-- Deep Links -->
<meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Accepts URIs that begin with YOUR_SCHEME://YOUR_HOST -->
<data
android:scheme="crudtodoapp"
android:host="crudtodoexample.com" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->

<meta-data
android:name="flutterEmbedding"
android:value="2" />
Expand Down
17 changes: 17 additions & 0 deletions ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,23 @@
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<!-- Deep Links -->
<key>FlutterDeepLinkingEnabled</key>
<true/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>crudtodoexample.com</string>
<key>CFBundleURLSchemes</key>
<array>
<string>crudtodoapp</string>
</array>
</dict>
</array>
<!-- End Deep Links -->
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
Expand Down
27 changes: 19 additions & 8 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:crud_todo_app/common/adaptive_contextual_layout.dart';
import 'package:crud_todo_app/ui/category_list_view.dart';
import 'package:crud_todo_app/navigator/crud_todo_information_parser.dart';
import 'package:crud_todo_app/navigator/crud_todo_router_delegate.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
Expand All @@ -18,12 +19,20 @@ Future<void> main() async {
runApp(const ProviderScope(child: TodoListApp()));
}

class TodoListApp extends StatelessWidget {
class TodoListApp extends StatefulWidget {
const TodoListApp({Key? key}) : super(key: key);

@override
_TodoListAppState createState() => _TodoListAppState();
}

class _TodoListAppState extends State<TodoListApp> {
final _todoRouterDelegate = CrudTodoRouterDelegate();
final _todoInfoParser = CrudTodoInformationParser();

@override
Widget build(BuildContext context) {
return MaterialApp(
return MaterialApp.router(
debugShowCheckedModeBanner: false,
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
Expand All @@ -32,12 +41,14 @@ class TodoListApp extends StatelessWidget {
),
),
onGenerateTitle: (ctx) {
if (getDevice() == DeviceSegment.desktop) {
setWindowTitle('To-Do List App');
}
return 'To-Do List App';
const title = 'To-Do List App';
if (getDevice() == DeviceSegment.desktop) setWindowTitle(title);

return title;
},
home: const CategoryListView(),
routerDelegate: _todoRouterDelegate,
routeInformationParser: _todoInfoParser,
backButtonDispatcher: RootBackButtonDispatcher(),
);
}
}
83 changes: 83 additions & 0 deletions lib/navigator/config/crud_todo_config.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import 'package:equatable/equatable.dart';

class CrudTodoConfig extends Equatable {
const CrudTodoConfig.categoryList()
: currentCategoryId = null,
currentTodoId = null,
isTodoNew = false,
isTodoUpdate = false,
isUnknown = false;

const CrudTodoConfig.todoList({String? categoryId})
: currentCategoryId = categoryId,
currentTodoId = null,
isTodoNew = false,
isTodoUpdate = false,
isUnknown = false;

const CrudTodoConfig.addTodo({String? categoryId})
: currentCategoryId = categoryId,
currentTodoId = null,
isTodoNew = true,
isTodoUpdate = false,
isUnknown = false;

const CrudTodoConfig.updateTodo({String? categoryId, String? todoId})
: currentCategoryId = categoryId,
currentTodoId = todoId,
isTodoNew = false,
isTodoUpdate = true,
isUnknown = false;

const CrudTodoConfig.unknown()
: currentCategoryId = null,
currentTodoId = null,
isTodoNew = false,
isTodoUpdate = false,
isUnknown = true;

final String? currentCategoryId;
final String? currentTodoId;
final bool isTodoNew;
final bool isTodoUpdate;
final bool isUnknown;

bool get isPageUnknown => isUnknown;

bool get isCategoryListPage =>
currentCategoryId == null &&
currentTodoId == null &&
!isTodoNew &&
!isTodoUpdate &&
!isUnknown;

bool get isTodoListPage =>
currentCategoryId != null &&
currentTodoId == null &&
!isTodoNew &&
!isTodoUpdate &&
!isUnknown;

bool get isAddTodoPage =>
currentCategoryId != null &&
currentTodoId == null &&
isTodoNew &&
!isTodoUpdate &&
!isUnknown;

bool get isUpdateTodoPage =>
currentCategoryId != null &&
currentTodoId != null &&
!isTodoNew &&
isTodoUpdate &&
!isUnknown;

@override
List<Object?> get props => [
currentCategoryId,
currentTodoId,
isTodoNew,
isTodoUpdate,
isUnknown,
];
}
107 changes: 107 additions & 0 deletions lib/navigator/crud_todo_information_parser.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import 'package:crud_todo_app/navigator/config/crud_todo_config.dart';
import 'package:flutter/material.dart';

class CrudTodoPath {
static const category = 'categories';
static const todo = 'todo';
static const unknown = '404';
}

class CrudTodoInformationParser extends RouteInformationParser<CrudTodoConfig> {
@override
Future<CrudTodoConfig> parseRouteInformation(
RouteInformation routeInformation,
) async {
final uri = Uri.parse(routeInformation.location ?? '');

if (uri.pathSegments.isEmpty) {
// Home '/'
return const CrudTodoConfig.categoryList();
} else if (uri.pathSegments.length == 1) {
// Home '/categories'
final firstSegment = uri.pathSegments[0].toLowerCase();
if (firstSegment == CrudTodoPath.category) {
return const CrudTodoConfig.categoryList();
}
} else if (uri.pathSegments.length == 2) {
// Category detail '/categories/{id}'
final firstSegment = uri.pathSegments[0].toLowerCase();
final secondSegment = uri.pathSegments[1];

if (firstSegment == CrudTodoPath.category) {
if (secondSegment.isNotEmpty) {
return CrudTodoConfig.todoList(categoryId: secondSegment);
}
}
} else if (uri.pathSegments.length == 3) {
// Todos new '/categories/{id}/todos/
final firstSegment = uri.pathSegments[0].toLowerCase();
final secondSegment = uri.pathSegments[1];
final thirdSegment = uri.pathSegments[2].toLowerCase();

if (firstSegment == CrudTodoPath.category) {
if (secondSegment.isNotEmpty) {
if (thirdSegment == CrudTodoPath.todo) {
return CrudTodoConfig.addTodo(categoryId: secondSegment);
}
}
}
} else if (uri.pathSegments.length == 4) {
// Todos update '/categories/{catId}/todos/{todoId}
final firstSegment = uri.pathSegments[0].toLowerCase();
final secondSegment = uri.pathSegments[1];
final thirdSegment = uri.pathSegments[2].toLowerCase();
final lastSegment = uri.pathSegments[3];

if (firstSegment == CrudTodoPath.category) {
if (secondSegment.isNotEmpty) {
if (thirdSegment == CrudTodoPath.todo) {
if (lastSegment.isNotEmpty) {
return CrudTodoConfig.updateTodo(
categoryId: secondSegment,
todoId: lastSegment,
);
}
}
}
}
}

return const CrudTodoConfig.unknown();
}

@override
RouteInformation? restoreRouteInformation(CrudTodoConfig configuration) {
if (configuration.isUnknown) {
return const RouteInformation(location: '/${CrudTodoPath.unknown}');
} else if (configuration.isCategoryListPage) {
const categoryPath = CrudTodoPath.category;

return const RouteInformation(location: '/$categoryPath');
} else if (configuration.isTodoListPage) {
const categoryPath = CrudTodoPath.category;
final currentCategoryId = configuration.currentCategoryId;

return RouteInformation(location: '/$categoryPath/$currentCategoryId');
} else if (configuration.isAddTodoPage) {
const categoryPath = CrudTodoPath.category;
final currentCategoryId = configuration.currentCategoryId;
const todoPath = CrudTodoPath.todo;

return RouteInformation(
location: '/$categoryPath/$currentCategoryId/$todoPath/',
);
} else if (configuration.isUpdateTodoPage) {
const categoryPath = CrudTodoPath.category;
final currentCategoryId = configuration.currentCategoryId;
const todoPath = CrudTodoPath.todo;
final currentTodoId = configuration.currentTodoId;

return RouteInformation(
location: '/$categoryPath/$currentCategoryId/$todoPath/$currentTodoId',
);
}

return null;
}
}
Loading

0 comments on commit c0e6de2

Please sign in to comment.