5 Step – Beautiful Design Responsive Flutter Login Page with Source Code

/Responsive Flutter Login Page with Source Code

A well-designed and responsive login page is crucial for any Flutter application, ensuring a seamless user experience across different devices.

In this guide, we will build a beautiful, responsive Flutter login page with source code, following clean architecture principles using Bloc.

By the end, you’ll have a fully functional authentication system that is scalable and efficient.

This is Day 3 in building the Connect app.

Responsive Flutter Login Page with Source Code

If you would like to know more about bloc click here ➡️

Although bloc architecture is super useful, it can be tough to understand initially.

I would like to give a very brief idea.

Most of you must know the MVC(model view controller framework) architecture.

Bloc is used to do the same job making the code clean by separating UI from Business logic. But, in it’s own way.

MVC (Model-View-Controller) Overview:

  • Model: This holds the data and the business logic.
  • View: This is the UI that displays the data and receives user inputs.
  • Controller: This acts as the middleman between the Model and the View, handling the logic when a user interacts with the View.

Now, let’s compare Bloc with MVC:

In Bloc, the Bloc is like the Controller: it listens for events from the UI, processes the logic (based on those events), and then updates the State, which the UI listens to and reacts upon. The Bloc doesn’t deal with the UI directly—it only manages the business logic and state.

Model in MVC vs. State in Bloc:

  • In MVC, the Model is responsible for the data and business logic.
  • In Bloc, the State represents the data that the app displays, and the Bloc itself handles the business logic of how the state changes based on events.

View in MVC vs. UI in Bloc:

  • In MVC, the View displays the data to the user and captures user input.
  • In Bloc, the UI still does the same thing (displays the data and captures user input), but instead of directly modifying the state, it communicates with the Bloc to send events and listens for state updates.

Controller in MVC vs. Bloc in Bloc Architecture:

  • In MVC, the Controller acts as the middleman between the Model and View. It listens to user input (from the View), performs logic (possibly modifying the Model), and updates the View.

If this gave you a brief idea of what bloc is great. If not no worries you’ll get it on the way.

Responsive Flutter Login Page with Source Code

Step 1: Define States

Head over to the presentation folder inside it create two folders:

  1. cubits
  2. pages

Cubit is the little sibling of the bloc. It is simple and only works with states, no events.

For that let’s start by creating our authentication states.

Inside the cubits folder create auth_states.dart

This is to manage states of authentication.

There can be 4 such states:

  1. Initial state
  2. Loading state
  3. authenticated state
  4. unauthenticated state

Then we will leave room for errors in the error state. You can consider this to be the 5th state.

Create an abstract class CAuthState.

Extend it in these 5 states.

➡️Click to view auth_states.dart
import 'package:connect/features/auth/domain/entities/app_user.dart';

abstract class CAuthState {}

// initial 
class CAuthInitialState extends CAuthState {}

// loading
class CAuthLoadingState extends CAuthState {}

// Authenticated
class CAuthAuthenticatedState extends CAuthState {
    final CAppUser user;
    CAuthAuthenticatedState(this.user);
}

// unauthenticated
class CAuthUnauthenticatedState extends CAuthState {}

// errors...
class CAuthErrorState extends CAuthState {
    final String message;
    CAuthErrorState(this.message);
}

Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code

These are the different possible states in the process of authentication.

Now moving on to the bloc part of it.

Step 2: Define Cubit

Install this package:

flutter pub add flutter_bloc

Inside the cubit folder create auth_cubit.dart

The purpose of this cubit is to manage different states.

