### Implement FormBlocListener for Form State Management Source: https://context7.com/softonixmobile/stx_flutter_form_bloc/llms.txt This example demonstrates how to define a `FormBloc` and use `FormBlocListener` to handle different form submission states like submitting, success, failure, and cancellation. It shows how to display a loading dialog and show snackbars for user feedback. ```dart import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:stx_flutter_form_bloc/stx_flutter_form_bloc.dart'; // 1. Define your FormBloc class LoginFormBloc extends FormBloc { final TextFieldBloc emailField = TextFieldBloc(); final TextFieldBloc passwordField = TextFieldBloc(); LoginFormBloc() { addFieldBlocs(fieldBlocs: [emailField, passwordField]); } @override Future onSubmitting() async { try { // Simulate an API call await Future.delayed(const Duration(seconds: 2)); if (emailField.value == 'user@example.com') { emitSuccess(successResponse: 'Welcome back!'); } else { emitFailure(failureResponse: 'Invalid credentials.'); } } catch (_) { emitFailure(failureResponse: 'Network error.'); } } } // 2. Use FormBlocListener in your widget tree class LoginScreen extends StatelessWidget { const LoginScreen({super.key}); @override Widget build(BuildContext context) { return BlocProvider( create: (_) => LoginFormBloc(), child: Builder( builder: (context) { final formBloc = context.read(); return FormBlocListener( formBloc: formBloc, onSubmitting: (context, state) { // Show loading overlay when form is submitting LoadingDialog.show(context); }, onSuccess: (context, state) { LoadingDialog.hide(context); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(state.successResponse ?? 'Success!')), ); Navigator.of(context).pushReplacementNamed('/home'); }, onFailure: (context, state) { LoadingDialog.hide(context); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.failureResponse ?? 'Something went wrong.'), backgroundColor: Colors.red, ), ); }, onCancel: (context, state) { LoadingDialog.hide(context); }, child: Scaffold( appBar: AppBar(title: const Text('Login')), body: Padding( padding: const EdgeInsets.all(16), child: Column( children: [ TextFieldBlocBuilder(textFieldBloc: formBloc.emailField), TextFieldBlocBuilder( textFieldBloc: formBloc.passwordField, suffixButton: SuffixButton.obscureText, ), ElevatedButton( onPressed: formBloc.submit, child: const Text('Login'), ), ], ), ), ), ); }, ), ); } } ``` -------------------------------- ### Show Loading Dialog with LoadingDialog.show() Source: https://context7.com/softonixmobile/stx_flutter_form_bloc/llms.txt Presents a non-dismissible modal dialog with a centered CircularProgressIndicator. It uses an internal guard to prevent duplicate dialogs. ```dart ElevatedButton( onPressed: () { context.read().submit(); // Manually trigger the loading dialog (if not using FormBlocListener) LoadingDialog.show(context); }, child: const Text('Submit'), ) ``` -------------------------------- ### Define and Use Reusable FormBlocListener Callbacks Source: https://context7.com/softonixmobile/stx_flutter_form_bloc/llms.txt A convenience typedef for `FormBlocListener` callbacks. Define reusable callbacks outside the widget tree for cleaner code. ```dart // The typedef resolves to: // void Function(BuildContext context, FormBlocState state) // Defining reusable callbacks outside the widget tree: FormBlocListenerCallback handleSuccess = (BuildContext context, FormBlocState state) { LoadingDialog.hide(context); final message = state.successResponse ?? 'Operation completed.'; ScaffoldMessenger.of(context) .showSnackBar(SnackBar(content: Text(message))); }; FormBlocListenerCallback handleFailure = (BuildContext context, FormBlocState state) { LoadingDialog.hide(context); final error = state.failureResponse ?? 'An error occurred.'; showDialog( context: context, builder: (_) => AlertDialog( title: const Text('Error'), content: Text(error), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('OK'), ) ], ), ); }; // Reuse in any FormBlocListener: FormBlocListener( onSuccess: handleSuccess, onFailure: handleFailure, child: myFormWidget, ) ``` -------------------------------- ### Hide Loading Dialog with LoadingDialog.hide() Source: https://context7.com/softonixmobile/stx_flutter_form_bloc/llms.txt Programmatically dismisses the loading dialog and resets the `loaderShown` guard. Safe to call even if no dialog is displayed. ```dart onSuccess: (context, state) { LoadingDialog.hide(context); // Dismisses dialog, resets loaderShown flag Navigator.of(context).pushReplacementNamed('/dashboard'); }, onFailure: (context, state) { LoadingDialog.hide(context); // Safe no-op if dialog was never shown ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(state.failureResponse ?? 'Error')), ); }, ``` -------------------------------- ### Custom FormBlocListener Logic with customListener Source: https://context7.com/softonixmobile/stx_flutter_form_bloc/llms.txt Use `customListener` for full manual control over all state transitions when built-in callbacks are insufficient. This overrides all named callbacks. ```dart FormBlocListener( formBloc: context.read(), customListener: (context, state) { // Full manual control over all state transitions switch (state.status) { case FormBlocStatus.submitting: debugPrint('Form is submitting...'); LoadingDialog.show(context); break; case FormBlocStatus.success: LoadingDialog.hide(context); debugPrint('Success: ${state.successResponse}'); break; case FormBlocStatus.failure: LoadingDialog.hide(context); debugPrint('Failure: ${state.failureResponse}'); break; case FormBlocStatus.cancelled: LoadingDialog.hide(context); debugPrint('Submission cancelled.'); break; default: break; } }, child: const SizedBox.shrink(), ) ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.