Flutter web url navigation - flutter

I would like to know how can I navigate to a URL in my Flutter web app.
Currently am using the Navigator.of(context).push(MaterialPageRoute(...)); and I only get localhost:5354/#/ in the address bar.
Also I would like to know how I can I navigate to a particular URL directly by just pasting the URL into the browser's addresses bar.

You need to use named routes instead of directly using classes to routes.
You can use this package named fluro https://pub.dev/packages/fluro
or else you can use default navigation that flutter provides.
with fluro you can do something like this
main.dart
import '../routes/routes.dart';
void main() {
FluroRouter.setupRouter();
// run app
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
onGenerateRoute: FluroRouter.router.generator,
);
}
}
routes.dart
import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
class FluroRouter {
static Router router = Router();
static Handler _storyhandler = Handler(
handlerFunc: (BuildContext context, Map<String, dynamic> params) =>
HomeView(id: params['id'][0]));
static Handler _homehandler = Handler(
handlerFunc: (BuildContext context, Map<String, dynamic> params) =>
Home());
static void setupRouter() {
router.define(
'/',
handler: _homehandler,
);
router.define(
'/story/:id',
handler: _storyhandler,
);
}
}
you can also define routes with query parameters.
Hope this helps!

you must use of Navigator v2 for Web.
see more info: here and here

Related

Flutter on web, using query params in url redirects to inital route

I'm having a pretty weird issue with Flutter when using it for a web page. I need to process a query param from an external source, which hits my website with e.g /page?param=1
I have a super simple flutter project setup:
import 'package:client/screens/screens.dart';
import 'package:flutter/material.dart';
import 'package:url_strategy/url_strategy.dart';
void main() {
setPathUrlStrategy();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
routes: {
"/": (context) => HomeScreen(),
"/page": (context) => PageScreen(),
},
);
}
}
When going to "/" or "/page" it works fine. But as soon as I go to "/page?param=1". I get the following error:
The following message was thrown:
Could not navigate to initial route.
The requested route name was: "/page?param=1"
There was no corresponding route in the app, and therefore the initial route specified will be
ignored and "/" will be used instead.
Is Flutter not able to see what a query param is? It's web 101 doing something like this, I must be doing something wrong, I just can't find the answer.
Try using onGenerateRoute callback in MaterialApp, for eg:
onGenerateRoute: (RouteSettings settings) {
Widget? pageView;
if (settings.name != null) {
var uriData = Uri.parse(settings.name!);
//uriData.path will be your path and uriData.queryParameters will hold query-params values
switch (uriData.path) {
case '/page':
pageView = PageScreen();
break;
//....
}
}
if (pageView != null) {
return MaterialPageRoute(
builder: (BuildContext context) => pageView!);
}
},

Navigation with Fluro (Flutter web)

I'm trying to have navigation into pages. Trying to create some routes.
The problem I have is:
When the route changes it disappears from the URL
When I try to go back or forward in the browser it doesn't do anything.
I'm trying to use the Fluro package. I'm also trying to compare their example with mine and I don't find what is the difference.
main.dart:
void main() {
runApp(AppComponent());
}
class AppComponent extends StatefulWidget {
#override
State createState() {
return _AppComponentState();
}
}
class _AppComponentState extends State<AppComponent> {
_AppComponentState() {
final router = FluroRouter();
Routes.configureRoutes(router);
Application.router = router;
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'NexTeam',
debugShowCheckedModeBanner: false,
initialRoute: kHomeRoute,
onGenerateRoute: Application.router.generator,
);
}
}
class Application {
static FluroRouter router;
}
router.dart:
class Routes {
final router = FluroRouter();
static Handler _loginHandler = Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) => LoginPage());
static Handler _registerHandler = Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) => RegisterPage());
static Handler _homeHandler = Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) => HomePage());
static Handler _profileHandler = Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) => ProfilePage());
static Handler _notificationsHandler = Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) => NotificationsPage());
static Handler _chatHandler = Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) => ChatPage());
static void configureRoutes(FluroRouter router) {
router.define(kLoginRoute, handler: _loginHandler);
router.define(kRegisterRoute, handler: _registerHandler);
router.define(kHomeRoute, handler: _homeHandler);
router.define(kProfileRoute, handler: _profileHandler);
router.define(kNotificationsRoute, handler: _notificationsHandler);
router.define(kChatRoute, handler: _chatHandler);
}
}
function to navigate:
Application.router.navigateTo(context, kNotificationsRoute);
I still don't know why, but the issue was that all my pages started with MaterialApp, and the solution was to use Material.