➡️Click To View The Code of auth_cubit.dart
import 'package:connect/features/auth/domain/entities/app_user.dart';
import 'package:connect/features/auth/domain/repos/auth_repo.dart';
import 'package:connect/features/auth/presentation/cubits/auth_states.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class CAuthCubit extends Cubit<CAuthState> {
  final CAuthRepo authRepo;
  CAppUser? _currentUser;

  CAuthCubit({required this.authRepo}) : super(CAuthInitialState());

  // check if user is already authenticated
  void checkAuth() async {
    final CAppUser? user = await authRepo.getCurrentUser();
    if(user!=null){
      _currentUser = user;
      emit(CAuthAuthenticatedState(user));
    }
    else {
      emit(CAuthUnauthenticatedState());
    }
  }

  // get current user
  CAppUser? get currentUser => _currentUser;

  // login with email and password
  Future<void> login(String email, String password) async {
    try {
      emit(CAuthLoadingState());
      final user = await authRepo.loginWithEmailPassword(email, password);
      if(user != null) {
        _currentUser = user;
        emit(CAuthAuthenticatedState(user));
      } 
      else {
        emit(CAuthUnauthenticatedState());
      }
    }
    catch(e) {
      emit(CAuthErrorState(e.toString()));
      emit(CAuthUnauthenticatedState());
    }
  }

  // register with email and password
  Future<void> register(String name, String email, String password) async {
    try {
      emit(CAuthLoadingState());
      final user = await authRepo.registerWithEmailPassword(name, email, password);
      if(user != null) {
        _currentUser = user;
        emit(CAuthAuthenticatedState(user));
      } 
      else {
        emit(CAuthUnauthenticatedState());
      }
    }
    catch(e) {
      emit(CAuthErrorState(e.toString()));
      emit(CAuthUnauthenticatedState());
    }
  }

  // logout
  Future<void> logout() async {
    _currentUser = null;
    await authRepo.logout();
    emit(CAuthUnauthenticatedState());
  }
}
➡️Click To See The Explanation if you Can’t Understand Any Particular Thing

Imports

  • app_user.dart → Defines the CAppUser entity, which represents a user.
  • auth_repo.dart → Defines CAuthRepo, an authentication repository that provides methods for login, registration, and logout.
  • auth_states.dart → Defines different authentication states used in this Cubit.
  • flutter_bloc.dart → Provides the Cubit class, used for state management.

Class Definition

  • This class extends Cubit<CAuthState>, meaning it manages authentication states.
  • The states are defined in auth_states.dart.

Properties

  • authRepo: A repository instance that provides authentication-related methods.
  • _currentUser: A private variable that stores the currently logged-in user.

Constructor

  • Takes authRepo as a required parameter.
  • Initializes the Cubit with CAuthInitialState() (default state when the app starts).

Check Authentication

  • Calls authRepo.getCurrentUser() (asynchronously) to check if a user is already logged in.
  • If a user exists:
    • _currentUser is updated.
    • Emits CAuthAuthenticatedState(user).
  • If no user is found, emits CAuthUnauthenticatedState().

📌 When does this function run?

  • When the app starts to check if the user is already logged in.

Get Current User

  • Provides a getter method to access _currentUser.

Login with Email and Password

  1. Emits CAuthLoadingState() (to indicate the loading state).
  2. Calls authRepo.loginWithEmailPassword(email, password).
  3. If authentication is successful:
    • Updates _currentUser.
    • Emits CAuthAuthenticatedState(user).
  4. If authentication fails, emits CAuthUnauthenticatedState().
  5. If an error occurs (e.g., network failure, incorrect credentials):
    • Emits CAuthErrorState(e.toString()).
    • Then resets the state to CAuthUnauthenticatedState().

📌 Handles error gracefully and prevents app crashes.

Register with Email and Password

  • Similar to login(), but calls authRepo.registerWithEmailPassword(name, email, password).
  • If registration is successful, updates _currentUser and emits CAuthAuthenticatedState(user).
  • If registration fails, emits CAuthUnauthenticatedState().
  • If an error occurs, it catches the exception and emits CAuthErrorState(e.toString()).

📌 Ensures state transitions are smooth even in case of errors.

Logout

  1. Calls authRepo.logout(), which clears the user session.
  2. Emits CAuthUnauthenticatedState() to update the UI.

📌 After logout, _currentUser remains unchanged in memory. If needed, it should be reset to null.

Summary

FunctionPurposeState Emitted
checkAuth()Check if a user is logged inCAuthAuthenticatedState / CAuthUnauthenticatedState
login(email, password)Logs in a userCAuthLoadingState, CAuthAuthenticatedState, CAuthUnauthenticatedState, CAuthErrorState
register(name, email, password)Registers a new userCAuthLoadingState, CAuthAuthenticatedState, CAuthUnauthenticatedState, CAuthErrorState
logout()Logs out the userCAuthUnauthenticatedState
currentUserRetrieves logged-in user

