Featured Image
Software Development

Implementing Authentication and Navigation in Flutter Web using go_router

In today’s digital world, implementing robust authentication and seamless navigation are crucial aspects of any web application. 

When it comes to Flutter Web, developers face unique challenges in setting up authentication and navigation due to the nature of web-based interactions. 

Fortunately, there is a powerful package called go_router that simplifies the process and enhances the user experience. 

In this article, we will delve into the importance of authentication and navigation in Flutter Web and explore how go_router can be utilized to overcome these challenges effectively.

Authentication in Flutter Web:

Authentication forms the backbone of secure web applications, ensuring that only authorized users can access sensitive information or perform specific actions. Implementing authentication in Flutter Web is of utmost importance as it provides several benefits and plays a crucial role in the overall security and integrity of your application.

Benefits of Implementing Authentication:

  1. Data Protection: By implementing authentication, you can safeguard user data from unauthorized access. It ensures that only authenticated users have access to sensitive information, such as personal details, payment information, or private content.
  1. User Accountability: Authentication allows you to establish user accountability within your application. By verifying user identities, you can track and attribute specific actions or modifications to individual users, enabling better auditing and accountability.
  1. Personalized User Experience: Authenticated users can enjoy a personalized experience within your application. You can tailor content, settings, and features based on user preferences, leading to enhanced user satisfaction and engagement.
  2. Secure Communication: Authentication facilitates secure communication between the client and server by employing encryption and cryptographic protocols. This ensures that sensitive data transmitted over the network remains confidential and protected from interception or tampering.

Importance of Implementing Authentication:

  1. Protecting User Privacy: Authentication is crucial for safeguarding user privacy and preventing unauthorized access to personal information. It establishes trust and confidence among users, encouraging them to interact with your application without concerns about their data being compromised.
  1. Preventing Unauthorized Access: Implementing authentication mechanisms prevents unauthorized users from gaining access to restricted areas or performing actions reserved for authenticated users. It adds an additional layer of security and prevents potential misuse or abuse of your application’s functionalities.
  1. Regulatory Compliance: Depending on your application’s nature, you may be required to comply with specific regulations and standards, such as GDPR (General Data Protection Regulation). Implementing authentication helps you meet these compliance requirements, ensuring the protection of user data and avoiding potential legal repercussions.
  1. Protecting Intellectual Property: Authentication is vital for safeguarding proprietary or confidential information within your application. By restricting access to authenticated users, you can protect your intellectual property, trade secrets, or confidential business data from unauthorized disclosure or misuse.
  2. Building User Trust: Authentication enhances user trust in your application. When users feel confident that their data is protected, they are more likely to engage with your application, share sensitive information, and conduct transactions. Building user trust is essential for the success and growth of your application.

By recognizing the benefits and understanding the importance of implementing authentication in Flutter Web, you can ensure the security, privacy, and integrity of your application. 

It enables you to provide a seamless and trusted user experience while protecting sensitive data from unauthorized access. 

Invest the necessary time and effort into implementing robust authentication mechanisms to establish a strong foundation for your Flutter Web application’s security and success.

Navigation in Flutter Web:

Seamless navigation is vital for creating intuitive and user-friendly web applications. In Flutter Web, navigation plays a crucial role in providing a smooth user experience and ensuring users can effortlessly move between different sections of your application. Let’s explore the benefits and importance of implementing effective navigation in your Flutter Web projects.

Benefits of Implementing Navigation:

  1. Enhanced User Experience: Proper navigation enables users to explore and interact with your application seamlessly. It allows them to navigate through different screens or sections without confusion, ensuring a positive user experience and increased user satisfaction.
  1. Improved Discoverability: Clear and intuitive navigation makes it easier for users to find and access specific features or content within your application. By organizing information effectively and providing visible navigation elements, users can quickly discover and navigate to the desired sections.
  1. Simplified Information Hierarchy: Navigation helps establish a logical information hierarchy within your application. By categorizing and structuring content using appropriate navigation elements, users can easily understand the relationships between different sections and navigate accordingly.
  2. Consistency across Platforms: Flutter Web’s navigation system, which resembles mobile app navigation, provides consistency for users who are already familiar with Flutter mobile applications. By adopting a similar navigation approach, you ensure a seamless experience for users transitioning between different platforms.

