Home > Back-end >  good way to mange state inside dialog with a form on it, being displayed from page with its own bloc
good way to mange state inside dialog with a form on it, being displayed from page with its own bloc

Time:10-30

See for the invoice page I have BlocBuilder wrapped in a scaffold of statful page, inside that body under several widgets is a call to future void in seperate file call to create a dialog widget. And inside the dialog method is a call to create an invoice form which is in a seprate file and is stateful class displayed to be displayed on the dialog screen. In this form the user will be able to add and delete UI elements from a list view what I need to do is rebuild the widget either dialog screen/form or the list view/ to reflect the changes made by the user

import 'package:flutter/material.dart';

import 'dart:developer' as dev;
import 'package:track/src/features/invoices/application/bloc.dart';
import 'package:track/src/features/invoices/application/events.dart';
import 'package:track/src/features/invoices/application/pdf_invoice_api.dart';

class InvoiceForm extends StatefulWidget {
  final InvoiceBlocController blocController;
  const InvoiceForm(this.blocController, {Key? key}) : super(key: key);

  @override
  State<InvoiceForm> createState() => _InvoiceFormState();
}

class _InvoiceFormState extends State<InvoiceForm> {
  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          TextFormField(
            controller: TextEditingController()
              ..text = widget.blocController.invoice.client,
            validator: (value) {
              value!.isEmpty ? 'Enter a value for client' : null;
            },
            style: Theme.of(context).textTheme.labelMedium,
            decoration: InputDecoration(
                focusedBorder: const UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colors.white,
                  ),
                ),
                enabledBorder: const UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colors.white,
                  ),
                ),
                labelText: 'Client:',
                labelStyle: Theme.of(context).textTheme.labelMedium),
          ),
          TextFormField(
            controller: TextEditingController()
              ..text =
                  '${widget.blocController.invoice.projectNumber}-${widget.blocController.invoice.invoiceNumber}',
            validator: (value) {
              value!.isEmpty ? 'Enter a valid project number' : null;
            },
            style: Theme.of(context).textTheme.labelMedium,
            decoration: InputDecoration(
                focusedBorder: const UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colors.white,
                  ),
                ),
                enabledBorder: const UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colors.white,
                  ),
                ),
                labelText: 'Client:',
                labelStyle: Theme.of(context).textTheme.labelMedium),
          ),
          ListView.builder(
            shrinkWrap: true,
            itemCount: widget.blocController.invoice.items.length,
            itemBuilder: (context, index) {
              final item = widget.blocController.invoice.items[index];
              return ListTile(
                contentPadding: EdgeInsets.zero,
                trailing: IconButton(
                    onPressed: () {
                      widget.blocController.add(DeleteItemFromInvoice(index));
                    },
                    icon: Icon(Icons.delete)),
                title: Column(
                  children: [
                    Row(
                      children: [
                        itemTextFormField(
                            initialValue: item.name ?? '',
                            labelText: 'name',
                            index: index),
                        SizedBox(width: 20),
                        itemTextFormField(
                            initialValue: item.description ?? '',
                            labelText: 'description',
                            index: index),
                      ],
                    ),
                    Row(
                      children: [
                        itemTextFormField(
                            initialValue: item.quantity.toString(),
                            labelText: 'quantity',
                            index: index),
                        SizedBox(width: 20),
                        itemTextFormField(
                            initialValue: item.costBeforeVAT.toString(),
                            labelText: 'Cost Before VAT',
                            index: index),
                      ],
                    ),
                    SizedBox(height: 30),
                    Divider(
                      thickness: 2,
                      color: Colors.black,
                    )
                  ],
                ),
              );
            },
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              IconButton(
                  onPressed: () {
                    dev.log('button clicked to add new item');
                    widget.blocController.add(AddNewItemToInvoice());
                  },
                  icon: Icon(Icons.add)),
              IconButton(
                  onPressed: () async {
                    _formKey.currentState!.save();
                    Navigator.pop(context);
                    await PdfInvoiceApi.generate(widget.blocController.invoice);
                  },
                  icon: Icon(Icons.send))
            ],
          )
        ],
      ),
    );
  }

  Expanded itemTextFormField({
    required String initialValue,
    required String labelText,
    required int index,
  }) {
    return Expanded(
      child: TextFormField(
        controller: TextEditingController()..text = initialValue,
        onSaved: (newValue) {
          widget.blocController.add(UpdateInvoiceDetails(index));
        },
        style: Theme.of(context).textTheme.labelMedium,
        decoration: InputDecoration(
          focusedBorder: const UnderlineInputBorder(
            borderSide: BorderSide(
              color: Colors.white,
            ),
          ),
          enabledBorder: const UnderlineInputBorder(
            borderSide: BorderSide(
              color: Colors.white,
            ),
          ),
          labelText: labelText,
          labelStyle: Theme.of(context).textTheme.labelMedium,
        ),
      ),
    );
  }
}

InvoiceDialog Source code: InvoiceDialog

CodePudding user response:

You can pass the main bloc to the dialog widget and call the bloc function that you want and it will reflect on the main screen

How can you do this? by injecting the MainBloc value to DialogWidget with BlocProvider.value

MainWidget

class MainWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (BuildContext context) => MainBloc(),
      child: BlocConsumer<MainBloc, MainStates>(
        listener: (BuildContext context, MainStates state) {},
        builder: (BuildContext context, MainStates state) {
          final bloc = MainBloc.get(context);
          return GestureDetector(
            onTap: () {
              showDialog(
                context,
                builder: (context) => BlocProvider.value(
                  value: bloc,
                  child: WidgetTwoDialog(),
                ),
              );
            },
            child: Item(),
          );
        },
      ),
    );
  }
}

DialogWidget

class DialogWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final bloc = MainBloc.get(context);
    return GestureDetector(
      onTap: () {
        bloc.addToList();
      },
      child: Text('Remove form the main screen'),
    );
  }
} 

Also, this answer might help you to get my point well here

CodePudding user response:

you can wrap your dialog within the stateful builder and then you will get the method to set your dialog state.

  • Related