Skip to content

Merge feature/improve-login-page-with-tests-tur-blackbox into master#8

Open
busratuter wants to merge 1 commit intoJideGuru:masterfrom
busratuter:feature/improve-login-page-with-tests-tur-blackbox
Open

Merge feature/improve-login-page-with-tests-tur-blackbox into master#8
busratuter wants to merge 1 commit intoJideGuru:masterfrom
busratuter:feature/improve-login-page-with-tests-tur-blackbox

Conversation

@busratuter
Copy link
Copy Markdown

Automated PR created from task completion


This PR was created from task: https://cloud.blackbox.ai/tasks/Wax1OcnLmL5p8zqhi4F8j

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @busratuter, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the user authentication and registration experience by introducing a new, dedicated login screen and thoroughly refactoring the existing registration form. It centralizes input validation logic into a reusable utility and improves custom form widgets for better state management and user interaction. The changes are backed by a comprehensive suite of unit tests, ensuring the robustness and correctness of the updated components.

Highlights

  • New Login Screen: A dedicated login screen (lib/screens/login.dart) has been introduced, providing a user interface for authentication with email and password fields, 'Remember me' functionality, and navigation to the registration page.
  • Enhanced Registration Form: The existing registration form (lib/screens/home.dart) has been refactored to be a StatefulWidget, incorporating form validation, TextEditingControllers for input fields, and explicit save/cancel actions. It now uses the new Validators utility for input checks.
  • Robust Input Validation: A new utility file (lib/utils/validators.dart) has been added, centralizing common input validation logic for email, password, phone numbers, names, and customer IDs, ensuring data integrity across forms.
  • Improved Custom Widgets: The Gender, Membership, and InputField widgets have been upgraded to be StatefulWidgets or accept controllers/callbacks, allowing for better state management and interaction. A new PasswordField widget was also added with a visibility toggle.
  • Comprehensive Unit Tests: Extensive unit tests have been added for the new login screen, the refactored registration screen, the Validators utility, and all custom form widgets (Gender, Membership, InputField, PasswordField), significantly improving code coverage and reliability.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request significantly improves the application by refactoring the registration page into a stateful widget, adding a new login page, implementing form validation, and introducing a comprehensive suite of tests. The use of named routes and modern Flutter practices like const constructors and updated buttons is excellent. My review focuses on improving null safety, enhancing widget reusability, fixing a few minor bugs in both the application and test code, and addressing some maintainability and accessibility concerns. Overall, this is a high-quality contribution.

Comment thread lib/screens/login.dart
}

void _handleLogin() {
if (_formKey.currentState!.validate()) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using the bang operator ! on _formKey.currentState can lead to a runtime crash if the form state is unexpectedly null. It's safer to use a null-aware access ?. with a null check to prevent this.

Suggested change
if (_formKey.currentState!.validate()) {
if (_formKey.currentState?.validate() ?? false) {

Comment thread lib/screens/home.dart
}

void _handleSave() {
if (_formKey.currentState!.validate()) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using the bang operator ! on _formKey.currentState can lead to a runtime crash if the form state is unexpectedly null. It's safer to use a null-aware access ?. with a null check to prevent this.

Suggested change
if (_formKey.currentState!.validate()) {
if (_formKey.currentState?.validate() ?? false) {

Comment thread lib/screens/home.dart
),
child: Form(
key: _formKey,
child: Column(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This registration form is missing fields for password creation and confirmation. A user registration process should allow the user to set a password. Consider adding password and confirm password fields. You could use the PasswordField widget that has been created in lib/widgets/password_field.dart but is currently unused.

Comment thread lib/utils/validators.dart
Comment on lines +7 to +37
final emailRegex = RegExp(
r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
);

if (!emailRegex.hasMatch(value)) {
return 'Please enter a valid email';
}

return null;
}

static String? validatePassword(String? value) {
if (value == null || value.isEmpty) {
return 'Password is required';
}

if (value.length < 6) {
return 'Password must be at least 6 characters';
}

return null;
}

static String? validatePhone(String? value) {
if (value == null || value.isEmpty) {
return 'Phone number is required';
}

final phoneRegex = RegExp(r'^\+?[0-9]{10,15}$');

if (!phoneRegex.hasMatch(value.replaceAll(RegExp(r'\s+'), ''))) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Creating a RegExp instance inside a function means it will be recompiled on every call. For better performance, especially in a validator that might be called frequently, define this RegExp as a top-level final variable outside the class so it's compiled only once.

Comment thread lib/screens/login.dart
Comment on lines +3 to +4
import 'package:login_page/widgets/input_field.dart';
import 'package:login_page/widgets/password_field.dart';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

These imports for input_field.dart and password_field.dart are not used in this file. They should be removed to keep the code clean and reduce clutter.

Comment on lines +33 to 35
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width / 3.7,
),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Hardcoding the maxWidth with MediaQuery.of(context).size.width / 3.7 makes this InputField widget less reusable, as it's tightly coupled to the layout of the screen where it's currently used. It's generally better to let the parent widget control the size and layout. The Expanded widget is already handling the space. Consider removing these constraints to make the widget more flexible.

Comment on lines +44 to +48
borderSide: BorderSide(color: Colors.blue[50]!),
borderRadius: BorderRadius.circular(5.0),
),
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.blue[50],
),
borderRadius: BorderRadius.circular(5.0),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.blue[50],
),
borderRadius: BorderRadius.circular(5.0),
),
hintText: "$content",
fillColor: Colors.blue[50],
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue[50]!),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using the bang operator ! on Colors.blue[50] is risky. While it's unlikely to be null for a MaterialColor, it's safer to provide a fallback value to prevent potential crashes, for example Colors.blue[50] ?? Colors.grey.