Where to handle Firebase Dynamic Links in Flutter?

I use Firebase dynamic links and also named routes. What I want is to install a global listener for the dynamic link events and forward to register page if a token is provided. In the code below I got the exception The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget. which means I have to put navigation code below the home: property of MaterialApp. But when doing this I had to implement the dynamic links event handler for earch route.
class MyApp extends StatelessWidget {
String title = "Framr";
#override
Widget build(BuildContext context) {
FirebaseDynamicLinks.instance.onLink(
onSuccess: (linkData) {
if (linkData != null) {
try {
Navigator.pushNamed(context, '/register', arguments: linkData);
// throws: The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.
} catch(e) {
print(e);
}
}
return null;
}
);
return MaterialApp(
title: "...",
home: LoginPage(),
routes: {
'/createEvent': (context) => CreateEventPage(),
'/showEvent': (context) => ShowEventPage(),
'/register': (context) => RegisterPage(),
},
);
}
}
I was able to get this work by following the example provided from the dynamic link README with the use of the no_context_navigation package or GlobalKey to workaround around the lack of context to call Navigator.pushNamed(...). Note: You don't have to use no_context_navigation. You can implement the no context routing yourself. Here's an example.
// Add this
import 'package:no_context_navigation/no_context_navigation.dart';
void main() {
runApp(MaterialApp(
title: 'Dynamic Links Example',
// Add this
navigatorKey: NavigationService.navigationKey,
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => MyHomeWidget(), // Default home route
'/helloworld': (BuildContext context) => MyHelloWorldWidget(),
},
));
}
class MyHomeWidgetState extends State<MyHomeWidget> {
.
.
.
#override
void initState() {
super.initState();
this.initDynamicLinks();
}
void initDynamicLinks() async {
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLink) async {
// Add this.
final NavigationService navService = NavigationService();
final Uri deepLink = dynamicLink?.link;
if (deepLink != null) {
// This doesn't work due to lack of context
// Navigator.pushNamed(context, deepLink.path);
// Use this instead
navService.pushNamed('/helloworld', args: dynamicLink);
}
},
onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
}
);
final PendingDynamicLinkData data = await FirebaseDynamicLinks.instance.getInitialLink();
final Uri deepLink = data?.link;
if (deepLink != null) {
// This doesn't work due to lack of context
// Navigator.pushNamed(context, deepLink.path);
// Use this instead
navService.pushNamed('/helloworld', args: dynamicLink);
}
}
.
.
.
}
// pubspec.yaml
no_context_navigation: ^1.0.4

How to pass a class instance reference as a generic type in flutter

My objective is to create a method that would take the arguments of the buildcontext and screen reference and rout it accordingly with the provided details. My partially in complete code as bellow. help would much appreciate.
My Helper class
import 'package:flutter/material.dart';
class Utils {
static routToPage <T> (BuildContext context, <T> page){
Navigator.of(context).push(MaterialPageRoute(builder: (context) =>page);
}
}
The class I wants to access it
import './utils'
import 'package:flutter/material.dart';
class Home extends StatelessWidget {
navigateToPage(){
Utils.rout(BuildContext context, PageTwo())
}
}
For now as the syntax are wrong I'm stuck with my approach.
Well, I think no need to create static class. Here is a working example.
Paste the below method in a dart file.
void navigateToScreen(BuildContext context, Widget widget) {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => widget,
));
}
And use the class for navigating, like
navigateToScreen(buildContext, YourNavigationScreen());
I prefer using Widget as a object parameter instead of <T>.
Also you can check the easiest way navigate to another page by using route like this.
Create Route in (yourproject)/lib/route/routes.dart
import './page/main_page.dart';
import './page/one_page.dart';
import './page/two_page.dart';
final routes = {
'/': (BuildContext context) => MainPage(),
'/pageone': (BuildContext context) => PageOne(),
'/pagetwo': (BuildContext context) => PageTwo(),
}
Main Page in (yourproject)/lib/main.dart
import './route/routes.dart';
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
SystemChrome.setEnabledSystemUIOverlays([]);
return MaterialApp(
initialRoute: '/', // the default route is MainPage()
routes: routes,
);
}
}
When navigating to other page you can simply use either one of these Navigator.pushNamed, Navigator.popAndPushNamed, Navigator.pushNamedAndRemoveUntil, Navigator.pushReplacementNamed do like this
// your can simple change the route of your choice by changing the name of route
Navigator.pushNamed(context, '/pageone');
Navigator.popAndPushNamed(context, '/pagetwo');
...