Importance of Implementing Navigation:

  1. User Engagement: Intuitive navigation encourages users to explore your application further, leading to increased engagement and prolonged usage. When users can easily navigate between screens and find relevant content, they are more likely to stay engaged with your application and accomplish their goals.
  1. Goal Achievement: Effective navigation facilitates users in achieving their desired goals within your application. Whether it’s making a purchase, accessing information, or performing specific actions, streamlined navigation ensures users can reach their intended destinations efficiently.
  1. Conversion Optimization: Well-designed navigation can significantly impact conversion rates. By guiding users through the conversion funnel and providing clear paths to desired actions (such as making a purchase or signing up), you can optimize the conversion process and improve overall conversion rates.
  2. User Retention: Seamless navigation contributes to user retention by reducing friction and frustration during the browsing experience. When users can easily find what they need and navigate through your application effortlessly, they are more likely to return and continue using your application in the future.

Incorporating effective navigation in your Flutter Web application not only enhances the user experience but also helps achieve business goals, improve conversion rates, and increase user retention. By adopting a user-centric approach and designing a clear and intuitive navigation system, you can provide a seamless browsing experience that keeps users engaged, satisfied, and coming back for more.

Investing time and effort in understanding your users’ needs, organizing your content, and implementing well-designed navigation elements will undoubtedly contribute to the success and usability of your Flutter Web application.

Learn How to Build Complex Navigation Stack with Flutter

What is Go Router?

Go Router is a powerful Flutter package specifically designed for managing navigation in Flutter Web applications.
It offers several benefits over the standard Flutter navigation system, making it an excellent choice for implementing navigation in Flutter Web projects.

Why use go_router instead of Flutter Navigation 2.0?

While Flutter Navigation 2.0 provides a solid foundation for navigation, go_router offers additional features and flexibility specifically tailored for Flutter Web. It simplifies the navigation process and improves overall performance, resulting in a smoother user experience. Go Router leverages the browser history API to manage the page stack and back navigation, allowing users to navigate backwards through the application history using the browser’s back button or other navigation gestures. This feature ensures consistent behaviour and enhances user convenience.

Features of Go Router:

Easy setup: Go Router provides a straightforward and intuitive API, making it simple to integrate with your Flutter Web project.
Deep linking: Go Router enables deep linking by updating the URL dynamically, allowing users to share specific screens or states of your application.
Nested routing: With Go Router, you can easily implement nested routing, allowing for complex and hierarchical navigation structures.
Error handling: Go Router offers built-in error handling mechanisms, making it easier to handle exceptions and provide meaningful error messages to users.

Here’s How You Can Implement Authentication and Navigation in Flutter Web using go_router

Setting Up the Project:

Create flutter project
  • Create a new Flutter project by running the following command or from Android Studio
flutter create my_app
Add dependencies
  • Add the following dependencies to the pubspec.yaml with the latest version.
dependencies:
 go_router: ^7.0.1
 hive: ^2.2.3
 hive_flutter: ^1.1.0
 dev_dependencies:
 build_runner: ^2.4.2
 hive_generator: ^2.0.0

Here, the hive is used to store local data. You can use shared_preferences or any other local data storage package instead of the hive.

Creating a Common Service:

Create a common service to handle authentication-related logic, such as user authentication, session management, and secure storage. 

This service will serve as the foundation for implementing authentication in your Flutter Web application.

  • Create a services folder inside the lib folder and create the following file.
  • You can place other common services files inside this folder.
app_service.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hive/hive.dart';