How It Works in the App

CAuthUnauthenticatedState() is emitted.

On App Start

checkAuth() runs to check if the user is logged in.

If authenticated → CAuthAuthenticatedState(user).

Else → CAuthUnauthenticatedState().

On Login

The user enters their email & password.

login(email, password) is called.

If correct → CAuthAuthenticatedState(user).

If incorrect → CAuthUnauthenticatedState().

On Registration

The user enters name, email & password.

register(name, email, password) is called.

If successful → CAuthAuthenticatedState(user).

If error → CAuthErrorState(error).

On Logout

logout() is called.

Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code

Finally, we are all set for the UI of our Login Page.

Step 3: Create a Login Page And Connect It To main.dart

Create login_page.dart in the lib/features/auth/presentation/pages folder.

➡️Click To View login_page.dart
import 'package:flutter/material.dart';
class CLoginPage extends StatefulWidget {
  const CLoginPage({super.key});

  @override
  State<CLoginPage> createState() => _CLoginPageState();
}

class _CLoginPageState extends State<CLoginPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold();
  }
}

Create a themes folder in lib folder.

Add light_mode.dart in this folder:

➡️Click To View light_mode.dart
import 'package:flutter/material.dart';

ThemeData lightMode = ThemeData(
  colorScheme: ColorScheme.light(
    surface: Colors.white,
    primary: Color(0xFFFCE4EC),  // Button background color
    secondary: Colors.black,      // Button text color
    tertiary: Colors.grey.shade400, // Hint text color
    inversePrimary: Colors.black,  // Focused text field border
  ),
  scaffoldBackgroundColor: Colors.white,
  elevatedButtonTheme: ElevatedButtonThemeData(
    style: ElevatedButton.styleFrom(
      backgroundColor: Color(0xFFFCE4EC),
      padding: const EdgeInsets.symmetric(vertical: 16),
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(8),
      ),
    ),
  ),
  inputDecorationTheme: InputDecorationTheme(
    hintStyle: TextStyle(color: Colors.grey.shade400),
    enabledBorder: UnderlineInputBorder(
      borderSide: BorderSide(color: Colors.grey.shade200),
    ),
    focusedBorder: const UnderlineInputBorder(
      borderSide: BorderSide(color: Colors.black),
    ),
  ),
);
➡️Call these in the main.dart
import 'package:connect/features/auth/presentation/pages/login_page.dart';
import 'package:connect/firebase_options.dart';
import 'package:connect/themes/light_mode.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

void main() async {
  // firebase setup
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  // run app
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme:lightMode,
      home: const CLoginPage(),
    );
  }
}

Step 4: Design The Login Page

Create an assets folder in the main project directory.

Now, create an images folder inside it.

Add this illustration or any illustration inside it.

login illustration

You can get a lot of such for free in Freepik or any other such platform.

Now head over to pubspec.yaml and add this file therein.

# To add assets to your application, add an assets section, like this:
  assets:
    - assets/images/

Inside the lib folder create a utils folder. Inside that create a responsive_helper.dart

➡️Click To View responsive_helper.dart
import 'package:flutter/material.dart';

class ResponsiveHelper {
  final BuildContext context;
  late double screenWidth;
  late double screenHeight;

  ResponsiveHelper(this.context) {
    screenWidth = MediaQuery.of(context).size.width;
    screenHeight = MediaQuery.of(context).size.height;
  }

  // Font size based on screen width
  double fontSize(double percentage) {
    return screenWidth * (percentage / 100);
  }

  // Spacing based on screen height
  double height(double percentage) {
    return screenHeight * (percentage / 100);
  }

  // Padding based on screen width
  double width(double percentage) {
    return screenWidth * (percentage / 100);
  }
}

Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code

Go on to create a components folder inside the presentation layer.

Create two files:

  1. c_button.dart
  2. c_text_field.dart
➡️Click To View c_button.dart
import 'package:flutter/material.dart';
import 'package:connect/utils/responsive_helper.dart';

