flutter_animated_login 1.0.1
flutter_animated_login: ^1.0.1 copied to clipboard
A Flutter package to create a beautiful animated login screen with phone/email otp, password, and social login options. It also includes a phone number with country code picker.
Changelog #
All notable changes to this project are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
1.0.1 - 2026-09-13 #
Fixed #
- Changing the country from code no longer reports an error. With
formatInputon,controller.prefill(countryIsoCode: ...),reset()to a different initial country, or a rebuild that turnsformatInputon or off, logged "setState() or markNeedsBuild() called during build" in debug builds while the phone field reformatted the number. So did prefilling an international number (+1...) while the phone field was shown. - A success dismisses an error shown by an earlier copy of a screen. A
wrong code, then Edit, Continue and the right code left "That code is
incorrect" on screen, because each screen tracked only the errors it had
shown itself. The screens of a
FlutterAnimatedLoginnow share one tracker. - No flash of the empty form before a late redirect. When the app moves
to its home screen shortly after
onLogin,onSignup,onResetPasswordoronVerifyreturns, for example from an auth listener, the flow used to reset first and the outgoing route cross-faded back to an empty form. It now waits 150 ms for the app to navigate before resetting. While it waits the form stays busy, so it cannot be submitted again, and a step the app opens in the meantime (signup for a new user, say) is kept. - The keyboard's action key no longer submits twice. Pressing Done again
while
onLogin,onSignuporonResetPasswordwas still running called it a second time; only the button was disabled.
1.0.0 - 2026-09-13 #
The flow's state moved out of process-wide globals and into a per-instance controller you can drive yourself, the email field became a real email field, and every user-facing string became overridable. See MIGRATION.md for an upgrade guide — most apps need only a dependency bump.
Breaking #
- Minimum SDK is Dart 3.7 / Flutter 3.29. The old floor of 3.10 could
never have compiled: the package already called
Color.withValues, which arrived in 3.27. 3.29 is where the rest of what it needs lands —TextFormField.stylusHandwritingEnabled(3.27 deprecatedscribbleEnabledwithout shipping the replacement) andTapRegionUpCallback, which pinput 6 uses and so requires despite declaring">=3.7.0"itself. A CI leg builds on exactly this floor so it cannot drift again. flutter_intl_phone_fieldmoved from 0.0.7 to 0.1.x, and this package re-exports it. Its breaking changes reach you directly:PhoneNumberis immutable,PhoneNumber.countryCodealways carries a leading+,Country.dialCodeno longer contains the area code for +1 territories, 117 territories' accepted number lengths were corrected against libphonenumber, andCountryPickerDialogwas replaced byshowCountryPicker. See that package's own migration guide.pinputmoved from 5.x to 6.x, also re-exported.signalsis no longer a dependency. It backed five module-level globals and pulled in five transitive packages (signals_core,signals_flutter,signals_hooks,preact_signals,flutter_hooks) to hold four booleans, and its 7.x line requires Dart 3.5. Nothing in the public API exposed it, but it is no longer available transitively.SignupData.additionalSignupDatais non-nullable (Map<String, String>, defaulting toconst {}) and no longer carriesconfirmPassword. That value is a check on the form, not data your backend asked for; setSignupConfig.includeConfirmPasswordInData: trueto get it back.SignupConfig.textFiledConfig,SignupConfig.passwordTextFiledConfigandResetConfig.textFiledConfigare now nullable and default tonull, meaning "reuse the login screen's". They were previously declared non-null and then unconditionally overwritten, so they could never take effect.EmailPhoneTextFiledConfigdefault changes.stylewasTextStyle(fontSize: 16)and is nownull(inherit the theme);textAlignwasTextAlign.leftand is nowTextAlign.start, which follows the ambient text direction;invalidMessagewas'Invalid Mobile Number'and is nownull, falling back toFormMessages.invalidPhoneNumber.- The screen widgets
FlutterAnimatedVerify,FlutterAnimatedSignupandFlutterAnimatedResettake acontrollerinstead of loose controllers and form keys. They are internal to the flow; useFlutterAnimatedLogin.
Fixed #
- The login flow no longer leaks state between instances or across
navigations. The page index, the identifier, the phone/email mode, the
form-validity flag and the submit spinner were module-level
signals that nodisposeever reset. Pushing the login route, tapping through to the OTP screen and popping left the globals set, so the next push opened on the OTP screen — showing, and on resend re-texting, the previous user's phone number. Two login widgets alive at once shared one page index. All of this state is now per-instance, insideFlutterAnimatedLoginController. - Email sign-in works again. The email field was an
IntlPhoneFieldwith its country selector hidden. Since flutter_intl_phone_field 0.1.0 reduces that field's value to digits, an email address came back as the empty string: the submit button never enabled, the validator reported "please enter a phone number" under a valid address, and a controller pre-seeded with an email was wiped on first build. Email mode is now a realTextFormField. - The signup and reset-password screens are reachable again. Their only
entry point rendered inside
if (!isLoginWithOTP), so with the defaultLoginType.otpno user could ever get to them, and there was no programmatic route either. - A custom
FormMessagesreaches every screen.LoginConfig.copyWithsilently droppedmessagesandloginFieldInputType, and the package callscopyWithinternally on the signup and reset screens — so translations reverted to English exactly where they were passed through.EmailPhoneTextFiledConfig.copyWithlikewise reset a customstyletoTextStyle(fontSize: 16)on every call and discardedsearchText. - The password reveal toggle stays where you put it. Its
ValueNotifierwas constructed insidebuild, so the field snapped back to obscured on any rebuild — a window resize, a rotation, the Android keyboard opening — and leaked a notifier every time. - The submit button no longer enables with an empty password. The identifier field overwrote the shared validity flag with a value computed from the identifier alone, clobbering the password requirement.
PageConfig.colorsaccepts any number of colours.GradientBoxhardcodedstops: const [0, 1], so a list that was not exactly two colours long threwArgumentErrorat paint time — in release builds too.- The signup screen honours
PageConfig. It required one and never used it, so the background, card decoration and padding reverted to defaults on that screen alone. - Pasting or autofilling an international phone number no longer corrupts
it. The field passed
inputFormatters: const []rather thannull, which replaced the phone field's entire default chain — country-detection on paste, digit filtering, the per-country length cap and as-you-type formatting.+919876543210could be submitted as+91+919876543210. - Configurable OTP lengths work.
initStatehardcoded 6 in three places while the field honouredOtpTextFiledConfig.length, so a 4-digit code never auto-submitted and an 8-digit one was truncated to 6. - The one-time-code screen no longer leaks a
TapGestureRecognizerper second.TitleWidgetallocated one inbuild, and that screen rebuilds once a second for its countdown. - No more crash after navigating away mid-verify. The success path touched
the controller and the
BuildContextafterawaitwith nomountedguard. - Caller-supplied controllers are no longer disposed by the package. Ownership is now explicit: a controller you pass in is yours to dispose, and only what the package created does it dispose.
- The signup button can no longer double-submit. Its loading flag was
cleared in a
finallybut never set, so the spinner cancelled mid-request. - The package no longer nulls your
SignalsObserver, because it no longer uses signals at all. It used to set the app-wide observer tonullininitStateand never restore it, silently killing signals DevTools for the whole app. - The email validator accepts real addresses. The old pattern
(
[\w-]{2,4}for the TLD) rejected plus-addressing, every TLD longer than four letters, and non-ASCII addresses, leaving the submit button disabled with no explanation. - Status messages no longer throw without a
ScaffoldMessenger. They usemaybeOfand warn in debug instead. - Navigating during a screen transition no longer throws. Each screen
built its own
Scaffold, so while one cross-faded into the next, two Scaffolds in the same route both drew the current SnackBar under one Hero tag. Navigating in that window — go_router'scontext.goor an auth redirect straight after an error, for instance — threw "There are multiple heroes that share the same tag within a subtree". The flow now has a single Scaffold outside the transition. - Phone numbers read correctly in right-to-left layouts. The phone field
inherited RTL, so the bidi algorithm reordered its digit groups: the UAE
number
50 123 4567displayed as4567 123 50. Phone numbers and email addresses are now always laid out left to right, in the form and on the code screen. - "Resend OTP" only appears when there is something to resend with. Without
onResendOtpthe button showed anyway, and tapping it restarted the countdown as though a new code were on its way. - A success dismisses the error it follows. Entering a wrong code and then the right one left "Error" on screen over the app's home page for up to four seconds. Only the package's own error is dismissed; a message your callback shows is left alone.
- An international number typed into the default field keeps its country.
Typing
+44 7700 900123into the email-or-phone field switched to the phone field at+4on the default country and treated the rest as national digits; 0.0.15 sent+1+447700900. The field now waits for the calling code and selects that country, soonLoginreceives+447700900123. EmailPhoneTextFieldConfig.initialValueseeds the field once. It was handed to the phone field, which re-applied it every time it was rebuilt, so a number the user had typed was replaced by the initial value again. It is now ignored when you pass your own controller, as documented.- No flash of the login form after a successful sign-in. When the app
navigated to its home screen from
onVerifyoronLogin, the package still reset its own screen behind the outgoing route, which visibly cross-faded back to an empty form. It now skips the reset once the app has navigated away. SignupDatacompares by value. Itsoperator ==compared theadditionalSignupDatamaps by identity, so two payloads carrying the same entries were never equal.LoginData.toString()andSignupData.toString()redact the secret. Logging one used to print the password or the one-time code in clear.
Added #
FlutterAnimatedLoginController— drive the flow from outside:goTo,showOtp,reset,prefill,setBusy,clearOtp, and readstep,identifier,isPhone,phoneNumber,isFormValid. Optional: one is created and disposed internally when you pass none.- Extra signup fields (GitHub
#8) —
SignupConfig.additionalFieldstakes a list ofSignupFields, andSignupConfig.customFieldstakes builders for anything a text field cannot express. Values arrive inSignupData.additionalSignupData. LoginType.otpAndPasswordis now implemented. It renders a password field plus a link that switches to the one-time-code path, andLoginData.methodtells you which ran. It previously showed a code-only screen with no password field, and on success cleared the form rather than opening the code screen.PasswordPolicywithminLength,maxLength, character-class rules,disallowpatterns and aminStrengthbar, plusPasswordPolicy.standard()and.strict(). Composes with your ownvalidatorrather than replacing it.- Password strength meter, requirement checklist and caps-lock warning —
PasswordTextFiledConfig.showStrengthMeter,showRequirementChecklist,showCapsLockHintandstrengthBuilder. Caps lock is read fromHardwareKeyboard, so it needs no plugin. ConsentConfig— a checkbox that gates the submit button, reported back onLoginData.acceptedTermsandSignupData.acceptedTerms.AnimatedLoginTheme, aThemeExtension— brand every screen in one place, including the page transition's duration, curve and builder. Per-config properties still win over it.ProviderLayout(iconRow,iconWrap,fullWidthStacked) and a richerLoginProvider:iconWidgetfor a brand mark,buttonfor a fully custom button,backgroundColor,foregroundColorandsemanticLabel. Apple's and Google's sign-in guidelines need a labelled full-width button with their own mark, which the icon-only API could not produce.LoginProvider.errorsToExcludeFromErrorMessageandproviderNeedsSignUpCallbacknow work. Both were declared, documented and never read.FormMessagescovers every user-facing string — it grew from 17 fields to 60, absorbing the 26 English literals that were hardcoded in the screens and the country picker's strings, and gainedcopyWithand afallbackconstant.- Configurable OTP resend —
VerifyConfig.resendCooldown(was hardcoded to 60 seconds),startCooldownOnOpen,maxResendAttempts,countdownBuilder,onCooldownFinishedandautoSubmitOnFill. - Back-navigation handling — the Android system back button and the browser
back button return to the login screen from the OTP, signup and reset
screens instead of leaving the flow. Opt out with
handleBackNavigation: false. onStepChangedso the host can react to screen transitions.PageConfig.useScaffold— set false whenFlutterAnimatedLoginalready sits inside your ownScaffold, which is the common case and used to nest one inside another. PlususeSafeArea,scrollPhysics,pageHeaderandpageFooter.LoginConfig.showSignupLink/showForgotLinkto control the secondary links independently of the login type, andproviderLayout,providerSpacingandfieldGap.- flutter_intl_phone_field 0.1.x capabilities are now reachable through
EmailPhoneTextFiledConfig:favoriteCountries,onlyCountries,excludeCountries,formatInput,showExampleAsHint,detectCountryOnPaste,initialValueFormat,strictValidation,flagShape,flagSize,flagBuilder,dialCodeBuilder,countrySelectorBuilder,phoneController,showCountryCode,onTapOutsideandrestorationId. - pinput 6 capabilities —
OtpTextFiledConfig.onTapUpOutside,showErrorWhenFocused,enableInteractiveSelectionandsemanticLabel. LoginData.method,LoginData.phoneNumber,LoginData.acceptedTerms,copyWithandconstconstructors on both data classes.- Accessibility — semantic labels on the social buttons and the one-time-code
field, tooltips on the password reveal toggle, 48×48 minimum touch targets,
live regions on status messages and the countdown, and
AutofillGrouparound every form so password managers offer to save. - A test suite. The package previously had none.
Changed #
- Every screen owns its own
FormandGlobalKey<FormState>. One key used to wrap the whole switcher, so during a 300 ms page transition two screens' fields lived in the sameFormState. - Only the visible screen is built; all four used to be constructed on every frame.
- Buttons are full-width up to 400 logical pixels rather than pinned to half
the viewport, and the secondary links and social buttons use
Wrap, so neither overflows at large text scales. - The default card shadow is a soft themed shadow rather than opaque black at a 100-pixel blur.
- Status messages follow the theme instead of hardcoded
Colors.green.shade400and friends. PasswordTextFiledConfig.scribbleEnabledis nowstylusHandwritingEnabled, matching Flutter 3.29. The old name still works and is deprecated.
Deprecated #
Everything below still works and is scheduled for removal in 2.0.0:
FlutterAnimatedLogin.debug— signals are gone, so there is no observer left to toggle. It does nothing.LoginConfig.termsAndConditionsandLoginConfig.privacyPolicy(bothString?) — never read. UseFlutterAnimatedLogin.termsAndConditions(aWidget) orFlutterAnimatedLogin.consent.SignupConfig.resendButtonandResetConfig.resendButton— neither screen has a resend button; the fields were copied fromVerifyConfig.EmailPhoneTextFiledConfig.searchText— useFormMessages.searchCountry.PasswordTextFiledConfig.scribbleEnabled— usestylusHandwritingEnabled.
Packaging #
- The published archive is roughly 600 KB instead of 31 MB. A 32 MB demo
video sat in the repository root with no
.pubignore, so every consumer downloaded it on everypub get. The video and the six 0.0.x screenshots are gone from the repository, replaced by WebP captures of 1.0.0 inscreenshots/, including an animated demo. - A rewritten README, with a screenshot gallery, the callback contract, recipes, troubleshooting, and copy-paste prompts for AI coding assistants — each prompt run on a fresh app before it was published.
homepage,repository,issue_trackeranddocumentationpoint atitsarvinddev, where the repository actually lives; the oldrvndsngwnURLs 404ed. Addedfunding:.- CI now runs. It triggered on pushes to a
masterbranch that does not exist, asserted 120-column formatting against 80-column code, and had its test step commented out.
0.0.15 2025 #
- dependencies updated
0.0.14 2025 #
- rename stylusHandwritingEnabled to scribbleEnabled for legacy support
0.0.13 2025 #
- dependencies updated
- initial values supported
- extra dependencies removed
0.0.12 2025 #
- dependencies updated
- SMS autofill on Android now requires adding
smart_auth(or similar) to your own project — see pinput's migration guide
0.0.11 #
textInputActionadded for all text fieldsonSubmittedadded for all text fields
0.0.10 #
- state reset added for login and signup
0.0.9 #
- signals disposed error fixed
0.0.8 #
- fixed multiple widgets using the same
GlobalKey
0.0.7 #
- login with password added
- reset password screen added
- signup screen added
0.0.6 #
- OTP verification handling added
0.0.5 #
LoginDataclass added for handling OTP functionality
0.0.4 #
- title widget added with custom style
0.0.3 #
PageConfigadded for customizing page layout and stylepinputandflutter_intl_phone_fieldre-exported