import '../../models/user_data.dart';
import '../pages/login_page.dart';

class AppService extends ChangeNotifier {
 /// Ensure to make this as a singleton class.
 AppService._();

 factory AppService() => _instance;

 static AppService get instance => _instance;
 static final AppService _instance = AppService._();

 final Box storageBox = Hive.box('App Service Box');

 final navigatorKey = GlobalKey<NavigatorState>();

 BuildContext get context => navigatorKey.currentContext!;

 final _kCurrentUserKey = 'current_user';

 UserData? currentUser;

 bool get isLoggedIn => currentUser != null;

 void initialize() {
   final user = storageBox.get(_kCurrentUserKey);
   if (user != null) currentUser = user;
 }

 void setUserData(UserData userData) {
   storageBox.put(_kCurrentUserKey, userData);
   currentUser = userData;
   notifyListeners();
 }

 void manageAutoLogout() {
   terminate();
   context.go(LoginPage.route);
 }

 Future<void> terminate() async {
   currentUser = null;
   storageBox.clear();
 }
}
  • Create a models folder inside the lib folder and create the following file.
  • This UserData model is helpful for saving data in local storage.
user_data.dart
import 'package:hive/hive.dart';

part 'user_data.g.dart';

@HiveType(typeId: 0)
class UserData {
 @HiveField(0)
 final String? id;
 @HiveField(1)
 final String? email;

 const UserData({
   this.id,
   this.email,
 });
}
  • Run the below command to generate the hive adapter class.
flutter pub run build_runner build --delete-conflicting-outputs

This command will generate user_data.g.dart at the same location as user_data.dart

Setting Up Navigation:

Initialize go_router in your application and define the initial route.
You can set up a default route and handle the navigation based on the user’s authentication status.

  • Create a folder routing inside the lib folder and create the following two files.
  • The GoRoute is used to create every route. If you want to make sub-routes then those pages will go under the routes array of a GoRoute.
router.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

import '../pages/home_page.dart';
import '../pages/login_page.dart';
import '../services/app_service.dart';

part 'redirection.dart';

final router = GoRouter(
 redirect: _redirect,
 debugLogDiagnostics: true,
 refreshListenable: AppService.instance,
 navigatorKey: AppService.instance.navigatorKey,
 routes: <GoRoute>[
   GoRoute(
     path: '/',
     redirect: (_, __) => HomePage.route,
   ),
   GoRoute(
     path: LoginPage.route,
     pageBuilder: (context, state) => const MaterialPage(child: LoginPage()),
   ),
   GoRoute(
     path: HomePage.route,
     pageBuilder: (context, state) => const MaterialPage(child: HomePage()),
   ),
 ],
);
Redirection:

Redirect users based on their authentication status. If a user is not authenticated, redirect them to the login page. Otherwise, navigate them to the home page or any other authorized sections.

  • _redirect method is useful for redirecting the user to a proper route. For example, if a user is logged in to the web page and types the URL of login, then it should go to the home URL automatically. That could be done by the following.
redirection.dart
part of 'router.dart';

String? _redirect(BuildContext context, GoRouterState state) {
 final isLoggedIn = AppService.instance.isLoggedIn;
 final isLoginRoute = state.matchedLocation == LoginPage.route;

 if (!isLoggedIn && !isLoginRoute) {
   return LoginPage.route;
 } else if (isLoggedIn && isLoginRoute) {
   return HomePage.route;
 }
 return null;
}
Setup Routing:

Define routes for different screens and components of your application.
With go_router, you can easily map routes to specific pages or widgets.

  • Edit your main.dart for setup router using the GoRouter.
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_web_plugins/url_strategy.dart';
import 'package:hive_flutter/hive_flutter.dart';

import 'models/user_data.dart';
import 'routing/router.dart';
import 'services/app_service.dart';

