Flutter - How can I pass a Bloc as a parameter in a reusable Widget?
Asked Answered
A

1

6

I'm building a login page, and I have created what I intend to be used as a fully dynamic TextField widget.

I now need to create 2 instances of that widget (email and password inputs) and send parameters for hintText, errorText, and then my LoginBloc and validation method which are of course different for each input.

My problem is that Dart isn't letting me use the bloc my widget receives as a type and therefore the code doesn't work.

What can I do to fix this? Or am I doing this wrong altogether and is there a better way of doing this?

login page:

@override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: Padding(
          padding: const EdgeInsets.only(
            left: 16,
            right: 16,
          ),
          child: SingleChildScrollView(
            child: Column(
              children: <Widget>[
                Container(
                  margin: EdgeInsets.only(top: 50),
                  child: Text(
                    AppLocalizations.of(context).initiateSession,
                    style: Theme.of(context).textTheme.headline6,
                  ),
                ),
                BlocProvider(
                  create: (context) {
                    return LoginBloc();
                  },
                  child: BlocListener<LoginBloc, LoginState>(
                    listener: (context, state) {
                      if (state.status.isSubmissionFailure) {
                        ScaffoldMessenger.of(context)
                          ..hideCurrentSnackBar()
                          ..showSnackBar(
                            const SnackBar(
                                content: Text('Authentication Failure')),
                          );
                      }
                    },
                    child: Align(
                      alignment: const Alignment(0, -1 / 3),
                      child: Column(
                        mainAxisSize: MainAxisSize.min,
                        children: [
                          TextInput(AppLocalizations.of(context).email, LoginBloc, LoginEmailChanged, getError),
                          TextInput(AppLocalizations.of(context).password, LoginBloc, LoginEmailChanged, getError),
                        ],
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

custom textfield:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class TextInput extends StatelessWidget {
  final String hint;
  final Bloc bloc;
  final Function blocEvent;
  final String errorMessage;

  TextInput(this.hint, this.bloc, this.blocEvent, this.errorMessage);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.only(top: 10),
      child: TextField(
        decoration: InputDecoration(
          fillColor: Color(0xfff5f5f5),
          filled: true,
          border: InputBorder.none,
          hintText: hint,
          hintStyle: TextStyle(
            color: Color(0xffb4b4b4),
            fontSize: 18.0,
            fontFamily: 'Helvetica',
            fontWeight: FontWeight.w100,
          ),
          contentPadding: EdgeInsets.symmetric(
            vertical: 22,
            horizontal: 14,
          ),
          errorText: errorMessage,
        ),
        style: Theme.of(context).textTheme.bodyText1,
        cursorColor: Theme.of(context).primaryColor,
        cursorHeight: 20,
        onChanged: (textValue) =>
            context.read<bloc>().add(blocEvent(textValue)), //this is the line where the error occurs, where I use "bloc" as a type.
      ),
    );
  }
}
Adieu answered 18/1, 2022 at 17:19 Comment(0)
K
1

You are not supposed to pass bloc and blocEvent as parameters to your TextInput widget. You can perform a lookup from your child widgets using BlocProvider.of<LoginBloc>() or context.read<LoginBloc>() to get the instance of your LoginBloc.

First, it's better to have 2 separate widget class for EmailInputTextField and PasswordInputTextField. Now you can manage independent text fields.

Then you should have some event handling code inside of your bloc. For example, onEmailChanged(), onPasswordChanged().

Then you can do something like:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class EmailInputTextField extends StatelessWidget {

  const EmailInputTextField({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.only(top: 10),
      child: BlocBuilder<LoginBloc, LoginState>(
      builder: (context, state) {
        return TextField(
          decoration: InputDecoration(
            fillColor: Color(0xfff5f5f5),
            filled: true,
            border: InputBorder.none,
            hintText: 'Enter your email address',
            hintStyle: TextStyle(
              color: Color(0xffb4b4b4),
              fontSize: 18.0,
              fontFamily: 'Helvetica',
              fontWeight: FontWeight.w100,
            ),
            contentPadding: EdgeInsets.symmetric(
              vertical: 22,
              horizontal: 14,
            ),
            errorText: 'Invalid Email',
          ),
          style: Theme.of(context).textTheme.bodyText1,
          cursorColor: Theme.of(context).primaryColor,
          cursorHeight: 20,
          onChanged: (textValue) =>
              context.read<LoginBloc>().add(EmailChanged(textValue));  // Assuming you have an event class EmailChanged()
      );
     },
    ),
  );
 }
}

Also,

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class PasswordInputTextField extends StatelessWidget {

  const PasswordInputTextField({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.only(top: 10),
      child: BlocBuilder<LoginBloc, LoginState>(
      builder: (context, state) {
        return TextField(
          decoration: InputDecoration(
            fillColor: Color(0xfff5f5f5),
            filled: true,
            border: InputBorder.none,
            hintText: 'Enter your password',
            hintStyle: TextStyle(
              color: Color(0xffb4b4b4),
              fontSize: 18.0,
              fontFamily: 'Helvetica',
              fontWeight: FontWeight.w100,
            ),
            contentPadding: EdgeInsets.symmetric(
              vertical: 22,
              horizontal: 14,
            ),
            errorText: 'Short Password',
          ),
          style: Theme.of(context).textTheme.bodyText1,
          cursorColor: Theme.of(context).primaryColor,
          cursorHeight: 20,
          onChanged: (textValue) =>
              context.read<LoginBloc>().add(PasswordChanged(textValue));  // Assuming you have an event class PasswordChanged()
      );
     },
    ),
  );
 }
}
Kiblah answered 1/4, 2022 at 6:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.