Debug School

rakesh kumar
rakesh kumar

Posted on

How to display a loader until your asynchronous operations complete IN Flutter

It seems like you're asking how to display a loader while processing the initial values for the TextFormField widgets inside your FormWidget based on conditions. To achieve this, you can utilize the FutureBuilder widget to show a loader until your asynchronous operations complete. Here’s how you can modify your code:

Add a FutureBuilder: Wrap the part of your code that depends on asynchronous initializations with a FutureBuilder.

Display Loader: Display a loader (e.g., CircularProgressIndicator) while waiting for the async operations to complete.

Here’s an updated version of your _FormWidgetState with these changes:

import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:provider/provider.dart';
import 'package:flutter_application_1/view_modal/form_view_model.dart';
import '../../view_modal/influencer_view_model.dart';

class FormWidget extends StatefulWidget {
  final String title;
  final List<String> fieldLabels;
  final String? selectedCurrency;
  final List<String>? socialSite;

  FormWidget({
    required this.title,
    required this.fieldLabels,
    this.selectedCurrency,
    this.socialSite,
  });

  @override
  _FormWidgetState createState() => _FormWidgetState();
}

class _FormWidgetState extends State<FormWidget> {
  final _formKey = GlobalKey<FormState>();
  final Map<String, String> _formData = {};
  String? _selectedCurrency;
  bool _isLoggedIn = false;
  String? _email;

  @override
  void initState() {
    super.initState();
    _selectedCurrency = widget.selectedCurrency;
  }

  Future<Map<String, String?>> _getCredentials() async {
    final secureStorage = FlutterSecureStorage();
    String? email = await secureStorage.read(key: 'email');
    String? password = await secureStorage.read(key: 'password');
    return {'email': email, 'password': password};
  }

  String extractSocialSite(String label) {
    final RegExp regex = RegExp(r'Enter your (.*?) url');
    final match = regex.firstMatch(label);
    return match?.group(1) ?? '';
  }

  @override
  Widget build(BuildContext context) {
    final viewModel = Provider.of<FormViewModel>(context);
    final influencerViewModel = Provider.of<InfluencerViewModel>(context);

    return FutureBuilder(
      future: _getCredentials(),
      builder: (context, AsyncSnapshot<Map<String, String?>> snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return Center(child: CircularProgressIndicator());
        } else if (snapshot.hasError) {
          return Center(child: Text('Error: ${snapshot.error}'));
        } else {
          Map<String, String?> credentials = snapshot.data ?? {};
          String? email = credentials['email'];
          String? password = credentials['password'];

          if (email != null && password != null && !_isLoggedIn) {
            _isLoggedIn = true;
            _email = email;
            // Fetch data based on email
            Provider.of<InfluencerViewModel>(context, listen: false)
                .fetchInfluencersData(email);
          }

          return Padding(
            padding: const EdgeInsets.all(16.0),
            child: SingleChildScrollView(
              child: Form(
                key: _formKey,
                child: Column(
                  children: [
                    if (widget.title == 'Set Price')
                      Padding(
                        padding: const EdgeInsets.symmetric(vertical: 8.0),
                        child: Column(
                          children: [
                            Text('Select Currency'),
                            Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: [
                                // Radio buttons code here
                              ],
                            ),
                          ],
                        ),
                      ),

                    for (int i = 0; i < widget.fieldLabels.length; i++)
                      Padding(
                        padding: const EdgeInsets.symmetric(vertical: 8.0),
                        child: Consumer<InfluencerViewModel>(
                          builder: (context, influencerViewModel, child) {
                            String fieldKey =
                                extractSocialSite(widget.fieldLabels[i]);
                            String? initialValue;
                            if (widget.title == 'Set Price') {
                              initialValue =
                                  influencerViewModel.socialPrices?[fieldKey]
                                      ?.toString();
                            } else if (widget.title == 'Add social site') {
                              initialValue =
                                  influencerViewModel.socialurls?[fieldKey]
                                      ?.toString();
                            }
                            return TextFormField(
                              initialValue: initialValue ?? '',
                              decoration: InputDecoration(
                                labelText: widget.fieldLabels[i],
                                border: OutlineInputBorder(
                                  borderRadius: BorderRadius.circular(10.0),
                                ),
                                filled: true,
                                fillColor: Colors.grey[200],
                              ),
                              onSaved: (value) {
                                _formData[widget.fieldLabels[i]] = value ?? '';
                              },
                            );
                          },
                        ),
                      ),

                    SizedBox(height: 20),
                    ElevatedButton(
                      onPressed: () {
                        if (_formKey.currentState?.validate() ?? false) {
                          _formKey.currentState?.save();
                          viewModel.submitForm(_formData, _selectedCurrency);
                        }
                      },
                      child: Text('Save'),
                    ),
                  ],
                ),
              ),
            ),
          );
        }
      },
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:
FutureBuilder: Wraps the widget tree that depends on _getCredentials(). It handles different states (waiting, error, data) and displays appropriate UI (loader, error message, or form content).

Initialization Logic: Moved the logic related to checking credentials and fetching data inside the FutureBuilder. This ensures that the form initializes correctly based on async operations.

TextFormField: The initialValue for each TextFormField is set based on conditions (Set Price or Add social site). This ensures that the form fields are populated correctly once data is available.

By implementing these changes, your form widget will display a loader until the initial values are processed, providing a smoother user experience. Adjust the radio buttons and any other UI elements as per your specific requirements within the form.

Image description

Top comments (0)