How to navigate without context in flutter app?

I have an app that recieves push notification using OneSignal. I have made a notification opened handler that should open specific screen on click of the notification. How can i navigate to a screen without context. or how can I open specific screen on app startup. My code:
OneSignal.shared.setNotificationOpenedHandler((notification) {
var notify = notification.notification.payload.additionalData;
if (notify["type"] == "message") {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => DM(user: notify['id']),
),
);
}
if (notify["type"] == "user") {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => Profileo(notify["id"]),
),
);
}
if (notify["type"] == "post") {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ViewPost(notify["id"]),
),
);
}
});
I am able to achieve this when the app is opened for the first time but It only opens the homepage If i close the app and even if I re-open it. I guess that is because the context is changed.
Please Help!!
Look at this here:
https://github.com/brianegan/flutter_redux/issues/5#issuecomment-361215074
You can set a global key for your navigation:
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
Pass it to MaterialApp:
new MaterialApp(
title: 'MyApp',
onGenerateRoute: generateRoute,
navigatorKey: navigatorKey,
);
Push routes:
navigatorKey.currentState.pushNamed('/someRoute');
You can use this wonderful plugin:
https://pub.dev/packages/get
Description from the package: A consistent navigation library that lets you navigate between screens, open dialogs, and display snackbars from anywhere in your code without context.
Get.to(NextScreen()); // look at this simplicity :)
Get.back(); // pop()
Get.off(NextScreen()); // clears the previous routes and opens a new screen.
This solution is general if you want to navigate or to show dialog without context using globalKey especially with Bloc or when your logic is separated from your UI part.
Firstly install this package:
Not: I'm using null safety version
get_it: ^7.2.0
Then create a separate file for your service locator:
service_location.dart
import 'package:get_it/get_it.dart';
GetIt locator = GetIt.instance;
class NavigationService {
final GlobalKey<NavigatorState> navigatorKey =
new GlobalKey<NavigatorState>();
Future<dynamic> navigateTo(String routeName) {
return navigatorKey.currentState!.pushNamed(routeName);
}
void setupLocator() {
locator.registerLazySingleton(() => NavigationService());
}
void showMyDialog() {
showDialog(
context: navigatorKey.currentContext!,
builder: (context) => Center(
child: Material(
color: Colors.transparent,
child: Text('Hello'),
),
));
}
}
on main.dart:
void main() {
WidgetsFlutterBinding.ensureInitialized();
NavigationService().setupLocator();
runApp(MyApp());
}
// add navigatorKey for MaterialApp
MaterialApp(
navigatorKey: locator<NavigationService>().navigatorKey,
),
at your business logic file bloc.dart
define this inside the bloc class or at whatever class you want to use navigation inside
Then start to navigate inside any function inside.
class Cubit extends Cubit<CubitState> {
final NavigationService _navigationService = locator<NavigationService>();
void sampleFunction(){
_navigationService.navigateTo('/home_screen'); // to navigate
_navigationService.showMyDialog(); // to show dialog
}
}
Not: I'm using generateRoute for routing.
Quickest fix is above using global navigatorKey (like #tsdevelopment answered).
To fix undefined navigatorKey, it must be imported from where it is instantiated (for this example in main.dart).
Your main.dart
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
void main() {
runApp(CupertinoApp(
title: 'Navigate without context',
initialRoute: '/',
navigatorKey: navigatorKey, // important
onGenerateRoute: ...
));
}
For example you are in your lib/utils/api.dart
import 'package:your_package_name/main.dart'; // important
abstract class API {
static Future<dynamic> get() async {
// call some api
...
// then you want to navigate to specific screen like login
navigatorKey.currentState?.pushNamed('/login'); // navigate to login, with null-aware check
}
}
Also have a gist example if you prefer in a service approach.
Check this: https://gist.github.com/josephdicdican/81e59fad70530eac251ad6c28e2dcd4b
I know this is an old post, but there is a package that handles navigation without the build context (Using a navigator key) called flutter_navigator: https://pub.dev/packages/flutter_navigator
It allows you to navigate something like this:
_flutterNavigation.push(//YourRoute);
Everything seems to be mapped 1:1 with Flutter's Navigator API, so there is no worries there!
You can use this no_context_navigation package
as the name suggests, we can navigate without context
navService.pushNamed('/detail_screen', args: 'From Home Screen');