class CButton extends StatelessWidget {
  final String text;
  final void Function()? onPressed;

  const CButton({super.key, required this.text, required this.onPressed});

  @override
  Widget build(BuildContext context) {
    final res = ResponsiveHelper(context); // Initialize responsive helper

    return SizedBox(
      width: res.width(100), // 100% of screen width
      child: ElevatedButton(
        onPressed: onPressed,
        style: ElevatedButton.styleFrom(
          backgroundColor: Theme.of(context).colorScheme.primary,
          padding: EdgeInsets.symmetric(vertical: res.height(2)), // 2% of screen height
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(res.width(3)), // 3% of screen width
          ),
        ),
        child: Text(
          text,
          style: TextStyle(
            color: Colors.black,
            fontSize: res.fontSize(4.5), // 4.5% of screen width
          ),
        ),
      ),
    );
  }
}
➡️Click To View c_text_field.dart
import 'package:flutter/material.dart';
import 'package:connect/utils/responsive_helper.dart';

class CTextField extends StatelessWidget {
  final String hintTxt;
  final bool ispass;

  const CTextField({super.key, required this.hintTxt, required this.ispass});

  @override
  Widget build(BuildContext context) {
    final res = ResponsiveHelper(context); // Initialize responsive helper

    return TextField(
      obscureText: ispass,
      decoration: InputDecoration(
        hintText: hintTxt,
        hintStyle: TextStyle(
          color: Colors.grey.shade400,
          fontSize: res.fontSize(4), // 4% of screen width
        ),
        enabledBorder: UnderlineInputBorder(
          borderSide: BorderSide(color: Colors.grey.shade200),
        ),
        focusedBorder: UnderlineInputBorder(
          borderSide: BorderSide(color: Theme.of(context).colorScheme.inversePrimary),
        ),
      ),
    );
  }
}

Finally, modify login_page.dart, and add the image, text, text fields, and button.

➡️Click To View login_page.dart
import 'package:flutter/material.dart';
import 'package:connect/utils/responsive_helper.dart';
import 'package:connect/features/auth/presentation/components/c_button.dart';
import 'package:connect/features/auth/presentation/components/c_text_field.dart';

class CLoginPage extends StatelessWidget {
  const CLoginPage({super.key});

