浏览代码

Refactor route Handlers (#6)

- Removes the RouteHandler typedef and introduces the Handler class as a replacement
- Router can now handle functions on top of Route definitions
Luke 8 年之前
父节点
当前提交
b767b5ca0b

+ 24 - 4
README.md

@@ -3,24 +3,44 @@
 
 The brightest, hippest, coolest router for Flutter.
 
-[![Version](https://img.shields.io/badge/version-1.0.0-blue.svg)](https://pub.dartlang.org/packages/fluro)
+[![Version](https://img.shields.io/badge/version-1.1.0-blue.svg)](https://pub.dartlang.org/packages/fluro)
 [![Build Status](https://travis-ci.org/goposse/fluro.svg?branch=master)](https://travis-ci.org/goposse/fluro)
 [![Coverage](https://codecov.io/gh/goposse/fluro/branch/master/graph/badge.svg)](https://codecov.io/gh/goposse/fluro)
 
 ## Features
 
 - Simple route navigation
+- Function handlers (map to a function instead of a route)
 - Wildcard parameter matching
 - Querystring parameter parsing
 - Common transitions built-in
 - Simple custom transition creation
 
+## Version compatability
+
+In general we try not to introduce breaking changes but version 1.1 did introduce a
+breaking change in order to support function handlers. As such, you will need to
+change all of your route handler definitions to use the new `Handler` class. The
+`RouteHandler` definition has now been removed.
+
+Swapping out the handlers should be as simple as changing:
+
+```dart
+RouteHandler usersHandler = (Map<String, String> params) {}
+```
+
+to
+
+```dart
+var usersHandler = new Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) {});
+```
+
 ## Getting started
 
 You should ensure that you add the router as a dependency in your flutter project.
 ```yaml
 dependencies:
- fluro: "^1.0.0"
+ fluro: "^1.1.0"
 ```
 
 You can also reference the git repo directly if you want:
@@ -48,9 +68,9 @@ you can access the router in other areas in your application.
 
 After instantiating the router, you will need to define your routes and your route handlers:
 ```dart
-var usersHandler = (Map<String, String> params) {
+var usersHandler = new Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) {
   return new UsersScreen(params["id"]);
-};
+});
 
 void defineRoutes(Router router) {
   router.define("/users/:id", handler: usersHandler);

+ 7 - 0
example/android/app/build.gradle

@@ -13,6 +13,7 @@ if (flutterRoot == null) {
 }
 
 apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
 
 flutter {
@@ -100,4 +101,10 @@ dependencies {
     androidTestCompile "com.android.support:support-annotations:$support_lib_ver"
     androidTestCompile 'com.android.support.test:runner:0.5'
     androidTestCompile 'com.android.support.test:rules:0.5'
+	compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
+}
+repositories {
+	maven {
+		url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1'
+	}
 }

+ 1 - 3
example/android/app/src/main/AndroidManifest.xml

@@ -23,9 +23,7 @@
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.BROWSABLE" />
-                <data
-                    android:host="link"
-                    android:scheme="woomera" />
+                <data android:host="link" android:scheme="fluro" />
             </intent-filter>
         </activity>
     </application>

+ 0 - 6
example/android/app/src/main/java/com/goposse/routersample/App.java

@@ -1,6 +0,0 @@
-package com.goposse.routersample;
-
-import io.flutter.app.FlutterApplication;
-
-public class App extends FlutterApplication {
-}

+ 5 - 0
example/android/app/src/main/java/com/goposse/routersample/App.kt

@@ -0,0 +1,5 @@
+package com.goposse.routersample
+
+import io.flutter.app.FlutterApplication
+
+class App : FlutterApplication()

+ 0 - 50
example/android/app/src/main/java/com/goposse/routersample/activities/MainActivity.java

@@ -1,50 +0,0 @@
-package com.goposse.routersample.activities;
-
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-
-import com.goposse.routersample.constants.Channels;
-
-import io.flutter.app.FlutterActivity;
-import io.flutter.plugin.common.MethodChannel;
-import io.flutter.plugins.PluginRegistry;
-
-public class MainActivity extends FlutterActivity {
-
-    private static final String LOG_TAG = "A:Main";
-
-    PluginRegistry pluginRegistry;
-    private static MethodChannel deepLinkChannel;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        pluginRegistry = new PluginRegistry();
-        pluginRegistry.registerAll(this);
-
-        if (deepLinkChannel == null) {
-            deepLinkChannel = new MethodChannel(getFlutterView(), Channels.DEEP_LINK_RECEIVED);
-        }
-
-        Intent intent = getIntent();
-        checkForLinkEvent(intent);
-    }
-
-    private void checkForLinkEvent(Intent intent) {
-        String action = intent.getAction();
-        Log.d(LOG_TAG, "Hey!!! " + action);
-        if (action.equals(Intent.ACTION_VIEW)) {
-            Uri data = intent.getData();
-            if (data != null) {
-                String path = data.getQueryParameter("path");
-                if (path != null) {
-                    Log.d(LOG_TAG, String.format("Received external link: %s", data.toString()));
-                    deepLinkChannel.invokeMethod("linkReceived", path);
-                }
-            }
-        }
-    }
-
-}

+ 44 - 0
example/android/app/src/main/java/com/goposse/routersample/activities/MainActivity.kt

@@ -0,0 +1,44 @@
+package com.goposse.routersample.activities
+
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+
+import com.goposse.routersample.constants.Channels
+
+import io.flutter.app.FlutterActivity
+import io.flutter.plugin.common.MethodChannel
+import io.flutter.plugins.GeneratedPluginRegistrant
+
+class MainActivity : FlutterActivity() {
+
+	private val LOG_TAG = "A:Main"
+	private var deepLinkChannel: MethodChannel? = null
+
+	override fun onCreate(savedInstanceState: Bundle?) {
+		super.onCreate(savedInstanceState)
+		GeneratedPluginRegistrant.registerWith(this)
+		deepLinkChannel = MethodChannel(flutterView, Channels.DEEP_LINK_RECEIVED)
+	}
+
+	override fun onResume() {
+		super.onResume()
+		checkForLinkEvent(intent)
+	}
+
+	private fun checkForLinkEvent(intent: Intent) {
+		if (intent.action == Intent.ACTION_VIEW && intent.data != null) {
+			val path = intent.data.getQueryParameter("path")
+			val query = intent.data.getQueryParameter("query")
+			if (path != null) {
+				val passedObjs = mutableMapOf<String, Any>("path" to path)
+				if (query != null) {
+					passedObjs["query"] = query
+				}
+				deepLinkChannel?.invokeMethod("linkReceived", passedObjs)
+				Log.d(LOG_TAG, "Sent message to flutter: linkReceived=$path")
+			}
+		}
+	}
+}

+ 0 - 6
example/android/app/src/main/java/com/goposse/routersample/constants/Channels.java

@@ -1,6 +0,0 @@
-package com.goposse.routersample.constants;
-
-public class Channels {
-    private static final String CHANNEL_PREFIX = "channel:com.goposse.routerdemo";
-    public static final String DEEP_LINK_RECEIVED = CHANNEL_PREFIX + "/deeplink";
-}

+ 6 - 0
example/android/app/src/main/java/com/goposse/routersample/constants/Channels.kt

@@ -0,0 +1,6 @@
+package com.goposse.routersample.constants
+
+object Channels {
+	private val CHANNEL_PREFIX = "channel:com.goposse.routersample"
+	val DEEP_LINK_RECEIVED = CHANNEL_PREFIX + "/deeplink"
+}

+ 8 - 3
example/android/build.gradle

@@ -1,11 +1,16 @@
 buildscript {
-    repositories {
+	ext.kotlin_version = '1.1.3-eap-34'
+	repositories {
         jcenter()
-    }
+		maven {
+			url 'http://dl.bintray.com/kotlin/kotlin-eap-1.1'
+		}
+	}
 
     dependencies {
         classpath 'com.android.tools.build:gradle:2.4.0-alpha7'
-    }
+		classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+	}
 }
 
 allprojects {

二进制
example/assets/fonts/Lazer84.ttf


+ 0 - 49
example/lib/app.dart

@@ -1,49 +0,0 @@
-/*
- * fluro
- * A Posse Production
- * http://goposse.com
- * Copyright (c) 2017 Posse Productions LLC. All rights reserved.
- * See LICENSE for distribution and usage details.
- */
-import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
-import 'package:router/fluro.dart';
-import 'package:router_example/config/application.dart';
-import 'package:router_example/config/route_handlers.dart';
-import 'package:router_example/screens/home_screen.dart';
-
-class App extends StatelessWidget {
-
-  static const platform = const MethodChannel('channel:com.goposse.routerdemo/deeplink');
-
-  App() {
-    Router router = new Router();
-    router.define("/demo", handler: showDemoHandler);
-    Application.router = router;
-    configureDeepLinker();
-  }
-  
-  @override
-  Widget build(BuildContext context) {
-    return new MaterialApp(
-      title: 'Flutter Demo',
-      theme: new ThemeData(
-        primarySwatch: Colors.blue,
-      ),
-      home: new HomeScreen(),
-    );
-  }
-
-  void configureDeepLinker() {
-    platform.setMethodCallHandler((MethodCall call) async {
-      if (call.method == "linkReceived") {
-        String path = call.arguments;
-        if (path != null) {
-          print("got path: $path");
-        }
-      }
-    });
-  }
-
-}
-

+ 59 - 0
example/lib/components/app/app_component.dart

@@ -0,0 +1,59 @@
+/*
+ * fluro
+ * A Posse Production
+ * http://goposse.com
+ * Copyright (c) 2017 Posse Productions LLC. All rights reserved.
+ * See LICENSE for distribution and usage details.
+ */
+import '../../config/application.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:fluro/fluro.dart';
+import '../home/home_component.dart';
+import 'package:router_example/config/routes.dart';
+
+class AppComponent extends StatefulWidget {
+
+  @override
+  State createState() {
+    return new AppComponentState();
+  }
+}
+
+class AppComponentState extends State<AppComponent> {
+
+  static MethodChannel platform = const MethodChannel('channel:com.goposse.routersample/deeplink');
+
+  AppComponentState() {
+    Router router = new Router();
+    Routes.configureRoutes(router);
+    Application.router = router;
+    configureDeepLinker();
+    print("Configured channel receiver in flutter ..");
+  }
+
+
+  void configureDeepLinker() {
+    platform.setMethodCallHandler((MethodCall call) async {
+      if (call.method == "linkReceived") {
+        Map<String, dynamic> passedObjs = call.arguments;
+        if (passedObjs != null) {
+          var path = passedObjs["path"];
+          Application.router.navigateTo(context, path);
+        }
+      }
+    });
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return new MaterialApp(
+      title: 'Flutter Demo',
+      theme: new ThemeData(
+        primarySwatch: Colors.blue,
+      ),
+      home: new HomeComponent(),
+    );
+  }
+}
+

+ 33 - 0
example/lib/components/demo/demo_message_component.dart

@@ -0,0 +1,33 @@
+/*
+ * fluro
+ * A Posse Production
+ * http://goposse.com
+ * Copyright (c) 2017 Posse Productions LLC. All rights reserved.
+ * See LICENSE for distribution and usage details.
+ */
+
+import 'package:flutter/material.dart';
+import 'package:meta/meta.dart';
+
+class DemoMessageComponent extends StatelessWidget {
+
+  DemoMessageComponent({@required this.message, this.color = const Color(0xFFFFFFFF)});
+
+  final String message;
+  final Color color;
+
+  @override
+  Widget build(BuildContext context) {
+    return new Material(
+      color: this.color,
+      child: new Center(
+        child: new Text(
+          message,
+          style: new TextStyle(
+            fontFamily: "Lazer84",
+          ),
+        )
+      ),
+    );
+  }
+}

+ 3 - 3
example/lib/screens/test_screen_01.dart → example/lib/components/demo/demo_simple_component.dart

@@ -5,12 +5,12 @@
  * Copyright (c) 2017 Posse Productions LLC. All rights reserved.
  * See LICENSE for distribution and usage details.
  */
+import '../../helpers/color_helpers.dart';
 import 'package:flutter/material.dart';
-import 'package:router_example/helpers/color_helpers.dart';
 
-class TestScreen01 extends StatelessWidget {
+class DemoSimpleComponent extends StatelessWidget {
 
-  TestScreen01({String message = "Testing", Color color = const Color(0xFFFFFFFF)})
+  DemoSimpleComponent({String message = "Testing", Color color = const Color(0xFFFFFFFF)})
       : this.message = message,
         this.color = color;
 

+ 123 - 0
example/lib/components/home/home_component.dart

@@ -0,0 +1,123 @@
+/*
+ * fluro
+ * A Posse Production
+ * http://goposse.com
+ * Copyright (c) 2017 Posse Productions LLC. All rights reserved.
+ * See LICENSE for distribution and usage details.
+ */
+
+import '../../config/application.dart';
+import 'package:fluro/fluro.dart';
+import 'package:flutter/material.dart';
+
+class HomeComponent extends StatelessWidget {
+  @override
+  Widget build(BuildContext context) {
+    var menuWidgets = <Widget>[
+      new Padding(
+        padding: new EdgeInsets.only(bottom: 25.0),
+        child: new Image(image: new AssetImage("assets/images/logo_fluro.png"), width: 200.0),
+      ),
+      menuButton(context, "Native Animation", "native"),
+      menuButton(context, "Preset (In from Left)", "preset-from-left"),
+      menuButton(context, "Preset (Fade In)", "preset-fade"),
+      menuButton(context, "Custom Transition", "custom"),
+      menuButton(context, "Function Call", "function-call"),
+      new Padding(
+        padding: new EdgeInsets.only(top: 65.0, left: 60.0, right: 60.0),
+        child: new Center(
+          child: new ConstrainedBox(
+            constraints: new BoxConstraints.tightFor(height: 50.0),
+            child: new FlatButton(
+              onPressed: () {
+
+              },
+              child: new Text(
+                "Try going to fluro://deeplink?path=/message&text=fluro%20rocks%21%21",
+                textAlign: TextAlign.center,
+                style: new TextStyle(
+                  fontSize: 10.0,
+                  color: const Color(0xFFFFFFFF),
+                ),
+              ),
+            ),
+          ),
+        ),
+      ),
+    ];
+
+    return new Material(
+      color: const Color(0xFF00D6F7),
+      child: new Column(
+        mainAxisAlignment: MainAxisAlignment.center,
+        children: menuWidgets,
+      ),
+    );
+  }
+
+  // helpers
+  Widget menuButton(BuildContext context, String title, String key) {
+    return new Padding(
+      padding: new EdgeInsets.all(4.0),
+      child: new ConstrainedBox(
+        constraints: new BoxConstraints(minHeight: 32.0),
+        child: new FlatButton(
+          child: new Text(
+            title,
+            style: new TextStyle(
+              color: const Color(0xFF004F8F),
+            ),
+          ),
+          onPressed: () {
+            tappedMenuButton(context, key);
+          },
+        ),
+      ),
+    );
+  }
+
+  // actions
+  void tappedMenuButton(BuildContext context, String key) {
+    String message = "";
+    String hexCode = "#FFFFFF";
+    TransitionType transitionType = TransitionType.native;
+    if (key != "custom" && key != "function-call") {
+      if (key == "native") {
+        hexCode = "#F76F00";
+        message = "This screen should have appeared using the default flutter animation for the current OS";
+      } else if (key == "preset-from-left") {
+        hexCode = "#5BF700";
+        message = "This screen should have appeared with a slide in from left transition";
+        transitionType = TransitionType.inFromLeft;
+      } else if (key == "preset-fade") {
+        hexCode = "#F700D2";
+        message = "This screen should have appeared with a fade in transition";
+        transitionType = TransitionType.fadeIn;
+      }
+      Application.router.navigateTo(context, "/demo?message=$message&color_hex=$hexCode", transition: transitionType);
+    } else if (key == "custom") {
+      hexCode = "#DFF700";
+      message = "This screen should have appeared with a crazy custom transition";
+      var transition =
+          (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
+        return new ScaleTransition(
+          scale: animation,
+          child: new RotationTransition(
+            turns: animation,
+            child: child,
+          ),
+        );
+      };
+      Application.router.navigateTo(
+        context,
+        "/demo?message=$message&color_hex=$hexCode",
+        transition: TransitionType.custom,
+        transitionBuilder: transition,
+        transitionDuration: const Duration(milliseconds: 600),
+      );
+    } else {
+      message = "You tapped the function button!";
+      Application.router.navigateTo(context, "/demo/func?message=$message");
+    }
+  }
+}

+ 2 - 1
example/lib/config/application.dart

@@ -5,7 +5,8 @@
  * Copyright (c) 2017 Posse Productions LLC. All rights reserved.
  * See LICENSE for distribution and usage details.
  */
-import 'package:router/fluro.dart';
+
+import 'package:fluro/fluro.dart';
 
 class Application {
   static Router router;

+ 41 - 6
example/lib/config/route_handlers.dart

@@ -5,17 +5,52 @@
  * Copyright (c) 2017 Posse Productions LLC. All rights reserved.
  * See LICENSE for distribution and usage details.
  */
+import '../helpers/color_helpers.dart';
+import '../components/demo/demo_simple_component.dart';
 import 'package:flutter/painting.dart';
-import 'package:router/fluro.dart';
-import 'package:router_example/helpers/color_helpers.dart';
-import 'package:router_example/screens/test_screen_01.dart';
+import 'package:fluro/fluro.dart';
+import 'package:flutter/material.dart';
 
-RouteHandler showDemoHandler = (Map<String, String> params) {
+var demoRouteHandler = new Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) {
   String message = params["message"];
   String colorHex = params["color_hex"];
   Color color = new Color(0xFFFFFFFF);
   if (colorHex != null && colorHex.length > 0) {
     color = new Color(ColorHelpers.fromHexString(colorHex));
   }
-  return new TestScreen01(message: message, color: color);
-};
+  return new DemoSimpleComponent(message: message, color: color);
+});
+
+var demoFunctionHandler = new Handler(type: HandlerType.function,
+    handlerFunc: (BuildContext context, Map<String, dynamic> params)
+{
+      String message = params["message"];
+      showDialog(context: context,
+        child: new AlertDialog(
+          title: new Text(
+            "Hey Hey!",
+            style: new TextStyle(
+              color: const Color(0xFF00D6F7),
+              fontFamily: "Lazer84",
+              fontSize: 22.0,
+            ),
+          ),
+          content: new Text("$message"),
+          actions: <Widget>[
+            new Padding(
+              padding: new EdgeInsets.only(bottom: 8.0, right: 8.0),
+              child: new FlatButton(
+                onPressed: () {
+                  Navigator.of(context).pop(true);
+                },
+                child: new Text("OK"),
+              ),
+            ),
+          ],
+        ),
+      );
+    });
+
+var deepLinkHandler = new Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) {
+
+});

+ 16 - 0
example/lib/config/routes.dart

@@ -0,0 +1,16 @@
+import 'package:fluro/fluro.dart';
+import './route_handlers.dart';
+
+class Routes {
+
+  static String demoSimple = "/demo";
+  static String demoFunc = "/demo/func";
+  static String deepLink = "/message";
+
+  static void configureRoutes(Router router) {
+    router.define(demoSimple, handler: demoRouteHandler);
+    router.define(demoFunc, handler: demoFunctionHandler);
+    router.define(deepLink, handler: deepLinkHandler);
+  }
+
+}

+ 2 - 2
example/lib/main.dart

@@ -5,9 +5,9 @@
  * Copyright (c) 2017 Posse Productions LLC. All rights reserved.
  * See LICENSE for distribution and usage details.
  */
+import 'components/app/app_component.dart';
 import 'package:flutter/material.dart';
-import 'package:router_example/app.dart';
 
 void main() {
-  runApp(new App());
+  runApp(new AppComponent());
 }

+ 0 - 98
example/lib/screens/home_screen.dart

@@ -1,98 +0,0 @@
-/*
- * fluro
- * A Posse Production
- * http://goposse.com
- * Copyright (c) 2017 Posse Productions LLC. All rights reserved.
- * See LICENSE for distribution and usage details.
- */
-import 'package:flutter/material.dart';
-import 'package:router/fluro.dart';
-import 'package:router_example/config/application.dart';
-
-class HomeScreen extends StatelessWidget {
-
-  BuildContext context;
-
-  @override
-  Widget build(BuildContext context) {
-    this.context = context;
-    var menuWidgets = <Widget>[
-      new Padding(
-        padding: new EdgeInsets.only(bottom: 15.0),
-        child: new Image(image: new AssetImage("assets/images/logo_fluro.png"), width: 200.0),
-      ),
-      menuButton("Native Animation", "native"),
-      menuButton("Preset (In from Left)", "preset-from-left"),
-      menuButton("Preset (Fade In)", "preset-fade"),
-      menuButton("Custom", "custom"),
-    ];
-    return new Material(
-      color: new Color(0xFF00D6F7),
-      child: new Column(
-        mainAxisAlignment: MainAxisAlignment.center,
-        children: menuWidgets,
-      ),
-    );
-  }
-
-  // helpers
-  Widget menuButton(String title, String key) {
-    return new Padding(
-      padding: new EdgeInsets.all(4.0),
-      child: new ConstrainedBox(
-        constraints: new BoxConstraints(minHeight: 42.0),
-        child: new FlatButton(
-          child: new Text(
-            title,
-            style: new TextStyle(
-              color: new Color(0xFF004F8F),
-            ),
-          ),
-          onPressed: () {
-            tappedMenuButton(key);
-          },
-        ),
-      ),
-    );
-  }
-
-  // actions
-  void tappedMenuButton(String key) {
-    String message = "";
-    String hexCode = "#FFFFFF";
-    TransitionType transitionType = TransitionType.native;
-    if (key != "custom") {
-      if (key == "native") {
-        hexCode = "#F76F00";
-        message = "This screen should have appeared using the default flutter animation for the current OS";
-      } else if (key == "preset-from-left") {
-        hexCode = "#5BF700";
-        message = "This screen should have appeared with a slide in from left transition";
-        transitionType = TransitionType.inFromLeft;
-      } else if (key == "preset-fade") {
-        hexCode = "#F700D2";
-        message = "This screen should have appeared with a fade in transition";
-        transitionType = TransitionType.fadeIn;
-      }
-      Application.router.navigateTo(this.context, "/demo?message=$message&color_hex=$hexCode",
-          transition: transitionType);
-    } else {
-      hexCode = "#DFF700";
-      message = "This screen should have appeared with a crazy custom transition";
-      var transition = (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation,
-          Widget child) {
-        return new ScaleTransition(
-          scale: animation,
-          child: new RotationTransition(
-            turns: animation,
-            child: child,
-          ),
-        );
-      };
-      Application.router.navigateTo(this.context, "/demo?message=$message&color_hex=$hexCode",
-        transition: TransitionType.fadeIn, transitionBuilder: transition,
-        transitionDuration: const Duration(milliseconds: 600),
-      );
-    }
-  }
-}

+ 6 - 2
example/pubspec.yaml

@@ -4,10 +4,14 @@ description: An example project for the flutter router.
 dependencies:
   flutter:
     sdk: flutter
-  router:
+  fluro:
     path: ../
 flutter:
   uses-material-design: false
   assets:
     - assets/images/logo_fluro.png
-    - assets/images/acc_boom.png
+    - assets/images/acc_boom.png
+  fonts:
+    - family: Lazer84
+      fonts:
+        - asset: assets/fonts/Lazer84.ttf

+ 34 - 3
lib/src/common.dart

@@ -8,14 +8,45 @@
 part of fluro;
 
 ///
-typedef Route<Null> RouteCreator(RouteSettings route, Map<String, String> parameters);
+enum HandlerType {
+  route,
+  function,
+}
 
 ///
-typedef Widget RouteHandler(Map<String, String> parameters);
+class Handler {
+  Handler({this.type = HandlerType.route, this.handlerFunc});
+  final HandlerType type;
+  final HandlerFunc handlerFunc;
+}
+
+///
+typedef Route<Null> RouteCreator(RouteSettings route, Map<String, dynamic> parameters);
+
+///
+typedef Widget HandlerFunc(BuildContext context, Map<String, dynamic> parameters);
 
 ///
 class AppRoute {
   String route;
-  RouteHandler handler;
+  dynamic handler;
   AppRoute(this.route, this.handler);
+}
+
+enum RouteMatchType {
+  visual,
+  nonVisual,
+  noMatch,
+}
+
+///
+class RouteMatch {
+  RouteMatch({
+    @required this.matchType = RouteMatchType.noMatch,
+    this.route = null,
+    this.errorMessage = "Unable to match route. Please check the logs."
+  });
+  final Route<Null> route;
+  final RouteMatchType matchType;
+  final String errorMessage;
 }

+ 26 - 13
lib/src/router.dart

@@ -22,11 +22,11 @@ class Router {
   RouteTree _routeTree = new RouteTree();
 
   /// Generic handler for when a route has not been defined
-  RouteHandler notFoundHandler;
+  Handler notFoundHandler;
 
   /// Creates a [PageRoute] definition for the passed [RouteHandler]. You can optionally provide a custom
   /// transition builder for the route.
-  void define(String routePath, {@required RouteHandler handler}) {
+  void define(String routePath, {@required Handler handler}) {
     _routeTree.addRoute(new AppRoute(routePath, handler));
   }
 
@@ -41,8 +41,12 @@ class Router {
     Duration transitionDuration = const Duration(milliseconds: 250),
     RouteTransitionsBuilder transitionBuilder})
   {
-    Route<Null> route = matchRoute(path, transitionType: transition,
+    RouteMatch routeMatch = matchRoute(context, path, transitionType: transition,
         transitionsBuilder: transitionBuilder, transitionDuration: transitionDuration);
+    Route<Null> route = routeMatch.route;
+    if (routeMatch.matchType == RouteMatchType.nonVisual) {
+      return;
+    }
     if (route == null && notFoundHandler != null) {
       route = _notFoundRoute(context, path);
     }
@@ -55,16 +59,16 @@ class Router {
 
   ///
   Route<Null> _notFoundRoute(BuildContext context, String path) {
-    RouteCreator creator = (RouteSettings routeSettings, Map<String, String> params) {
+    RouteCreator creator = (RouteSettings routeSettings, Map<String, dynamic> parameters) {
       return new MaterialPageRoute<Null>(settings: routeSettings, builder: (BuildContext context) {
-        return notFoundHandler(params);
+        return notFoundHandler.handlerFunc(context, parameters);
       });
     };
     return creator(new RouteSettings(name: path), null);
   }
 
   ///
-  Route<Null> matchRoute(String path, {RouteSettings routeSettings = null,
+  RouteMatch matchRoute(BuildContext buildContext, String path, {RouteSettings routeSettings = null,
     TransitionType transitionType, Duration transitionDuration = const Duration(milliseconds: 250),
     RouteTransitionsBuilder transitionsBuilder})
   {
@@ -74,17 +78,22 @@ class Router {
     }
     AppRouteMatch match = _routeTree.matchRoute(path);
     AppRoute route = match?.route;
+    Handler handler = (route != null ? route.handler : notFoundHandler);
     if (route == null && notFoundHandler == null) {
-      return null;
+      return new RouteMatch(matchType: RouteMatchType.noMatch, errorMessage: "No matching route was found");
     }
-    RouteHandler handler = (route != null ? route.handler : notFoundHandler);
     Map<String, String> parameters = match?.parameters ?? <String, String>{};
-    RouteCreator creator = (RouteSettings routeSettings, Map<String, String> params) {
+    if (handler.type == HandlerType.function) {
+      handler.handlerFunc(buildContext, parameters);
+      return new RouteMatch(matchType: RouteMatchType.nonVisual);
+    }
+
+    RouteCreator creator = (RouteSettings routeSettings, Map<String, dynamic> parameters) {
       bool isNativeTransition = (transitionType == TransitionType.native || transitionType == TransitionType.nativeModal);
       if (isNativeTransition) {
         return new MaterialPageRoute<Null>(settings: routeSettings, fullscreenDialog: transitionType == TransitionType.nativeModal,
             builder: (BuildContext context) {
-              return handler(params);
+              return handler.handlerFunc(context, parameters);
             });
       } else {
         var routeTransitionsBuilder;
@@ -95,14 +104,17 @@ class Router {
         }
         return new PageRouteBuilder<Null>(settings: routeSettings,
           pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
-            return handler(params);
+            return handler.handlerFunc(context, parameters);
           },
           transitionDuration: transitionDuration,
           transitionsBuilder: routeTransitionsBuilder,
         );
       }
     };
-    return creator(settingsToUse, parameters);
+    return new RouteMatch(
+      matchType: RouteMatchType.visual,
+      route: creator(settingsToUse, parameters),
+    );
   }
 
   RouteTransitionsBuilder _standardTransitionsBuilder(TransitionType transitionType) {
@@ -135,7 +147,8 @@ class Router {
   /// if any defined handler is found. It can also be used with the [MaterialApp.onGenerateRoute]
   /// property as callback to create routes that can be used with the [Navigator] class.
   Route<Null> generator(RouteSettings routeSettings) {
-    return matchRoute(routeSettings.name, routeSettings: routeSettings);
+    RouteMatch match = matchRoute(null, routeSettings.name, routeSettings: routeSettings);
+    return match.route;
   }
 
   /// Prints the route tree so you can analyze it.