Created
November 4, 2024 06:58
-
-
Save logickoder/b0f838c0b2872d8863f2ed8780762b83 to your computer and use it in GitHub Desktop.
Multi Progress Indicator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
/// A progress indicator that is divided into sections, each section represents a part of the total progress | |
/// The current position of the progress indicator is represented by the [current] property | |
/// The [sections] property represents the sections of the progress indicator | |
/// The length of the [sections] list represents the total number of sections | |
class MultiProgressIndicator extends StatelessWidget { | |
/// The current position of the progress indicator in relation to the sections | |
final int current; | |
/// Represents the sections of the progress indicator, each section is a part of the total progress | |
final List<int> sections; | |
/// Whether the progress indicator should fill the available space | |
final bool shouldFill; | |
const MultiProgressIndicator({ | |
super.key, | |
required this.current, | |
required this.sections, | |
this.shouldFill = false, | |
}); | |
@override | |
Widget build(BuildContext context) { | |
return Row( | |
children: [ | |
for (var i = 0; i < sections.length; ++i) ...{ | |
Expanded( | |
flex: shouldFill ? 1 : 0, | |
child: _buildSection(i, context), | |
), | |
if (i < sections.length - 1) ...{ | |
const SizedBox(width: 8), | |
}, | |
}, | |
], | |
); | |
} | |
Widget _buildSection(int index, BuildContext context) { | |
final section = sections[index]; | |
// the total count of all previous sections | |
final total = sections.sublist(0, index).fold(0, (p, e) => p + e); | |
return TweenAnimationBuilder( | |
tween: Tween<double>( | |
begin: 0, | |
end: current <= total | |
? 0 | |
: current >= total + section | |
? 1 | |
: (current - total) / section, | |
), | |
duration: Durations.medium1, | |
builder: (_, progress, __) { | |
return SizedBox( | |
height: 3, | |
width: 50, | |
child: LinearProgressIndicator( | |
value: progress, | |
backgroundColor: Colors.grey, | |
valueColor: const AlwaysStoppedAnimation(Colors.green), | |
borderRadius: BorderRadius.circular(3), | |
), | |
); | |
}, | |
); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'multi_progress_indicator.dart'; | |
class PasswordStrength extends StatelessWidget { | |
final String password; | |
const PasswordStrength(this.password, {super.key}); | |
@override | |
Widget build(BuildContext context) { | |
final current = [ | |
hasLowercase, | |
hasUppercase, | |
hasDigits, | |
hasSpecialCharacters, | |
hasMinLength | |
].where((e) => e).length; | |
const sections = [2, 2, 1]; | |
String strengthText; | |
Color strengthColor; | |
if (current >= 4) { | |
strengthText = 'strong'; | |
strengthColor = Colors.green; | |
} else if (current >= 2) { | |
strengthText = 'fair'; | |
strengthColor = Colors.yellow; | |
} else { | |
strengthText = 'weak'; | |
strengthColor = Colors.red; | |
} | |
return Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
MultiProgressIndicator( | |
current: current, | |
sections: sections, | |
), | |
const SizedBox(height: 12), | |
AnimatedSwitcher( | |
duration: Durations.medium1, | |
child: RichText( | |
key: ValueKey(strengthText), | |
text: TextSpan( | |
text: 'Password strength: ', | |
style: const TextStyle( | |
fontSize: 12, | |
color: Colors.black12, | |
fontWeight: FontWeight.w600, | |
), | |
children: [ | |
TextSpan( | |
text: strengthText, | |
style: TextStyle( | |
color: strengthColor, | |
), | |
), | |
], | |
), | |
), | |
), | |
const SizedBox(height: 12), | |
_PasswordCheck( | |
text: 'At least 1 upper and lower case letters.', | |
isChecked: hasUppercase && hasLowercase, | |
), | |
const SizedBox(height: 8), | |
_PasswordCheck( | |
text: 'At least 1 number and 1 special character.', | |
isChecked: hasDigits && hasSpecialCharacters, | |
), | |
const SizedBox(height: 8), | |
_PasswordCheck( | |
text: 'At least 8 characters', | |
isChecked: hasMinLength, | |
), | |
], | |
); | |
} | |
bool get hasLowercase => RegExp(r'[a-z]').hasMatch(password); | |
bool get hasUppercase => RegExp(r'[A-Z]').hasMatch(password); | |
bool get hasDigits => RegExp(r'[0-9]').hasMatch(password); | |
bool get hasSpecialCharacters => | |
RegExp(r'[!@#$%^&*(),.?":{}|<>]').hasMatch(password); | |
bool get hasMinLength => password.length >= 8; | |
} | |
class _PasswordCheck extends StatelessWidget { | |
final String text; | |
final bool isChecked; | |
const _PasswordCheck({ | |
required this.text, | |
required this.isChecked, | |
}); | |
@override | |
Widget build(BuildContext context) { | |
return Row( | |
children: [ | |
AnimatedSwitcher( | |
duration: Durations.medium1, | |
child: Icon( | |
key: ValueKey(isChecked), | |
isChecked | |
? Icons.check_circle_outline_outlined | |
: Icons.circle_outlined, | |
color: isChecked ? Colors.green : Colors.black12, | |
), | |
), | |
const SizedBox(width: 8), | |
Text( | |
text, | |
style: const TextStyle( | |
fontSize: 12, | |
color: Colors.black12, | |
), | |
), | |
], | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
1000423918.mp4