void main() async {
 WidgetsFlutterBinding.ensureInitialized();
 await Hive.initFlutter();
 Hive.registerAdapter(UserDataAdapter());
 await Hive.openBox('App Service Box');
 runApp(const MyApp());
}

class MyApp extends StatefulWidget {
 const MyApp({super.key});

 @override
 State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
 @override
 void initState() {
   super.initState();
   usePathUrlStrategy();
   AppService.instance.initialize();
 }

 @override
 Widget build(BuildContext context) {
   return MaterialApp.router(
     title: 'Web Authentication',
     debugShowCheckedModeBanner: false,
     routerDelegate: router.routerDelegate,
     routeInformationParser: router.routeInformationParser,
     routeInformationProvider: router.routeInformationProvider,
   );
 }
}
  • Here we use MaterialApp.router to implement Navigation 2.0.
  • usePathUrlStrategy is used to remove # from the URL.
Design pages:

Create the necessary pages and widgets for your application.
Each page should correspond to a specific route defined in the go_router configuration and provide the desired functionality.
Design your pages with user-friendly interfaces, intuitive navigation elements, and responsive layouts to ensure a pleasant user experience across various devices.

  • Create a pages folder inside lib and create the following files.
  • Login page design with an email and a password field.
login_page.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:web_auth_demo/models/user_data.dart';
import 'package:web_auth_demo/services/app_service.dart';

import 'home_page.dart';

class LoginPage extends StatefulWidget {
 static const route = '/login';

 const LoginPage({Key? key}) : super(key: key);

 @override
 State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
 final _emailCtrl = TextEditingController();
 final _passwordCtrl = TextEditingController();

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     body: ListView(
       padding: const EdgeInsets.all(16),
       children: [
         const Text(
           'Login to your account',
           style: TextStyle(
             fontSize: 16,
             fontWeight: FontWeight.w600,
           ),
         ),
         const SizedBox(height: 32),
         TextFormField(
           controller: _emailCtrl,
         ),
         const SizedBox(height: 16),
         TextFormField(
           controller: _passwordCtrl,
           obscureText: true,
         ),
         const SizedBox(height: 20),
         ElevatedButton(
           onPressed: () {
             AppService.instance.setUserData(UserData(
               id: DateTime.now().millisecondsSinceEpoch.toString(),
               email: _emailCtrl.text,
             ));
             context.go(HomePage.route);
           },
           child: const Text('Login'),
         )
       ],
     ),
   );
 }
}
home_page.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

import '../pages/login_page.dart';
import '../services/app_service.dart';

class HomePage extends StatefulWidget {
 static const route = '/home';

 const HomePage({Key? key}) : super(key: key);

 @override
 State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: const Text('Home page'),
       actions: [
         IconButton(
           onPressed: () async {
             await AppService.instance.terminate();
             if (mounted) context.go(LoginPage.route);
           },
           icon: const Icon(Icons.logout),
         )
       ],
     ),
     body: Center(
       child: Text('Login to ${AppService.instance.currentUser!.email}'),
     ),
   );
 }
}

Explore a comparison between the performance of an app built with Native Android and Flutter.

Conclusion:

Implementing authentication and navigation in Flutter Web is vital for creating secure and user-friendly web applications. 

The go_router package offers a powerful solution for managing navigation, providing a seamless browsing experience for users. By leveraging go_router, developers can simplify the navigation setup process, handle authentication efficiently, and enhance the overall user experience.

By following the steps outlined in this article, you can easily set up go_router in your Flutter Web project, add necessary dependencies, create a common service for authentication logic, define routes, and design pages that provide a cohesive and intuitive user experience. For any assistance with Flutter app development, connect with our team of skilled Flutter developers today. They will provide expert guidance and deliver top-notch solutions tailored to your needs.

author
Nayan Babariya
A Senior Flutter Engineer with specialization in the Flutter framework for building cross-platform mobile applications. I have a strong understanding of the Flutter ecosystem and am able to use its various features and tools to build high-quality, scalable, and maintainable apps.