  @override
  Widget build(BuildContext context) {
    final res = ResponsiveHelper(context); // Initialize helper

    return Scaffold(
      backgroundColor: Theme.of(context).scaffoldBackgroundColor,
      body: SafeArea(
        child: Padding(
          padding: EdgeInsets.all(res.width(6)), // 6% padding
          child: ListView(
            children: [
              Image.asset(
                "assets/images/login_img.png",      
              ),
              SizedBox(height: res.height(2)),

              // Welcome Back text
              Center(
                child: Text(
                  'Welcome Back',
                  style: Theme.of(context).textTheme.headline6?.copyWith(
                        fontSize: res.fontSize(7), // 7% of screen width
                        fontWeight: FontWeight.bold,
                      ),
                ),
              ),

              SizedBox(height: res.height(2)),

              // Not registered text and sign-up link
              Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text(
                    'Not registered yet?',
                    style: TextStyle(color: Theme.of(context).colorScheme.tertiary),
                  ),
                  TextButton(
                    onPressed: () {},
                    child: Text(
                      'Sign up',
                      style: TextStyle(color: Theme.of(context).colorScheme.inversePrimary),
                    ),
                  ),
                ],
              ),

              SizedBox(height: res.height(3)),

              // Name TextField
              const CTextField(hintTxt: "Name", ispass: false),

              SizedBox(height: res.height(2)),

              // Password TextField
              const CTextField(hintTxt: "Password", ispass: true),

              SizedBox(height: res.height(4)),

              // Log In Button
              CButton(text: 'Log In', onPressed: () {}),

              SizedBox(height: res.height(2)),

              // Forgotten password text
              Center(
                child: TextButton(
                  onPressed: () {},
                  child: Text(
                    'Forgotten your password or login details?',
                    style: TextStyle(
                      color: Theme.of(context).colorScheme.tertiary,
                      fontSize: res.fontSize(3), // 3% of screen width
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Your login page should look like this:

Responsive Flutter Login Page with Source Code

This is responsive but only for mobile phones.

Let’s now make it responsive for the desktop as well.

We will be deploying it on the web only where it needs to look good on both platforms.

Step 5: Make Login Page Desktop Responsive

Create a method _buildMobileLayout And transfer all the scaffold’s code to this method.

➡️Click To view _buildMobileLayout
 Widget _buildMobileLayout(BuildContext context, ResponsiveHelper res) {
    return Padding(
      padding: EdgeInsets.all(res.width(6)),
      child: ListView(
        shrinkWrap: true,
        children: [
          Image.asset(
            "assets/images/login_img.png",
          ),
          SizedBox(height: res.height(2)),
          Center(
            child: Text(
              'Welcome Back',
              style: Theme.of(context).textTheme.headline6?.copyWith(
                    fontSize: res.fontSize(7),
                    fontWeight: FontWeight.bold,
                  ),
            ),
          ),
          SizedBox(height: res.height(2)),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                'Not registered yet?',
                style: TextStyle(
                  color: Theme.of(context).colorScheme.tertiary,
                ),
              ),
              TextButton(
                onPressed: () {},
                child: Text(
                  'Sign up',
                  style: TextStyle(
                    color: Theme.of(context).colorScheme.inversePrimary,
                  ),
                ),
              ),
            ],
          ),
          SizedBox(height: res.height(3)),
          const CTextField(hintTxt: "Name", ispass: false),
          SizedBox(height: res.height(2)),
          const CTextField(hintTxt: "Password", ispass: true),
          SizedBox(height: res.height(4)),
          CButton(text: 'Log In', onPressed: () {}),
          SizedBox(height: res.height(2)),
          Center(
            child: TextButton(
              onPressed: () {},
              child: Text(
                'Forgotten your password or login details?',
                style: TextStyle(
                  color: Theme.of(context).colorScheme.tertiary,
                  fontSize: res.fontSize(3),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }

Create another method _buildDesktopLayout here we will code it up for the desktop.

Modify the existing Scaffold to this:

// login_page.dart
import 'package:flutter/material.dart';
import 'package:connect/utils/responsive_helper.dart';
import 'package:connect/features/auth/presentation/components/c_button.dart';
import 'package:connect/features/auth/presentation/components/c_text_field.dart';

class CLoginPage extends StatelessWidget {
  const CLoginPage({super.key});

  @override
  Widget build(BuildContext context) {
    final res = ResponsiveHelper(context);
    final isDesktop = res.width(100) >= 800; // Desktop breakpoint

    return Scaffold(
      backgroundColor: Theme.of(context).scaffoldBackgroundColor,
      body: SafeArea(
        child: Center(
          child: SingleChildScrollView(
            child: isDesktop
                ? _buildDesktopLayout(context, res)
                : _buildMobileLayout(context, res),
          ),
        ),
      ),
    );
  }

  }

Now let’s edit our _buildDesktopLayout method

➡️Click To View _buildDesktopLayout
Widget _buildDesktopLayout(BuildContext context, ResponsiveHelper res) {
    return Row(
      children: [
        // Left side - Image
        Expanded(
          child: Container(
            padding: EdgeInsets.symmetric(
              horizontal: res.width(8),
              vertical: res.height(4),
            ),
            child: Image.asset(
              "assets/images/login_img.png",
              fit: BoxFit.contain,
            ),
          ),
        ),
        // Right side - Login form
        Expanded(
          child: Container(
            padding: EdgeInsets.symmetric(
              horizontal: res.width(10),
              vertical: res.height(4),
            ),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Center(
            child: Text(
              'Welcome Back',
              style: Theme.of(context).textTheme.headline6?.copyWith(
                    fontSize: res.fontSize(3),
                    fontWeight: FontWeight.bold,
                  ),
            ),
          ),
          SizedBox(height: res.height(2)),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                'Not registered yet?',
                style: TextStyle(
                  color: Theme.of(context).colorScheme.tertiary,
                ),
              ),
              TextButton(
                onPressed: () {},
                child: Text(
                  'Sign up',
                  style: TextStyle(
                    color: Theme.of(context).colorScheme.inversePrimary,
                  ),
                ),
              ),
            ],
          ),
          SizedBox(height: res.height(3)),
          const CTextField(hintTxt: "Name", ispass: false),
          SizedBox(height: res.height(2)),
          const CTextField(hintTxt: "Password", ispass: true),
          SizedBox(height: res.height(4)),
          CButton(text: 'Log In', onPressed: () {}),
          SizedBox(height: res.height(2)),
          Center(
            child: TextButton(
              onPressed: () {},
              child: Text(
                'Forgotten your password or login details?',
                style: TextStyle(
                  color: Theme.of(context).colorScheme.tertiary,
                  fontSize: res.fontSize(1),
                ),
              ),
            ),
          ),
              ],
            ),
          ),
        ),
      ],
    );
  }

Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code

If everything goes well your web output should look like this.

login page desktop
Responsive Flutter Login Page with Source Code

We will also need to modify c_button.dart and c_text_field.dart

➡️Click To View c_button.dart
import 'package:flutter/material.dart';
import 'package:connect/utils/responsive_helper.dart';

class CButton extends StatelessWidget {
  final String text;
  final void Function()? onPressed;

  const CButton({super.key, required this.text, required this.onPressed});

  @override
  Widget build(BuildContext context) {
    final res = ResponsiveHelper(context); // Initialize responsive helper
    final isDesktop = res.width(100) >= 800;

    return SizedBox(
      width: isDesktop ? res.width(30) : res.width(100), // 100% of screen width
      child: ElevatedButton(
        onPressed: onPressed,
        style: ElevatedButton.styleFrom(
          backgroundColor: Theme.of(context).colorScheme.primary,
          padding: EdgeInsets.symmetric(vertical: isDesktop ? res.width(1) : res.height(2)), // 2% of screen height
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(isDesktop ? res.width(1) : res.width(3)), // 3% of screen width
          ),
        ),
        child: Text(
          text,
          style: TextStyle(
            color: Colors.black,
            fontSize: isDesktop ? res.fontSize(1) :  res.fontSize(4.5), // 4.5% of screen width
          ),
        ),
      ),
    );
  }
}
➡️Click To View c_text_field.dart
import 'package:flutter/material.dart';
import 'package:connect/utils/responsive_helper.dart';

class CTextField extends StatelessWidget {
  final String hintTxt;
  final bool ispass;

  const CTextField({super.key, required this.hintTxt, required this.ispass});

  @override
  Widget build(BuildContext context) {
    final res = ResponsiveHelper(context); // Initialize responsive helper
    final isDesktop = res.width(100) >= 800;
    
    return TextField(
      obscureText: ispass,
      decoration: InputDecoration(
        hintText: hintTxt,
        hintStyle: TextStyle(
          color: Colors.grey.shade400,
          fontSize: isDesktop ? res.fontSize(1) : res.fontSize(4), // 4% of screen width
        ),
        enabledBorder: UnderlineInputBorder(
          borderSide: BorderSide(color: Colors.grey.shade200),
        ),
        focusedBorder: UnderlineInputBorder(
          borderSide: BorderSide(color: Theme.of(context).colorScheme.inversePrimary),
        ),
      ),
    );
  }
}
➡️Click To View Final login_page.dart
// login_page.dart
import 'package:flutter/material.dart';
import 'package:connect/utils/responsive_helper.dart';
import 'package:connect/features/auth/presentation/components/c_button.dart';
import 'package:connect/features/auth/presentation/components/c_text_field.dart';

class CLoginPage extends StatelessWidget {
  const CLoginPage({super.key});

  @override
  Widget build(BuildContext context) {
    final res = ResponsiveHelper(context);
    final isDesktop = res.width(100) >= 800; // Desktop breakpoint

    return Scaffold(
      backgroundColor: Theme.of(context).scaffoldBackgroundColor,
      body: SafeArea(
        child: Center(
          child: SingleChildScrollView(
            child: isDesktop
                ? _buildDesktopLayout(context, res)
                : _buildMobileLayout(context, res),
          ),
        ),
      ),
    );
  }

  Widget _buildDesktopLayout(BuildContext context, ResponsiveHelper res) {
    return Row(
      children: [
        // Left side - Image
        Expanded(
          child: Container(
            padding: EdgeInsets.symmetric(
              horizontal: res.width(8),
              vertical: res.height(4),
            ),
            child: Image.asset(
              "assets/images/login_img.png",
              fit: BoxFit.contain,
            ),
          ),
        ),
        // Right side - Login form
        Expanded(
          child: Container(
            padding: EdgeInsets.symmetric(
              horizontal: res.width(10),
              vertical: res.height(4),
            ),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Center(
            child: Text(
              'Welcome Back',
              style: Theme.of(context).textTheme.headline6?.copyWith(
                    fontSize: res.fontSize(3),
                    fontWeight: FontWeight.bold,
                  ),
            ),
          ),
          SizedBox(height: res.height(2)),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                'Not registered yet?',
                style: TextStyle(
                  color: Theme.of(context).colorScheme.tertiary,
                ),
              ),
              TextButton(
                onPressed: () {},
                child: Text(
                  'Sign up',
                  style: TextStyle(
                    color: Theme.of(context).colorScheme.inversePrimary,
                  ),
                ),
              ),
            ],
          ),
          SizedBox(height: res.height(3)),
          const CTextField(hintTxt: "Name", ispass: false),
          SizedBox(height: res.height(2)),
          const CTextField(hintTxt: "Password", ispass: true),
          SizedBox(height: res.height(4)),
          CButton(text: 'Log In', onPressed: () {}),
          SizedBox(height: res.height(2)),
          Center(
            child: TextButton(
              onPressed: () {},
              child: Text(
                'Forgotten your password or login details?',
                style: TextStyle(
                  color: Theme.of(context).colorScheme.tertiary,
                  fontSize: res.fontSize(1),
                ),
              ),
            ),
          ),
              ],
            ),
          ),
        ),
      ],
    );
  }

  Widget _buildMobileLayout(BuildContext context, ResponsiveHelper res) {
    return Padding(
      padding: EdgeInsets.all(res.width(6)),
      child: ListView(
        shrinkWrap: true,
        children: [
          Image.asset(
            "assets/images/login_img.png",
          ),
          SizedBox(height: res.height(2)),
          Center(
            child: Text(
              'Welcome Back',
              style: Theme.of(context).textTheme.headline6?.copyWith(
                    fontSize: res.fontSize(7),
                    fontWeight: FontWeight.bold,
                  ),
            ),
          ),
          SizedBox(height: res.height(2)),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                'Not registered yet?',
                style: TextStyle(
                  color: Theme.of(context).colorScheme.tertiary,
                ),
              ),
              TextButton(
                onPressed: () {},
                child: Text(
                  'Sign up',
                  style: TextStyle(
                    color: Theme.of(context).colorScheme.inversePrimary,
                  ),
                ),
              ),
            ],
          ),
          SizedBox(height: res.height(3)),
          const CTextField(hintTxt: "Name", ispass: false),
          SizedBox(height: res.height(2)),
          const CTextField(hintTxt: "Password", ispass: true),
          SizedBox(height: res.height(4)),
          CButton(text: 'Log In', onPressed: () {}),
          SizedBox(height: res.height(2)),
          Center(
            child: TextButton(
              onPressed: () {},
              child: Text(
                'Forgotten your password or login details?',
                style: TextStyle(
                  color: Theme.of(context).colorScheme.tertiary,
                  fontSize: res.fontSize(3),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Tomorrow we will continue with the register page.

And we will be implementing login functionality through our demo user.

With a creative loading screen. And that’s all I can think of now.

How do you feel watching this app getting to life???

Challenge

I have a simple challenge for you guys to give me cool and interesting ideas for the loading screen to boost engagement on the app.

This is the first challenge so I wanted to keep it simple. But its not as simple as it sounds…

Let me know your ideas I’ll see you tomorrow.

Till then stay happy and keep coding.

Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code Responsive Flutter Login Page with Source Code

Leave a Reply