Suggested change
borderSide: BorderSide(color: Colors.blue[50]!),
borderRadius: BorderRadius.circular(5.0),
),
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.blue[50],
),
borderRadius: BorderRadius.circular(5.0),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.blue[50],
),
borderRadius: BorderRadius.circular(5.0),
),
hintText: "$content",
fillColor: Colors.blue[50],
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue[50]!),
borderSide: BorderSide(color: Colors.blue[50] ?? Colors.grey),
borderRadius: BorderRadius.circular(5.0),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue[50] ?? Colors.grey),

Comment on lines +28 to +58
Widget _buildMembershipOption(String type, String label) {
final isSelected = _selectedMembership == type;
return Row(
mainAxisSize: MainAxisSize.min,
children: [
InkWell(
onTap: () {
setState(() {
_selectedMembership = type;
});
widget.onChanged?.call(type);
},
child: CircleAvatar(
backgroundColor: isSelected ? Colors.blue[300] : Colors.blue[50],
child: Icon(
Icons.account_balance_wallet,
color: isSelected ? Colors.white : Colors.grey,
),
],
);
},
),
),
const SizedBox(width: 10.0),
Container(
width: 50.0,
child: Text(
label,
textAlign: TextAlign.left,
),
),
],
);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to the Gender widget, the InkWell only wraps the CircleAvatar. For better usability, the tappable area should include the text label as well. Consider wrapping the entire Row returned by _buildMembershipOption in the InkWell.

Comment on lines +36 to +68
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width / 3.7,
),
child: TextFormField(
controller: widget.controller,
obscureText: _obscureText,
validator: widget.validator,
style: const TextStyle(fontSize: 15.0),
decoration: InputDecoration(
contentPadding: const EdgeInsets.all(10.0),
border: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue[50]!),
borderRadius: BorderRadius.circular(5.0),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue[50]!),
borderRadius: BorderRadius.circular(5.0),
),
filled: true,
fillColor: Colors.blue[50],
suffixIcon: IconButton(
icon: Icon(
_obscureText ? Icons.visibility : Icons.visibility_off,
color: Colors.grey,
),
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
),
),
),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This widget has a couple of areas for improvement:

  1. Reusability: The hardcoded maxWidth constraint (MediaQuery.of(context).size.width / 3.7) makes the widget less reusable. It's better to let the parent widget control the width.
  2. Robustness: Using the ! operator on Colors.blue[50] is risky. Provide a fallback color to be safe.

Also, note that this widget is currently not used anywhere in the project. It should either be integrated (e.g., into the registration form) or removed.


// Find email field and enter invalid email
final emailFields = find.byType(TextFormField);
await tester.enterText(emailFields.at(3), 'invalid-email');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This test for invalid email format seems to be targeting the wrong field. emailFields.at(3) corresponds to the 'Mobile' field, not the 'Email' field, which is at index 2. This will cause the test to pass incorrectly or fail for the wrong reason.

Suggested change
await tester.enterText(emailFields.at(3), 'invalid-email');
await tester.enterText(emailFields.at(2), 'invalid-email');

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants